mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-18 18:18:23 -05:00
MM-67498 Bulk migrate batches M13-20 from Enzyme to RTL (#35207)
* test: bulk migrate batches m13-20 from enzyme to rtl * remove commented test block --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
61b7fc1594
commit
d09ab7173e
67 changed files with 15541 additions and 11497 deletions
|
|
@ -0,0 +1,70 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/widgets/settings/AutocompleteSelector check snapshot with value prop and changing focus 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
class="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
value="value from prop"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/widgets/settings/AutocompleteSelector check snapshot with value prop and changing focus 2`] = `
|
||||
<div>
|
||||
<div
|
||||
class="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
class="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
value="value from input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/widgets/settings/AutocompleteSelector render component with required props 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
class="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
value="some value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/actions_menu/ActionsMenu on mobile view should match snapshot 1`] = `
|
||||
<ContextConsumer>
|
||||
<Component />
|
||||
</ContextConsumer>
|
||||
`;
|
||||
exports[`components/actions_menu/ActionsMenu on mobile view should match snapshot 1`] = `<div />`;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import ActionsMenu from 'components/actions_menu/actions_menu';
|
||||
import type {Props} from 'components/actions_menu/actions_menu';
|
||||
|
||||
import {renderWithContext} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
jest.mock('utils/utils', () => {
|
||||
|
|
@ -46,10 +46,10 @@ describe('components/actions_menu/ActionsMenu on mobile view', () => {
|
|||
pluginMenuItemComponents: [],
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ActionsMenu {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,36 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {act, render, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import AutocompleteSelector from './autocomplete_selector';
|
||||
|
||||
let mockOnItemSelected: ((selected: any) => void) | undefined;
|
||||
|
||||
jest.mock('components/suggestion/suggestion_box', () => {
|
||||
const ReactMod = require('react');
|
||||
const MockSuggestionBox = ReactMod.forwardRef(function MockSuggestionBox(props: any, ref: any) {
|
||||
mockOnItemSelected = props.onItemSelected;
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
className={props.className || 'form-control'}
|
||||
value={props.value || ''}
|
||||
onChange={(e: any) => props.onChange?.({target: e.target})}
|
||||
onFocus={props.onFocus}
|
||||
onBlur={props.onBlur}
|
||||
placeholder={props.placeholder}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return {__esModule: true, default: MockSuggestionBox};
|
||||
});
|
||||
|
||||
describe('components/widgets/settings/AutocompleteSelector', () => {
|
||||
test('render component with required props', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = render(
|
||||
<AutocompleteSelector
|
||||
id='string.id'
|
||||
label='some label'
|
||||
|
|
@ -16,43 +38,11 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
|
|||
providers={[]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<div
|
||||
className="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
className="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<Connect(SuggestionBox)
|
||||
className="form-control"
|
||||
completeOnTab={true}
|
||||
containerClass="select-suggestion-container"
|
||||
listComponent={[Function]}
|
||||
listPosition="top"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onItemSelected={[Function]}
|
||||
openOnFocus={true}
|
||||
openWhenEmpty={true}
|
||||
providers={Array []}
|
||||
renderNoResults={true}
|
||||
replaceAllInputOnSelect={true}
|
||||
value="some value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('check snapshot with value prop and changing focus', () => {
|
||||
const wrapper = shallow<AutocompleteSelector>(
|
||||
test('check snapshot with value prop and changing focus', async () => {
|
||||
const {container} = render(
|
||||
<AutocompleteSelector
|
||||
providers={[]}
|
||||
label='some label'
|
||||
|
|
@ -60,83 +50,28 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
wrapper.instance().onBlur();
|
||||
const input = screen.getByRole('textbox');
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<div
|
||||
className="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
className="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<Connect(SuggestionBox)
|
||||
className="form-control"
|
||||
completeOnTab={true}
|
||||
containerClass="select-suggestion-container"
|
||||
listComponent={[Function]}
|
||||
listPosition="top"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onItemSelected={[Function]}
|
||||
openOnFocus={true}
|
||||
openWhenEmpty={true}
|
||||
providers={Array []}
|
||||
renderNoResults={true}
|
||||
replaceAllInputOnSelect={true}
|
||||
value="value from prop"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
// Initially not focused, shows prop value
|
||||
expect(input).toHaveValue('value from prop');
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
wrapper.instance().onChange(({target: {value: 'value from input'} as HTMLInputElement}));
|
||||
wrapper.instance().onFocus();
|
||||
// Focus the input and type a new value
|
||||
await userEvent.click(input);
|
||||
await userEvent.clear(input);
|
||||
await userEvent.type(input, 'value from input');
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<div
|
||||
className="form-group"
|
||||
data-testid="autoCompleteSelector"
|
||||
>
|
||||
<label
|
||||
className="control-label "
|
||||
>
|
||||
some label
|
||||
</label>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<Connect(SuggestionBox)
|
||||
className="form-control"
|
||||
completeOnTab={true}
|
||||
containerClass="select-suggestion-container"
|
||||
listComponent={[Function]}
|
||||
listPosition="top"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onItemSelected={[Function]}
|
||||
openOnFocus={true}
|
||||
openWhenEmpty={true}
|
||||
providers={Array []}
|
||||
renderNoResults={true}
|
||||
replaceAllInputOnSelect={true}
|
||||
value="value from input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
expect(input).toHaveValue('value from input');
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
// Blur the input - value should revert to prop value
|
||||
await userEvent.tab();
|
||||
expect(input).toHaveValue('value from prop');
|
||||
});
|
||||
|
||||
test('onSelected', () => {
|
||||
const onSelected = jest.fn();
|
||||
const wrapper = shallow<AutocompleteSelector>(
|
||||
render(
|
||||
<AutocompleteSelector
|
||||
label='some label'
|
||||
value='some value'
|
||||
|
|
@ -146,7 +81,9 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
|
|||
);
|
||||
|
||||
const selected = {text: 'sometext', value: 'somevalue', id: '', username: '', display_name: ''};
|
||||
wrapper.instance().handleSelected(selected);
|
||||
act(() => {
|
||||
mockOnItemSelected!(selected);
|
||||
});
|
||||
|
||||
expect(onSelected).toHaveBeenCalledTimes(1);
|
||||
expect(onSelected).toHaveBeenCalledWith(selected);
|
||||
|
|
|
|||
|
|
@ -1,43 +1,35 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`EmojiPage should render without crashing 1`] = `
|
||||
<div
|
||||
className="backstage-content emoji-list"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="backstage-header"
|
||||
class="backstage-content emoji-list"
|
||||
>
|
||||
<h1>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Custom Emoji"
|
||||
id="emoji_list.header"
|
||||
/>
|
||||
</h1>
|
||||
<Memo(AnyTeamPermissionGate)
|
||||
permissions={
|
||||
Array [
|
||||
"create_emojis",
|
||||
]
|
||||
}
|
||||
<div
|
||||
class="backstage-header"
|
||||
>
|
||||
<Link
|
||||
className="add-link"
|
||||
to="/team/emoji/add"
|
||||
<h1>
|
||||
Custom Emoji
|
||||
</h1>
|
||||
<div
|
||||
data-testid="permission-gate"
|
||||
>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
type="button"
|
||||
<a
|
||||
class="add-link"
|
||||
href="/team/emoji/add"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Add Custom Emoji"
|
||||
id="emoji_list.add"
|
||||
/>
|
||||
</button>
|
||||
</Link>
|
||||
</Memo(AnyTeamPermissionGate)>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="button"
|
||||
>
|
||||
Add Custom Emoji
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-testid="emoji-list"
|
||||
/>
|
||||
</div>
|
||||
<Connect(EmojiList)
|
||||
scrollToTop={[MockFunction]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ShallowWrapper} from 'enzyme';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import AnyTeamPermissionGate from 'components/permissions_gates/any_team_permission_gate';
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
|
||||
import EmojiList from './emoji_list';
|
||||
import EmojiPage from './emoji_page';
|
||||
|
||||
jest.mock('utils/utils', () => ({
|
||||
localizeMessage: jest.fn().mockReturnValue('Custom Emoji'),
|
||||
}));
|
||||
|
||||
jest.mock('./emoji_list', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid='emoji-list'/>,
|
||||
}));
|
||||
|
||||
jest.mock('components/permissions_gates/any_team_permission_gate', () => ({
|
||||
__esModule: true,
|
||||
default: ({children}: {children: React.ReactNode}) => <div data-testid='permission-gate'>{children}</div>,
|
||||
}));
|
||||
|
||||
describe('EmojiPage', () => {
|
||||
const mockLoadRolesIfNeeded = jest.fn();
|
||||
const mockScrollToTop = jest.fn();
|
||||
|
|
@ -30,27 +36,31 @@ describe('EmojiPage', () => {
|
|||
};
|
||||
|
||||
it('should render without crashing', () => {
|
||||
const wrapper: ShallowWrapper = shallow(<EmojiPage {...defaultProps}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<EmojiPage {...defaultProps}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the emoji list and the add button with permission', () => {
|
||||
const wrapper: ShallowWrapper = shallow(<EmojiPage {...defaultProps}/>);
|
||||
expect(wrapper.find(EmojiList).exists()).toBe(true);
|
||||
expect(wrapper.find(AnyTeamPermissionGate).exists()).toBe(true);
|
||||
expect(wrapper.find(Link).prop('to')).toBe('/team/emoji/add');
|
||||
renderWithContext(<EmojiPage {...defaultProps}/>);
|
||||
expect(screen.getByTestId('emoji-list')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('permission-gate')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link')).toHaveAttribute('href', '/team/emoji/add');
|
||||
});
|
||||
|
||||
it('should not render the add button if permission is not granted', () => {
|
||||
const wrapper: ShallowWrapper = shallow(
|
||||
<EmojiPage {...defaultProps}/>,
|
||||
).setProps({teamName: '', actions: {loadRolesIfNeeded: mockLoadRolesIfNeeded}});
|
||||
expect(wrapper.find(AnyTeamPermissionGate).exists()).toBe(true);
|
||||
expect(wrapper.find(Link).exists()).toBe(true); // Update this to match your permission setup
|
||||
renderWithContext(
|
||||
<EmojiPage
|
||||
{...defaultProps}
|
||||
teamName=''
|
||||
actions={{loadRolesIfNeeded: mockLoadRolesIfNeeded}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId('permission-gate')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render EmojiList component', () => {
|
||||
const wrapper = shallow(<EmojiPage {...defaultProps}/>);
|
||||
expect(wrapper.find(EmojiList).exists()).toBe(true);
|
||||
renderWithContext(<EmojiPage {...defaultProps}/>);
|
||||
expect(screen.getByTestId('emoji-list')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,69 +1,59 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/emoji_picker/components/EmojiPickerHeader handleEmojiPickerClose, should have called props.handleEmojiPickerClose 1`] = `
|
||||
<div
|
||||
className="emoji-picker__header"
|
||||
>
|
||||
<button
|
||||
className="close emoji-picker__header-close-button"
|
||||
onClick={[MockFunction]}
|
||||
type="button"
|
||||
<div>
|
||||
<div
|
||||
class="emoji-picker__header"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
<button
|
||||
class="close emoji-picker__header-close-button"
|
||||
type="button"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<h4
|
||||
class="modal-title emoji-picker__header-title"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="emoji_picker.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
<h4
|
||||
className="modal-title emoji-picker__header-title"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Emoji Picker"
|
||||
id="emoji_picker.header"
|
||||
/>
|
||||
</h4>
|
||||
Emoji Picker
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/emoji_picker/components/EmojiPickerHeader should match snapshot, 1`] = `
|
||||
<div
|
||||
className="emoji-picker__header"
|
||||
>
|
||||
<button
|
||||
className="close emoji-picker__header-close-button"
|
||||
onClick={[MockFunction]}
|
||||
type="button"
|
||||
<div>
|
||||
<div
|
||||
class="emoji-picker__header"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
<button
|
||||
class="close emoji-picker__header-close-button"
|
||||
type="button"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<h4
|
||||
class="modal-title emoji-picker__header-title"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="emoji_picker.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
<h4
|
||||
className="modal-title emoji-picker__header-title"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Emoji Picker"
|
||||
id="emoji_picker.header"
|
||||
/>
|
||||
</h4>
|
||||
Emoji Picker
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,36 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import EmojiPickerHeader from 'components/emoji_picker/components/emoji_picker_header';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
describe('components/emoji_picker/components/EmojiPickerHeader', () => {
|
||||
test('should match snapshot, ', () => {
|
||||
const props = {
|
||||
handleEmojiPickerClose: jest.fn(),
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<EmojiPickerHeader {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
test('handleEmojiPickerClose, should have called props.handleEmojiPickerClose', () => {
|
||||
test('handleEmojiPickerClose, should have called props.handleEmojiPickerClose', async () => {
|
||||
const props = {
|
||||
handleEmojiPickerClose: jest.fn(),
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<EmojiPickerHeader {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
wrapper.find('button').first().simulate('click');
|
||||
await userEvent.click(screen.getAllByRole('button')[0]);
|
||||
expect(props.handleEmojiPickerClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,97 +1,126 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/ProductBranding should fallback to ProductChannelsIcon when string icon name is not found in glyphMap 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductChannelsIcon
|
||||
size={24}
|
||||
/>
|
||||
<h1
|
||||
className="sr-only"
|
||||
<div>
|
||||
<span
|
||||
class="ProductBrandingContainer-fBgRMk bpZokb"
|
||||
tabindex="-1"
|
||||
>
|
||||
InvalidProduct
|
||||
</h1>
|
||||
<ProductBrandingHeading>
|
||||
InvalidProduct
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
<svg
|
||||
data-testid="ProductChannelsIcon"
|
||||
size="24"
|
||||
/>
|
||||
<h1
|
||||
class="sr-only"
|
||||
>
|
||||
InvalidProduct
|
||||
</h1>
|
||||
<span
|
||||
class="ProductBrandingHeading-cfoqHj gJNxBU"
|
||||
>
|
||||
InvalidProduct
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should render a React element icon when switcherIcon is a React node 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={-1}
|
||||
>
|
||||
<svg
|
||||
data-testid="custom-icon"
|
||||
<div>
|
||||
<span
|
||||
class="ProductBrandingContainer-fBgRMk bpZokb"
|
||||
tabindex="-1"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
/>
|
||||
</svg>
|
||||
<h1
|
||||
className="sr-only"
|
||||
>
|
||||
CustomProduct
|
||||
</h1>
|
||||
<ProductBrandingHeading>
|
||||
CustomProduct
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
<svg
|
||||
data-testid="custom-icon"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
/>
|
||||
</svg>
|
||||
<h1
|
||||
class="sr-only"
|
||||
>
|
||||
CustomProduct
|
||||
</h1>
|
||||
<span
|
||||
class="ProductBrandingHeading-cfoqHj gJNxBU"
|
||||
>
|
||||
CustomProduct
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Boards 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductBoardsIcon
|
||||
size={24}
|
||||
/>
|
||||
<h1
|
||||
className="sr-only"
|
||||
<div>
|
||||
<span
|
||||
class="ProductBrandingContainer-fBgRMk bpZokb"
|
||||
tabindex="-1"
|
||||
>
|
||||
Boards
|
||||
</h1>
|
||||
<ProductBrandingHeading>
|
||||
Boards
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
<svg
|
||||
data-testid="ProductChannelsIcon"
|
||||
size="24"
|
||||
/>
|
||||
<h1
|
||||
class="sr-only"
|
||||
>
|
||||
Boards
|
||||
</h1>
|
||||
<span
|
||||
class="ProductBrandingHeading-cfoqHj gJNxBU"
|
||||
>
|
||||
Boards
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Channels 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductChannelsIcon
|
||||
size={24}
|
||||
/>
|
||||
<h1
|
||||
className="sr-only"
|
||||
<div>
|
||||
<span
|
||||
class="ProductBrandingContainer-fBgRMk bpZokb"
|
||||
tabindex="-1"
|
||||
>
|
||||
Channels
|
||||
</h1>
|
||||
<ProductBrandingHeading>
|
||||
Channels
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
<svg
|
||||
data-testid="ProductChannelsIcon"
|
||||
size="24"
|
||||
/>
|
||||
<h1
|
||||
class="sr-only"
|
||||
>
|
||||
Channels
|
||||
</h1>
|
||||
<span
|
||||
class="ProductBrandingHeading-cfoqHj gJNxBU"
|
||||
>
|
||||
Channels
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Playbooks 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductPlaybooksIcon
|
||||
size={24}
|
||||
/>
|
||||
<h1
|
||||
className="sr-only"
|
||||
<div>
|
||||
<span
|
||||
class="ProductBrandingContainer-fBgRMk bpZokb"
|
||||
tabindex="-1"
|
||||
>
|
||||
Playbooks
|
||||
</h1>
|
||||
<ProductBrandingHeading>
|
||||
Playbooks
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
<svg
|
||||
data-testid="ProductChannelsIcon"
|
||||
size="24"
|
||||
/>
|
||||
<h1
|
||||
class="sr-only"
|
||||
>
|
||||
Playbooks
|
||||
</h1>
|
||||
<span
|
||||
class="ProductBrandingHeading-cfoqHj gJNxBU"
|
||||
>
|
||||
Playbooks
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import {TopLevelProducts} from 'utils/constants';
|
||||
import * as productUtils from 'utils/products';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
|
@ -12,37 +12,50 @@ import type {ProductComponent} from 'types/store/plugins';
|
|||
|
||||
import ProductBranding from './product_branding';
|
||||
|
||||
jest.mock('@mattermost/compass-icons/components', () => {
|
||||
const actual = jest.requireActual('@mattermost/compass-icons/components');
|
||||
return {
|
||||
...actual,
|
||||
ProductChannelsIcon: (props: any) => (
|
||||
<svg
|
||||
data-testid='ProductChannelsIcon'
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('components/ProductBranding', () => {
|
||||
test('should show correct icon glyph when we are on Channels', () => {
|
||||
const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct');
|
||||
currentProductSpy.mockReturnValue(null);
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ProductBranding/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show correct icon glyph when we are on Playbooks', () => {
|
||||
const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct');
|
||||
currentProductSpy.mockReturnValue(TestHelper.makeProduct(TopLevelProducts.PLAYBOOKS));
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ProductBranding/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show correct icon glyph when we are on Boards', () => {
|
||||
const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct');
|
||||
currentProductSpy.mockReturnValue(TestHelper.makeProduct(TopLevelProducts.BOARDS));
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ProductBranding/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render a React element icon when switcherIcon is a React node', () => {
|
||||
|
|
@ -62,12 +75,12 @@ describe('components/ProductBranding', () => {
|
|||
};
|
||||
currentProductSpy.mockReturnValue(productWithReactIcon);
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ProductBranding/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('[data-testid="custom-icon"]').exists()).toBe(true);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByTestId('custom-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should fallback to ProductChannelsIcon when string icon name is not found in glyphMap', () => {
|
||||
|
|
@ -78,11 +91,11 @@ describe('components/ProductBranding', () => {
|
|||
};
|
||||
currentProductSpy.mockReturnValue(productWithInvalidIcon);
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<ProductBranding/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('ProductChannelsIcon').exists()).toBe(true);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByTestId('ProductChannelsIcon')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,44 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import * as reactRedux from 'react-redux';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import {TopLevelProducts} from 'utils/constants';
|
||||
import * as productUtils from 'utils/products';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import ProductBranding from './product_branding';
|
||||
import ProductBrandingFreeEdition from './product_branding_team_edition';
|
||||
import ProductMenu, {ProductMenuButton, ProductMenuContainer} from './product_menu';
|
||||
import ProductMenuItem from './product_menu_item';
|
||||
import ProductMenuList from './product_menu_list';
|
||||
import ProductMenu from './product_menu';
|
||||
|
||||
jest.mock('./product_branding', () => {
|
||||
return function MockProductBranding() {
|
||||
return <div data-testid='product-branding'/>;
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./product_branding_team_edition', () => {
|
||||
return function MockProductBrandingFreeEdition() {
|
||||
return <div data-testid='product-branding-free-edition'/>;
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./product_menu_list', () => {
|
||||
return function MockProductMenuList() {
|
||||
return <div data-testid='product-menu-list'/>;
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('components/onboarding_tasks', () => ({
|
||||
OnboardingTaskCategory: 'onboardingTask',
|
||||
OnboardingTasksName: {VISIT_SYSTEM_CONSOLE: 'visit_system_console'},
|
||||
TaskNameMapToSteps: {visit_system_console: {FINISHED: 999}},
|
||||
useHandleOnBoardingTaskData: () => jest.fn(),
|
||||
}));
|
||||
|
||||
const spyProduct = jest.spyOn(productUtils, 'useCurrentProductId');
|
||||
spyProduct.mockReturnValue(null);
|
||||
|
||||
describe('components/global/product_switcher', () => {
|
||||
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');
|
||||
const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
|
||||
|
||||
beforeEach(() => {
|
||||
const products = [
|
||||
TestHelper.makeProduct(TopLevelProducts.BOARDS),
|
||||
|
|
@ -30,101 +46,90 @@ describe('components/global/product_switcher', () => {
|
|||
];
|
||||
const spyProducts = jest.spyOn(productUtils, 'useProducts');
|
||||
spyProducts.mockReturnValue(products);
|
||||
useDispatchMock.mockClear();
|
||||
useSelectorMock.mockClear();
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
const baseState = {
|
||||
entities: {
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
},
|
||||
},
|
||||
};
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'});
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
},
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
it('should match snapshot', () => {
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
baseState,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match snapshot without license', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'false'});
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render once when there are no top level products available', () => {
|
||||
const state = {
|
||||
users: {
|
||||
currentUserId: 'test_id',
|
||||
},
|
||||
...baseState,
|
||||
views: {
|
||||
...baseState.views,
|
||||
productMenu: {
|
||||
switcherOpen: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
const spyProducts = jest.spyOn(productUtils, 'useProducts');
|
||||
|
||||
spyProducts.mockReturnValue([]);
|
||||
expect(wrapper.find(ProductMenuItem).at(0)).toHaveLength(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
const menuItems = screen.getAllByRole('menuitem');
|
||||
expect(menuItems.length).toBeGreaterThanOrEqual(1);
|
||||
expect(menuItems.at(0)).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the correct amount of times when there are products available', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
views: {
|
||||
...baseState.views,
|
||||
productMenu: {
|
||||
switcherOpen: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
const products = [
|
||||
TestHelper.makeProduct(TopLevelProducts.BOARDS),
|
||||
TestHelper.makeProduct(TopLevelProducts.PLAYBOOKS),
|
||||
|
|
@ -133,172 +138,172 @@ describe('components/global/product_switcher', () => {
|
|||
const spyProducts = jest.spyOn(productUtils, 'useProducts');
|
||||
spyProducts.mockReturnValue(products);
|
||||
|
||||
expect(wrapper.find(ProductMenuItem)).toHaveLength(3);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
// Channels + 2 products
|
||||
expect(screen.getAllByRole('menuitem')).toHaveLength(3);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have an active button state when the switcher menu is open', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
views: {
|
||||
...baseState.views,
|
||||
productMenu: {
|
||||
switcherOpen: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
const setState = jest.fn();
|
||||
|
||||
const useStateSpy = jest.spyOn(React, 'useState');
|
||||
useStateSpy.mockImplementation(() => [false, setState]);
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
wrapper.find(ProductMenuContainer).simulate('click');
|
||||
expect(wrapper.find(ProductMenuButton).props()['aria-expanded']).toEqual(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const button = screen.getByRole('button', {name: 'Product switch menu'});
|
||||
expect(button).toHaveAttribute('aria-expanded', 'true');
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match snapshot with product switcher menu', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
views: {
|
||||
...baseState.views,
|
||||
productMenu: {
|
||||
switcherOpen: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper.find(ProductMenuList)).toHaveLength(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('product-menu-list')).toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render ProductBrandingFreeEdition for Entry license', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'entry',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'entry'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(1);
|
||||
expect(wrapper.find(ProductBranding)).toHaveLength(0);
|
||||
renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('product-branding-free-edition')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('product-branding')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render ProductBrandingFreeEdition for unlicensed', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'false'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(1);
|
||||
expect(wrapper.find(ProductBranding)).toHaveLength(0);
|
||||
renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('product-branding-free-edition')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('product-branding')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render ProductBranding for Professional license', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'professional',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'professional'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper.find(ProductBranding)).toHaveLength(1);
|
||||
expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(0);
|
||||
renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('product-branding')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('product-branding-free-edition')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render ProductBranding for Enterprise license', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'enterprise',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'enterprise'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper.find(ProductBranding)).toHaveLength(1);
|
||||
expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(0);
|
||||
renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('product-branding')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('product-branding-free-edition')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should match snapshot for Entry license', () => {
|
||||
const state = {
|
||||
views: {
|
||||
productMenu: {
|
||||
switcherOpen: false,
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'entry',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
useSelectorMock.mockReturnValueOnce(true);
|
||||
useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'entry'});
|
||||
const wrapper = shallow(<ProductMenu/>, {
|
||||
wrappingComponent: reactRedux.Provider,
|
||||
wrappingComponentProps: {store},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(
|
||||
<ProductMenu/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,32 +1,29 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import * as reactRedux from 'react-redux';
|
||||
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
import type {DeepPartial} from '@mattermost/types/utilities';
|
||||
|
||||
import MenuGroup from 'components/widgets/menu/menu_group';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import ProductMenuList from './product_menu_list';
|
||||
import type {Props as ProductMenuListProps} from './product_menu_list';
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux'),
|
||||
useSelector: jest.fn(),
|
||||
jest.mock('components/widgets/menu/menu_items/menu_cloud_trial', () => () => null);
|
||||
jest.mock('components/widgets/menu/menu_items/menu_item_cloud_limit', () => () => null);
|
||||
jest.mock('components/permissions_gates/system_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}</>);
|
||||
jest.mock('components/permissions_gates/team_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}</>);
|
||||
jest.mock('components/onboarding_tasks', () => ({
|
||||
VisitSystemConsoleTour: () => null,
|
||||
}));
|
||||
jest.mock('components/widgets/menu/menu_items/restricted_indicator', () => () => <div data-testid='RestrictedIndicator'/>);
|
||||
|
||||
describe('components/global/product_switcher_menu', () => {
|
||||
let useSelectorMock: jest.SpyInstance;
|
||||
|
||||
const getMenuWrapper = (props: ProductMenuListProps) => {
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
return wrapper.find(MenuGroup).shallow();
|
||||
};
|
||||
|
||||
const user = TestHelper.getUserMock({
|
||||
id: 'test-user-id',
|
||||
username: 'username',
|
||||
|
|
@ -59,15 +56,40 @@ describe('components/global/product_switcher_menu', () => {
|
|||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
|
||||
useSelectorMock.mockReturnValue(true);
|
||||
});
|
||||
const adminState: DeepPartial<GlobalState> = {
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: 'test-user-id',
|
||||
profiles: {
|
||||
'test-user-id': {
|
||||
id: 'test-user-id',
|
||||
username: 'username',
|
||||
roles: 'system_admin system_user',
|
||||
} as UserProfile,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const nonAdminState: DeepPartial<GlobalState> = {
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId: 'test-user-id',
|
||||
profiles: {
|
||||
'test-user-id': {
|
||||
id: 'test-user-id',
|
||||
username: 'username',
|
||||
roles: 'system_user',
|
||||
} as UserProfile,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test('should match snapshot with id', () => {
|
||||
const props = {...defaultProps, id: 'product-switcher-menu-test'};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not render if the user is not logged in', () => {
|
||||
|
|
@ -75,8 +97,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
currentUser: undefined as unknown as UserProfile,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.type()).toEqual(null);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
test('should match snapshot with most of the thing enabled', () => {
|
||||
|
|
@ -90,8 +112,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
canManageIntegrations: true,
|
||||
enablePluginMarketplace: true,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match userGroups snapshot with cloud free', () => {
|
||||
|
|
@ -101,8 +123,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
isStarterFree: true,
|
||||
isFreeTrial: false,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.find('#userGroups')).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#userGroups')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match userGroups snapshot with cloud free trial', () => {
|
||||
|
|
@ -112,8 +134,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
isStarterFree: false,
|
||||
isFreeTrial: true,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.find('#userGroups')).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#userGroups')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match userGroups snapshot with EnableCustomGroups config', () => {
|
||||
|
|
@ -123,8 +145,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
isStarterFree: false,
|
||||
isFreeTrial: false,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.find('#userGroups')).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#userGroups')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('user groups button is disabled for free', () => {
|
||||
|
|
@ -134,21 +156,19 @@ describe('components/global/product_switcher_menu', () => {
|
|||
isStarterFree: true,
|
||||
isFreeTrial: false,
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
expect(wrapper.find('#userGroups').prop('disabled')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#userGroups button')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should hide RestrictedIndicator if user is not admin', () => {
|
||||
useSelectorMock.mockReturnValueOnce(false);
|
||||
|
||||
const props = {
|
||||
...defaultProps,
|
||||
isStarterFree: true,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, nonAdminState);
|
||||
|
||||
expect(wrapper.find('RestrictedIndicator').exists()).toBe(false);
|
||||
expect(container.querySelector('[data-testid="RestrictedIndicator"]')).toBeNull();
|
||||
});
|
||||
|
||||
describe('should show integrations', () => {
|
||||
|
|
@ -157,8 +177,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
enableIncomingWebhooks: true,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('when outgoing webhooks enabled', () => {
|
||||
|
|
@ -166,8 +186,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
enableOutgoingWebhooks: true,
|
||||
};
|
||||
const wrapper = shallow(<ProductMenuList {...props}/>);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('when slash commands enabled', () => {
|
||||
|
|
@ -175,8 +195,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
enableCommands: true,
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('when oauth providers enabled', () => {
|
||||
|
|
@ -184,8 +204,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
enableOAuthServiceProvider: true,
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('when can manage system bots', () => {
|
||||
|
|
@ -193,8 +213,8 @@ describe('components/global/product_switcher_menu', () => {
|
|||
...defaultProps,
|
||||
canManageSystemBots: true,
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(true);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('unless cannot manage integrations', () => {
|
||||
|
|
@ -203,18 +223,19 @@ describe('components/global/product_switcher_menu', () => {
|
|||
canManageIntegrations: false,
|
||||
enableCommands: true,
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
expect(wrapper.find('#integrations').prop('show')).toBe(false);
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
expect(container.querySelector('#integrations')).toBeNull();
|
||||
});
|
||||
|
||||
it('should show integrations modal', () => {
|
||||
it('should show integrations modal', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
enableIncomingWebhooks: true,
|
||||
teamName: 'test-team',
|
||||
};
|
||||
const wrapper = getMenuWrapper(props);
|
||||
wrapper.find('#integrations').simulate('click');
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ProductMenuList {...props}/>, adminState);
|
||||
await userEvent.click(screen.getByText('Integrations'));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,39 +1,16 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/global/AtMentionsButton should match snapshot 1`] = `
|
||||
<WithTooltip
|
||||
title={
|
||||
<React.Fragment>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Recent mentions"
|
||||
id="channel_header.recentMentions"
|
||||
/>
|
||||
<Memo(KeyboardShortcutSequence)
|
||||
hideDescription={true}
|
||||
isInsideTooltip={true}
|
||||
shortcut={
|
||||
Object {
|
||||
"default": Object {
|
||||
"defaultMessage": "Recent mentions: Ctrl|Shift|M",
|
||||
"id": "shortcuts.nav.recent_mentions",
|
||||
},
|
||||
"mac": Object {
|
||||
"defaultMessage": "Recent mentions: ⌘|Shift|M",
|
||||
"id": "shortcuts.nav.recent_mentions.mac",
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<HeaderIconButton
|
||||
<div>
|
||||
<button
|
||||
aria-controls="searchContainer"
|
||||
aria-expanded={false}
|
||||
aria-expanded="false"
|
||||
aria-label="Recent mentions"
|
||||
icon="at"
|
||||
onClick={[Function]}
|
||||
toggled={false}
|
||||
/>
|
||||
</WithTooltip>
|
||||
class="HeaderIconButton"
|
||||
>
|
||||
<i
|
||||
class="icon-at"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,44 +1,45 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import IconButton from 'components/global_header/header_icon_button';
|
||||
import {showMentions} from 'actions/views/rhs';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import AtMentionsButton from './at_mentions_button';
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
let mockState: GlobalState;
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux') as typeof import('react-redux'),
|
||||
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
|
||||
useDispatch: () => mockDispatch,
|
||||
jest.mock('actions/views/rhs', () => ({
|
||||
closeRightHandSide: jest.fn(() => ({type: 'MOCK_CLOSE_RHS'})),
|
||||
showMentions: jest.fn(() => ({type: 'MOCK_SHOW_MENTIONS'})),
|
||||
}));
|
||||
|
||||
describe('components/global/AtMentionsButton', () => {
|
||||
beforeEach(() => {
|
||||
mockState = {views: {rhs: {isSidebarOpen: true}}} as GlobalState;
|
||||
});
|
||||
const initialState = {
|
||||
views: {
|
||||
rhs: {
|
||||
isSidebarOpen: true,
|
||||
},
|
||||
},
|
||||
} as unknown as GlobalState;
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<AtMentionsButton/>,
|
||||
initialState,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show active mentions', () => {
|
||||
const wrapper = shallow(
|
||||
test('should show active mentions', async () => {
|
||||
renderWithContext(
|
||||
<AtMentionsButton/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
wrapper.find(IconButton).simulate('click', {
|
||||
preventDefault: jest.fn(),
|
||||
});
|
||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Recent mentions'}));
|
||||
expect(showMentions).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
import * as reactRedux from 'react-redux';
|
||||
|
||||
import * as cloudActions from 'mattermost-redux/actions/cloud';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import {CloudProducts} from 'utils/constants';
|
||||
|
||||
import PlanUpgradeButton from './index';
|
||||
|
||||
describe('components/global/PlanUpgradeButton', () => {
|
||||
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');
|
||||
jest.mock('components/common/hooks/useOpenPricingModal', () => ({
|
||||
__esModule: true,
|
||||
default: () => ({openPricingModal: jest.fn(), isAirGapped: false}),
|
||||
}));
|
||||
|
||||
describe('components/global/PlanUpgradeButton', () => {
|
||||
beforeEach(() => {
|
||||
useDispatchMock.mockClear();
|
||||
jest.spyOn(cloudActions, 'getCloudSubscription').mockReturnValue({type: 'MOCK_GET_CLOUD_SUBSCRIPTION'} as any);
|
||||
jest.spyOn(cloudActions, 'getCloudProducts').mockReturnValue({type: 'MOCK_GET_CLOUD_PRODUCTS'} as any);
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
entities: {
|
||||
general: {
|
||||
|
|
@ -49,28 +52,19 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
it('should show Upgrade button in global header for admin users, cloud free subscription', () => {
|
||||
const state = {
|
||||
...initialState,
|
||||
};
|
||||
|
||||
it('should show Upgrade button in global header for admin users, cloud free subscription', () => {
|
||||
const cloudSubscriptionSpy = jest.spyOn(cloudActions, 'getCloudSubscription');
|
||||
const cloudProductsSpy = jest.spyOn(cloudActions, 'getCloudProducts');
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(1);
|
||||
expect(cloudProductsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(true);
|
||||
expect(screen.getByRole('button', {name: 'View plans'})).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show Upgrade button in global header for admin users, cloud and enterprise trial subscription', () => {
|
||||
|
|
@ -89,18 +83,12 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(true);
|
||||
expect(screen.getByRole('button', {name: 'View plans'})).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show for cloud enterprise non-trial', () => {
|
||||
|
|
@ -119,18 +107,12 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show for cloud professional product', () => {
|
||||
|
|
@ -149,18 +131,12 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show Upgrade button in global header for non admin cloud users', () => {
|
||||
|
|
@ -172,18 +148,12 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show Upgrade button in global header for non admin self hosted users', () => {
|
||||
|
|
@ -195,22 +165,16 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
},
|
||||
};
|
||||
state.entities.general.license = {
|
||||
IsLicensed: 'false', // starter
|
||||
IsLicensed: 'false',
|
||||
Cloud: 'false',
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show Upgrade button in global header for non enterprise edition self hosted users', () => {
|
||||
|
|
@ -231,18 +195,12 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
BuildEnterpriseReady: 'false',
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should NOT show Upgrade button in global header for self hosted non trial and licensed', () => {
|
||||
|
|
@ -256,19 +214,13 @@ describe('components/global/PlanUpgradeButton', () => {
|
|||
const cloudSubscriptionSpy = jest.spyOn(cloudActions, 'getCloudSubscription');
|
||||
const cloudProductsSpy = jest.spyOn(cloudActions, 'getCloudProducts');
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const dummyDispatch = jest.fn();
|
||||
useDispatchMock.mockReturnValue(dummyDispatch);
|
||||
|
||||
const wrapper = mount(
|
||||
<reactRedux.Provider store={store}>
|
||||
<PlanUpgradeButton/>
|
||||
</reactRedux.Provider>,
|
||||
renderWithContext(
|
||||
<PlanUpgradeButton/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(0); // no calls to cloud endpoints for non cloud
|
||||
expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(0);
|
||||
expect(cloudProductsSpy).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('#UpgradeButton').exists()).toEqual(false);
|
||||
expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/global/AtMentionsButton should match snapshot 1`] = `
|
||||
<WithTooltip
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Saved messages"
|
||||
id="channel_header.flagged"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<HeaderIconButton
|
||||
<div>
|
||||
<button
|
||||
aria-controls="searchContainer"
|
||||
aria-expanded={false}
|
||||
aria-expanded="false"
|
||||
aria-label="Saved messages"
|
||||
icon="bookmark-outline"
|
||||
onClick={[Function]}
|
||||
toggled={false}
|
||||
/>
|
||||
</WithTooltip>
|
||||
class="HeaderIconButton"
|
||||
>
|
||||
<i
|
||||
class="icon-bookmark-outline"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,44 +1,45 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import IconButton from 'components/global_header/header_icon_button';
|
||||
import {showFlaggedPosts} from 'actions/views/rhs';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import SavedPostsButton from './saved_posts_button';
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
let mockState: GlobalState;
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux') as typeof import('react-redux'),
|
||||
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
|
||||
useDispatch: () => mockDispatch,
|
||||
jest.mock('actions/views/rhs', () => ({
|
||||
closeRightHandSide: jest.fn(() => ({type: 'MOCK_CLOSE_RHS'})),
|
||||
showFlaggedPosts: jest.fn(() => ({type: 'MOCK_SHOW_FLAGGED_POSTS'})),
|
||||
}));
|
||||
|
||||
describe('components/global/AtMentionsButton', () => {
|
||||
beforeEach(() => {
|
||||
mockState = {views: {rhs: {isSidebarOpen: true}}} as GlobalState;
|
||||
});
|
||||
const initialState = {
|
||||
views: {
|
||||
rhs: {
|
||||
isSidebarOpen: true,
|
||||
},
|
||||
},
|
||||
} as unknown as GlobalState;
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<SavedPostsButton/>,
|
||||
initialState,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show active mentions', () => {
|
||||
const wrapper = shallow(
|
||||
test('should show active mentions', async () => {
|
||||
renderWithContext(
|
||||
<SavedPostsButton/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
wrapper.find(IconButton).simulate('click', {
|
||||
preventDefault: jest.fn(),
|
||||
});
|
||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Saved messages'}));
|
||||
expect(showFlaggedPosts).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/DialogIntroductionText should not fail on empty value 1`] = `
|
||||
<DialogIntroductionText
|
||||
emojiMap={
|
||||
EmojiMap {
|
||||
"customEmojis": Map {},
|
||||
"customEmojisArray": Array [],
|
||||
}
|
||||
}
|
||||
id="testblankvalue"
|
||||
value=""
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "",
|
||||
}
|
||||
}
|
||||
id="testblankvalue"
|
||||
/>
|
||||
</DialogIntroductionText>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/DialogIntroductionText should render message with supported values 1`] = `
|
||||
<DialogIntroductionText
|
||||
emojiMap={
|
||||
EmojiMap {
|
||||
"customEmojis": Map {},
|
||||
"customEmojisArray": Array [],
|
||||
}
|
||||
}
|
||||
id="testsupported"
|
||||
value="**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p><strong>bold</strong> <em>italic</em> <a class=\\"theme markdown__link\\" href=\\"https://mattermost.com/\\" rel=\\"noreferrer\\" target=\\"_blank\\">link</a> <br/> <a class=\\"theme markdown__link\\" href=\\"!https://mattermost.com/\\" rel=\\"noreferrer\\" target=\\"_blank\\">link target blank</a></p>",
|
||||
}
|
||||
}
|
||||
id="testsupported"
|
||||
/>
|
||||
</DialogIntroductionText>
|
||||
>
|
||||
<p>
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
|
||||
<em>
|
||||
italic
|
||||
</em>
|
||||
|
||||
<a
|
||||
class="theme markdown__link"
|
||||
href="https://mattermost.com/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
link
|
||||
</a>
|
||||
<br/>
|
||||
<a
|
||||
class="theme markdown__link"
|
||||
href="!https://mattermost.com/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
link target blank
|
||||
</a>
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import RadioSetting from 'components/widgets/settings/radio_setting';
|
||||
import TextSetting from 'components/widgets/settings/text_setting';
|
||||
import {render, screen} from 'tests/react_testing_utils';
|
||||
|
||||
import DialogElement from './dialog_element';
|
||||
|
||||
|
|
@ -23,29 +21,29 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
};
|
||||
|
||||
it('type textarea', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='textarea'
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find(TextSetting).dive().find('textarea').exists()).toBe(true);
|
||||
expect(document.querySelector('textarea')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('subtype blank', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
subtype=''
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(TextSetting).props().type).toEqual('text');
|
||||
expect(screen.getByTestId('testinginput')).toHaveAttribute('type', 'text');
|
||||
});
|
||||
|
||||
describe('subtype number', () => {
|
||||
test('value is 0', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='text'
|
||||
|
|
@ -53,11 +51,11 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
value={0}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find(TextSetting).props().value).toEqual(0);
|
||||
expect(screen.getByTestId('testingnumber')).toHaveValue(0);
|
||||
});
|
||||
|
||||
test('value is 123', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='text'
|
||||
|
|
@ -65,28 +63,28 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
value={123}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find(TextSetting).props().value).toEqual(123);
|
||||
expect(screen.getByTestId('testingnumber')).toHaveValue(123);
|
||||
});
|
||||
});
|
||||
|
||||
it('subtype email', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
subtype='email'
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find(TextSetting).props().type).toEqual('email');
|
||||
expect(screen.getByTestId('testingemail')).toHaveAttribute('type', 'email');
|
||||
});
|
||||
|
||||
it('subtype password', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
subtype='password'
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find(TextSetting).props().type).toEqual('password');
|
||||
expect(screen.getByTestId('testingpassword')).toHaveAttribute('type', 'password');
|
||||
});
|
||||
|
||||
describe('radioSetting', () => {
|
||||
|
|
@ -96,7 +94,7 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
];
|
||||
|
||||
test('RadioSetting is rendered when type is radio', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -104,11 +102,11 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(RadioSetting).exists()).toBe(true);
|
||||
expect(screen.getAllByRole('radio')).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('RadioSetting is rendered when options are null', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -116,11 +114,11 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(RadioSetting).exists()).toBe(true);
|
||||
expect(screen.getByTestId('testing')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('RadioSetting is rendered when options are null and value is null', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -129,11 +127,11 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(RadioSetting).exists()).toBe(true);
|
||||
expect(screen.getByTestId('testing')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('RadioSetting is rendered when options are null and value is not null', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -142,11 +140,11 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(RadioSetting).exists()).toBe(true);
|
||||
expect(screen.getByTestId('testing')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('RadioSetting is rendered when value is not one of the options', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -155,23 +153,25 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(RadioSetting).exists()).toBe(true);
|
||||
expect(screen.getByTestId('testing')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('No default value is selected from the radio button list', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
options={radioOptions}
|
||||
/>,
|
||||
);
|
||||
const instance = wrapper.instance() as DialogElement;
|
||||
expect(instance.props.value).toBeUndefined();
|
||||
const radios = screen.getAllByRole('radio');
|
||||
radios.forEach((radio) => {
|
||||
expect(radio).not.toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test('The default value can be specified from the list', () => {
|
||||
const wrapper = shallow(
|
||||
render(
|
||||
<DialogElement
|
||||
{...baseDialogProps}
|
||||
type='radio'
|
||||
|
|
@ -179,7 +179,7 @@ describe('components/interactive_dialog/DialogElement', () => {
|
|||
value={radioOptions[1].value}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find({options: radioOptions, value: radioOptions[1].value}).exists()).toBe(true);
|
||||
expect(screen.getByRole('radio', {name: radioOptions[1].text})).toBeChecked();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {render} from 'tests/react_testing_utils';
|
||||
import EmojiMap from 'utils/emoji_map';
|
||||
|
||||
import DialogIntroductionText from './dialog_introduction_text';
|
||||
|
|
@ -17,8 +17,8 @@ describe('components/DialogIntroductionText', () => {
|
|||
value: '**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)',
|
||||
emojiMap,
|
||||
};
|
||||
const wrapper = mount(<DialogIntroductionText {...descriptor}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<DialogIntroductionText {...descriptor}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not fail on empty value', () => {
|
||||
|
|
@ -27,7 +27,7 @@ describe('components/DialogIntroductionText', () => {
|
|||
value: '',
|
||||
emojiMap,
|
||||
};
|
||||
const wrapper = mount(<DialogIntroductionText {...descriptor}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<DialogIntroductionText {...descriptor}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,118 +1,86 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/learn_more_trial_modal/learn_more_trial_modal_step should match snapshot 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"getServerState": undefined,
|
||||
"identityFunctionCheck": "once",
|
||||
"stabilityCheck": "once",
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"subscription": Object {
|
||||
"addNestedSub": [Function],
|
||||
"getListeners": [Function],
|
||||
"handleChangeWrapper": [Function],
|
||||
"isSubscribed": [Function],
|
||||
"notifyNestedSubs": [Function],
|
||||
"trySubscribe": [Function],
|
||||
"tryUnsubscribe": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<LearnMoreTrialModalStep
|
||||
buttonLabel="button"
|
||||
description="Step description"
|
||||
id="stepId"
|
||||
svgElement={<svg />}
|
||||
svgWrapperClassName="stepClassname"
|
||||
title="Step title"
|
||||
/>
|
||||
</ContextProvider>
|
||||
<div>
|
||||
<div
|
||||
class="LearnMoreTrialModalStep slide-container"
|
||||
id="learnMoreTrialModalStep-stepId"
|
||||
>
|
||||
<div
|
||||
class="stepClassname svg-wrapper"
|
||||
>
|
||||
<svg />
|
||||
</div>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Step title
|
||||
</div>
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
Step description
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/learn_more_trial_modal/learn_more_trial_modal_step should match snapshot when loaded in cloud workspace 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"getServerState": undefined,
|
||||
"identityFunctionCheck": "once",
|
||||
"stabilityCheck": "once",
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"subscription": Object {
|
||||
"addNestedSub": [Function],
|
||||
"getListeners": [Function],
|
||||
"handleChangeWrapper": [Function],
|
||||
"isSubscribed": [Function],
|
||||
"notifyNestedSubs": [Function],
|
||||
"trySubscribe": [Function],
|
||||
"tryUnsubscribe": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<LearnMoreTrialModalStep
|
||||
bottomLeftMessage="Step bottom message"
|
||||
buttonLabel="button"
|
||||
description="Step description"
|
||||
id="stepId"
|
||||
isCloud={true}
|
||||
svgElement={<svg />}
|
||||
svgWrapperClassName="stepClassname"
|
||||
title="Step title"
|
||||
/>
|
||||
</ContextProvider>
|
||||
<div>
|
||||
<div
|
||||
class="LearnMoreTrialModalStep slide-container"
|
||||
id="learnMoreTrialModalStep-stepId"
|
||||
>
|
||||
<div
|
||||
class="stepClassname svg-wrapper"
|
||||
>
|
||||
<svg />
|
||||
</div>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Step title
|
||||
</div>
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
Step description
|
||||
</div>
|
||||
<div
|
||||
class="bottom-text-left-message"
|
||||
>
|
||||
Step bottom message
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/learn_more_trial_modal/learn_more_trial_modal_step should match snapshot with optional params 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"getServerState": undefined,
|
||||
"identityFunctionCheck": "once",
|
||||
"stabilityCheck": "once",
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"subscription": Object {
|
||||
"addNestedSub": [Function],
|
||||
"getListeners": [Function],
|
||||
"handleChangeWrapper": [Function],
|
||||
"isSubscribed": [Function],
|
||||
"notifyNestedSubs": [Function],
|
||||
"trySubscribe": [Function],
|
||||
"tryUnsubscribe": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<LearnMoreTrialModalStep
|
||||
bottomLeftMessage="Step bottom message"
|
||||
buttonLabel="button"
|
||||
description="Step description"
|
||||
id="stepId"
|
||||
svgElement={<svg />}
|
||||
svgWrapperClassName="stepClassname"
|
||||
title="Step title"
|
||||
/>
|
||||
</ContextProvider>
|
||||
<div>
|
||||
<div
|
||||
class="LearnMoreTrialModalStep slide-container"
|
||||
id="learnMoreTrialModalStep-stepId"
|
||||
>
|
||||
<div
|
||||
class="stepClassname svg-wrapper"
|
||||
>
|
||||
<svg />
|
||||
</div>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Step title
|
||||
</div>
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
Step description
|
||||
</div>
|
||||
<div
|
||||
class="bottom-text-left-message"
|
||||
>
|
||||
Step bottom message
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,12 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/learn_more_trial_modal/start_trial_btn should match snapshot 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"getServerState": undefined,
|
||||
"identityFunctionCheck": "once",
|
||||
"stabilityCheck": "once",
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"subscription": Object {
|
||||
"addNestedSub": [Function],
|
||||
"getListeners": [Function],
|
||||
"handleChangeWrapper": [Function],
|
||||
"isSubscribed": [Function],
|
||||
"notifyNestedSubs": [Function],
|
||||
"trySubscribe": [Function],
|
||||
"tryUnsubscribe": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<StartTrialBtn
|
||||
message="Start trial"
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
</ContextProvider>
|
||||
<div>
|
||||
<a
|
||||
class="btn btn-secondary"
|
||||
id="start_trial_btn"
|
||||
>
|
||||
Start trial
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import {GenericModal} from '@mattermost/components';
|
||||
|
||||
import Carousel from 'components/common/carousel/carousel';
|
||||
import LearnMoreTrialModal from 'components/learn_more_trial_modal/learn_more_trial_modal';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import mockStore from 'tests/test_store';
|
||||
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||
|
||||
jest.mock('components/common/hooks/useOpenStartTrialFormModal', () => ({
|
||||
__esModule: true,
|
||||
default: () => jest.fn(),
|
||||
}));
|
||||
|
||||
describe('components/learn_more_trial_modal/learn_more_trial_modal', () => {
|
||||
// required state to mount using the provider
|
||||
|
|
@ -59,101 +58,86 @@ describe('components/learn_more_trial_modal/learn_more_trial_modal', () => {
|
|||
onExited: jest.fn(),
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModal {...props}/>
|
||||
</Provider>,
|
||||
const {baseElement} = renderWithContext(
|
||||
<LearnMoreTrialModal {...props}/>,
|
||||
state,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show the learn more about trial modal carousel slides', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModal {...props}/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<LearnMoreTrialModal {...props}/>,
|
||||
state,
|
||||
);
|
||||
expect(wrapper.find('LearnMoreTrialModal').find('Carousel')).toHaveLength(1);
|
||||
expect(document.querySelector('#learnMoreTrialModalCarousel')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('should call on close', () => {
|
||||
test('should call on close', async () => {
|
||||
const mockOnClose = jest.fn();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
onClose={mockOnClose}
|
||||
/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
onClose={mockOnClose}
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
wrapper.find(GenericModal).props().onExited?.();
|
||||
await userEvent.click(screen.getByLabelText('Close'));
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call on exited', () => {
|
||||
test('should call on exited', async () => {
|
||||
const mockOnExited = jest.fn();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
onExited={mockOnExited}
|
||||
/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
onExited={mockOnExited}
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
wrapper.find(GenericModal).props().onExited?.();
|
||||
await userEvent.click(screen.getByLabelText('Close'));
|
||||
|
||||
expect(mockOnExited).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(mockOnExited).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should move the slides when clicking carousel next and prev buttons', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
/>
|
||||
</Provider>,
|
||||
test('should move the slides when clicking carousel next and prev buttons', async () => {
|
||||
renderWithContext(
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
// validate the value of the first slide
|
||||
let activeSlide = wrapper.find(Carousel).find('.slide.active-anim');
|
||||
let activeSlideId = activeSlide.find('LearnMoreTrialModalStep').props().id;
|
||||
expect(document.querySelector('.slide.active-anim #learnMoreTrialModalStep-useSso')).not.toBeNull();
|
||||
|
||||
expect(activeSlideId).toBe('useSso');
|
||||
|
||||
const nextButton = wrapper.find(Carousel).find('CarouselButton div.chevron-right');
|
||||
const prevButton = wrapper.find(Carousel).find('CarouselButton div.chevron-left');
|
||||
const nextButton = document.querySelector('.chevron-right')!;
|
||||
const prevButton = document.querySelector('.chevron-left')!;
|
||||
|
||||
// move to the second slide
|
||||
nextButton.simulate('click');
|
||||
await userEvent.click(nextButton);
|
||||
|
||||
activeSlide = wrapper.find(Carousel).find('.slide.active-anim');
|
||||
activeSlideId = activeSlide.find('LearnMoreTrialModalStep').props().id;
|
||||
|
||||
expect(activeSlideId).toBe('ldap');
|
||||
expect(document.querySelector('.slide.active-anim #learnMoreTrialModalStep-ldap')).not.toBeNull();
|
||||
|
||||
// move to the third slide
|
||||
nextButton.simulate('click');
|
||||
await userEvent.click(nextButton);
|
||||
|
||||
activeSlide = wrapper.find(Carousel).find('.slide.active-anim');
|
||||
activeSlideId = activeSlide.find('LearnMoreTrialModalStep').props().id;
|
||||
|
||||
expect(activeSlideId).toBe('systemConsole');
|
||||
expect(document.querySelector('.slide.active-anim #learnMoreTrialModalStep-systemConsole')).not.toBeNull();
|
||||
|
||||
// move back to the second slide
|
||||
prevButton.simulate('click');
|
||||
await userEvent.click(prevButton);
|
||||
|
||||
activeSlide = wrapper.find(Carousel).find('.slide.active-anim');
|
||||
activeSlideId = activeSlide.find('LearnMoreTrialModalStep').props().id;
|
||||
|
||||
expect(activeSlideId).toBe('ldap');
|
||||
expect(document.querySelector('.slide.active-anim #learnMoreTrialModalStep-ldap')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('should have the self hosted request trial button cloud free is disabled', () => {
|
||||
|
|
@ -170,18 +154,15 @@ describe('components/learn_more_trial_modal/learn_more_trial_modal', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
const nonCloudStore = mockStore(nonCloudState);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={nonCloudStore}>
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<LearnMoreTrialModal
|
||||
{...props}
|
||||
/>,
|
||||
nonCloudState,
|
||||
);
|
||||
|
||||
// validate the cloud start trial button is not present
|
||||
const selfHostedRequestTrialButton = wrapper.find('StartTrialBtn');
|
||||
expect(selfHostedRequestTrialButton).toHaveLength(1);
|
||||
expect(document.querySelector('#start_trial_btn')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import LearnMoreTrialModalStep from 'components/learn_more_trial_modal/learn_more_trial_modal_step';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import {renderWithContext} from 'tests/react_testing_utils';
|
||||
|
||||
describe('components/learn_more_trial_modal/learn_more_trial_modal_step', () => {
|
||||
const props = {
|
||||
|
|
@ -43,42 +41,37 @@ describe('components/learn_more_trial_modal/learn_more_trial_modal_step', () =>
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModalStep {...props}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<LearnMoreTrialModalStep {...props}/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot with optional params', () => {
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModalStep
|
||||
{...props}
|
||||
bottomLeftMessage='Step bottom message'
|
||||
/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<LearnMoreTrialModalStep
|
||||
{...props}
|
||||
bottomLeftMessage='Step bottom message'
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot when loaded in cloud workspace', () => {
|
||||
const cloudProps = {...props, isCloud: true};
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<LearnMoreTrialModalStep
|
||||
{...cloudProps}
|
||||
bottomLeftMessage='Step bottom message'
|
||||
/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<LearnMoreTrialModalStep
|
||||
{...cloudProps}
|
||||
bottomLeftMessage='Step bottom message'
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import StartTrialBtn from 'components/learn_more_trial_modal/start_trial_btn';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {act} from 'tests/react_testing_utils';
|
||||
import mockStore from 'tests/test_store';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
jest.mock('mattermost-redux/actions/general', () => ({
|
||||
...jest.requireActual('mattermost-redux/actions/general'),
|
||||
|
|
@ -28,6 +24,11 @@ jest.mock('actions/admin_actions', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
jest.mock('components/common/hooks/useOpenStartTrialFormModal', () => ({
|
||||
__esModule: true,
|
||||
default: () => jest.fn(),
|
||||
}));
|
||||
|
||||
describe('components/learn_more_trial_modal/start_trial_btn', () => {
|
||||
const state = {
|
||||
entities: {
|
||||
|
|
@ -62,38 +63,31 @@ describe('components/learn_more_trial_modal/start_trial_btn', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const props = {
|
||||
onClick: jest.fn(),
|
||||
message: 'Start trial',
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<StartTrialBtn {...props}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<StartTrialBtn {...props}/>,
|
||||
state,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should handle on click', async () => {
|
||||
const mockOnClick = jest.fn();
|
||||
|
||||
// Mount the component
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<StartTrialBtn
|
||||
{...props}
|
||||
onClick={mockOnClick}
|
||||
/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<StartTrialBtn
|
||||
{...props}
|
||||
onClick={mockOnClick}
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
wrapper.find('.btn-secondary').simulate('click');
|
||||
});
|
||||
await userEvent.click(container.querySelector('.btn-secondary')!);
|
||||
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -101,44 +95,17 @@ describe('components/learn_more_trial_modal/start_trial_btn', () => {
|
|||
test('should handle on click when rendered as button', async () => {
|
||||
const mockOnClick = jest.fn();
|
||||
|
||||
// Mount the component
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<StartTrialBtn
|
||||
{...props}
|
||||
renderAsButton={true}
|
||||
onClick={mockOnClick}
|
||||
/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<StartTrialBtn
|
||||
{...props}
|
||||
renderAsButton={true}
|
||||
onClick={mockOnClick}
|
||||
/>,
|
||||
state,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
wrapper.find('button').simulate('click');
|
||||
});
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// test('does not show success for embargoed countries', async () => {
|
||||
// const mockOnClick = jest.fn();
|
||||
|
||||
// const clonedState = JSON.parse(JSON.stringify(state));
|
||||
// clonedState.entities.admin.analytics.TOTAL_USERS = 451;
|
||||
|
||||
// // Mount the component
|
||||
// const wrapper = mountWithIntl(
|
||||
// <Provider store={mockStore(clonedState)}>
|
||||
// <StartTrialBtn
|
||||
// {...props}
|
||||
// onClick={mockOnClick}
|
||||
// />
|
||||
// </Provider>,
|
||||
// );
|
||||
|
||||
// act(() => {
|
||||
// wrapper.find('.start-trial-btn').simulate('click');
|
||||
// });
|
||||
|
||||
// expect(mockOnClick).not.toHaveBeenCalled();
|
||||
// });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,32 +2,23 @@
|
|||
|
||||
exports[`components/mfa/components/Confirm should match snapshot 1`] = `
|
||||
<div>
|
||||
<form
|
||||
className="form-group"
|
||||
onKeyPress={[Function]}
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<strong>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Set up complete!"
|
||||
id="mfa.confirm.complete"
|
||||
/>
|
||||
</strong>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Your account is now secure. Next time you sign in, you will be asked to enter a code from your authenticator app on your phone."
|
||||
id="mfa.confirm.secure"
|
||||
/>
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
type="submit"
|
||||
<div>
|
||||
<form
|
||||
class="form-group"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Okay"
|
||||
id="mfa.confirm.okay"
|
||||
/>
|
||||
</button>
|
||||
</form>
|
||||
<strong>
|
||||
Set up complete!
|
||||
</strong>
|
||||
<p>
|
||||
Your account is now secure. Next time you sign in, you will be asked to enter a code from your authenticator app on your phone.
|
||||
</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
Okay
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {redirectUserToDefaultTeam} from 'actions/global_actions';
|
||||
|
||||
import Confirm from 'components/mfa/confirm';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import Constants from 'utils/constants';
|
||||
|
||||
jest.mock('actions/global_actions', () => ({
|
||||
|
|
@ -23,13 +22,14 @@ describe('components/mfa/components/Confirm', () => {
|
|||
});
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(<Confirm/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<Confirm/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should submit on form submit', () => {
|
||||
const wrapper = mountWithIntl(<Confirm/>);
|
||||
wrapper.find('form').simulate('submit');
|
||||
test('should submit on form submit', async () => {
|
||||
renderWithContext(<Confirm/>);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Okay'}));
|
||||
|
||||
expect(redirectUserToDefaultTeam).toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -42,7 +42,7 @@ describe('components/mfa/components/Confirm', () => {
|
|||
map[event] = callback;
|
||||
});
|
||||
|
||||
mountWithIntl(<Confirm/>);
|
||||
renderWithContext(<Confirm/>);
|
||||
|
||||
const event = {
|
||||
preventDefault: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -2,84 +2,55 @@
|
|||
|
||||
exports[`components/mfa/setup should match snapshot without required text 1`] = `
|
||||
<div>
|
||||
<form
|
||||
className="form-group"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="1. Scan the QR code below using an authenticator app of your choice, such as Google Authenticator, Microsoft Authenticator app, or 1Password."
|
||||
id="mfa.setup.step1"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Alternatively, enter the secret key displayed below into the authenticator app manually."
|
||||
id="mfa.setup.step2_secret"
|
||||
/>
|
||||
</p>
|
||||
<div
|
||||
className="form-group"
|
||||
<div>
|
||||
<form
|
||||
class="form-group"
|
||||
>
|
||||
<p>
|
||||
1. Scan the QR code below using an authenticator app of your choice, such as Google Authenticator, Microsoft Authenticator app, or 1Password.
|
||||
</p>
|
||||
<p>
|
||||
Alternatively, enter the secret key displayed below into the authenticator app manually.
|
||||
</p>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
class="form-group"
|
||||
>
|
||||
<img
|
||||
alt="qr code image"
|
||||
src="data:image/png;base64,"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": 170,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
class="col-sm-12"
|
||||
>
|
||||
<img
|
||||
alt="qr code image"
|
||||
src="data:image/png;base64,"
|
||||
style="max-height: 170px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<p
|
||||
className="col-sm-12"
|
||||
<br />
|
||||
<div
|
||||
class="form-group"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Secret: {secret}"
|
||||
id="mfa.setup.secret"
|
||||
values={
|
||||
Object {
|
||||
"secret": "",
|
||||
}
|
||||
}
|
||||
<p
|
||||
class="col-sm-12"
|
||||
>
|
||||
Secret:
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
2. Enter the code generated by the authenticator app in the field below.
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="MFA Code"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="2. Enter the code generated by the authenticator app in the field below."
|
||||
id="mfa.setup.step3_code"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<LocalizedPlaceholderInput
|
||||
autoFocus={true}
|
||||
className="form-control"
|
||||
placeholder={
|
||||
Object {
|
||||
"defaultMessage": "MFA Code",
|
||||
"id": "mfa.setup.code",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Save"
|
||||
id="mfa.setup.save"
|
||||
/>
|
||||
</button>
|
||||
</form>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import Setup from 'components/mfa/setup/setup';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {act, waitFor} from 'tests/react_testing_utils';
|
||||
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
jest.mock('actions/global_actions', () => ({
|
||||
|
|
@ -32,12 +30,12 @@ describe('components/mfa/setup', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot without required text', async () => {
|
||||
const wrapper = shallow<Setup>(
|
||||
const {container} = renderWithContext(
|
||||
<Setup {...baseProps}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const requiredText = wrapper.find('#mfa.setup.required_mfa');
|
||||
expect(requiredText).not.toBeFalsy();
|
||||
expect(container).toMatchSnapshot();
|
||||
const requiredText = screen.queryByText(/Multi-factor authentication is required/);
|
||||
expect(requiredText).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should match snapshot with required text', async () => {
|
||||
|
|
@ -46,30 +44,32 @@ describe('components/mfa/setup', () => {
|
|||
enforceMultifactorAuthentication: true,
|
||||
};
|
||||
|
||||
const wrapper = shallow<Setup>(
|
||||
renderWithContext(
|
||||
<Setup {...props}/>,
|
||||
);
|
||||
const requiredText = wrapper.find('#mfa.setup.required_mfa');
|
||||
const requiredText = screen.queryByText(/Multi-factor authentication is required/);
|
||||
expect(requiredText).toBeDefined();
|
||||
});
|
||||
|
||||
test('should set state after calling component did mount', async () => {
|
||||
const wrapper = shallow<Setup>(
|
||||
renderWithContext(
|
||||
<Setup {...baseProps}/>,
|
||||
);
|
||||
expect(generateMfaSecret).toHaveBeenCalled();
|
||||
await wrapper.instance().componentDidMount();
|
||||
expect(wrapper.state('secret')).toEqual('generated secret');
|
||||
expect(wrapper.state('qrCode')).toEqual('qrcode');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/generated secret/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call activateMfa on submission', async () => {
|
||||
const wrapper = mountWithIntl(
|
||||
renderWithContext(
|
||||
<Setup {...baseProps}/>,
|
||||
);
|
||||
|
||||
(wrapper.instance() as Setup).input.current!.value = 'testcodeinput';
|
||||
wrapper.find('form').simulate('submit', {preventDefault: () => {}});
|
||||
const input = screen.getByPlaceholderText('MFA Code');
|
||||
await userEvent.type(input, 'testcodeinput');
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(baseProps.actions.activateMfa).toHaveBeenCalledWith('testcodeinput');
|
||||
|
|
@ -77,16 +77,15 @@ describe('components/mfa/setup', () => {
|
|||
});
|
||||
|
||||
test('should focus input when code is empty', async () => {
|
||||
const wrapper = mountWithIntl(
|
||||
renderWithContext(
|
||||
<Setup {...baseProps}/>,
|
||||
);
|
||||
const input = wrapper.find('input').getDOMNode() as HTMLInputElement;
|
||||
const focusSpy = jest.spyOn(input, 'focus');
|
||||
const input = screen.getByPlaceholderText('MFA Code');
|
||||
|
||||
wrapper.find('form').simulate('submit', {preventDefault: () => {}});
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(focusSpy).toHaveBeenCalled();
|
||||
expect(input).toHaveFocus();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -104,19 +103,16 @@ describe('components/mfa/setup', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
renderWithContext(
|
||||
<Setup {...props}/>,
|
||||
);
|
||||
const input = wrapper.find('input').getDOMNode() as HTMLInputElement;
|
||||
const focusSpy = jest.spyOn(input, 'focus');
|
||||
const input = screen.getByPlaceholderText('MFA Code');
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as Setup).input.current!.value = 'invalidcode';
|
||||
wrapper.find('form').simulate('submit', {preventDefault: () => {}});
|
||||
});
|
||||
await userEvent.type(input, 'invalidcode');
|
||||
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(focusSpy).toHaveBeenCalled();
|
||||
expect(input).toHaveFocus();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,528 +1,489 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/multiselect/multiselect MultiSelectList should match snapshot when custom no option message is defined 1`] = `
|
||||
<Fragment>
|
||||
<div>
|
||||
<div
|
||||
className="filtered-user-list"
|
||||
class="filtered-user-list"
|
||||
>
|
||||
<div
|
||||
className="filter-row"
|
||||
class="filter-row"
|
||||
>
|
||||
<div
|
||||
className="multi-select__container react-select"
|
||||
class="multi-select__container react-select"
|
||||
>
|
||||
<ForwardRef
|
||||
aria-invalid={false}
|
||||
className=""
|
||||
classNamePrefix="react-select-auto react-select"
|
||||
components={
|
||||
Object {
|
||||
"IndicatorsContainer": [Function],
|
||||
"Menu": [Function],
|
||||
"MultiValueLabel": [Function],
|
||||
"MultiValueRemove": [Function],
|
||||
}
|
||||
}
|
||||
getOptionLabel={[Function]}
|
||||
getOptionValue={[Function]}
|
||||
id="selectItems"
|
||||
inputValue=""
|
||||
isClearable={false}
|
||||
isMulti={true}
|
||||
menuIsOpen={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onInputChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"label": "0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"id": "1",
|
||||
"label": "1",
|
||||
"value": "1",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"label": "2",
|
||||
"value": "2",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"label": "3",
|
||||
"value": "3",
|
||||
},
|
||||
Object {
|
||||
"id": "4",
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
},
|
||||
Object {
|
||||
"id": "5",
|
||||
"label": "5",
|
||||
"value": "5",
|
||||
},
|
||||
Object {
|
||||
"id": "6",
|
||||
"label": "6",
|
||||
"value": "6",
|
||||
},
|
||||
Object {
|
||||
"id": "7",
|
||||
"label": "7",
|
||||
"value": "7",
|
||||
},
|
||||
]
|
||||
}
|
||||
styles={
|
||||
Object {
|
||||
"container": [Function],
|
||||
}
|
||||
}
|
||||
value={
|
||||
Array [
|
||||
Object {
|
||||
"id": "id",
|
||||
"label": "label",
|
||||
"value": "value",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SaveButton
|
||||
disabled={false}
|
||||
id="saveItems"
|
||||
onClick={[Function]}
|
||||
saving={false}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
id="multiSelectHelpMemberInfo"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{memberOptions, number} of {totalCount, number} members"
|
||||
id="multiselect.numMembers"
|
||||
values={
|
||||
Object {
|
||||
"memberOptions": 5,
|
||||
"totalCount": 8,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MultiSelectList
|
||||
ariaLabelRenderer={[Function]}
|
||||
customNoOptionsMessage={
|
||||
<div
|
||||
className="custom-no-options-message"
|
||||
class="css-1dedlln"
|
||||
id="selectItems"
|
||||
>
|
||||
<span
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
id="react-select-5-live-region"
|
||||
/>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
aria-live="polite"
|
||||
aria-relevant="additions text"
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
role="log"
|
||||
/>
|
||||
<div
|
||||
class="react-select-auto react-select__control css-13cymwt-control"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__value-container react-select-auto react-select__value-container--is-multi react-select-auto react-select__value-container--has-value css-1dyz3mf"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__multi-value css-1p3m7a8-multiValue"
|
||||
>
|
||||
<div
|
||||
class="react-select__padded-component"
|
||||
>
|
||||
<span>
|
||||
label
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Remove label"
|
||||
class="react-select-auto react-select__multi-value__remove css-v7duua"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-tj5bde-Svg"
|
||||
focusable="false"
|
||||
height="14"
|
||||
viewBox="0 0 20 20"
|
||||
width="14"
|
||||
>
|
||||
<path
|
||||
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="react-select-auto react-select__input-container css-19bb58m"
|
||||
data-value=""
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-invalid="false"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
class="react-select-auto react-select__input"
|
||||
id="react-select-5-input"
|
||||
role="combobox"
|
||||
spellcheck="false"
|
||||
style="color: inherit; background: 0px; opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||
tabindex="0"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveItems"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
No matches found
|
||||
Save
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
onAction={[Function]}
|
||||
onAdd={[Function]}
|
||||
onSelect={[Function]}
|
||||
optionRenderer={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"label": "0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"id": "1",
|
||||
"label": "1",
|
||||
"value": "1",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"label": "2",
|
||||
"value": "2",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"label": "3",
|
||||
"value": "3",
|
||||
},
|
||||
Object {
|
||||
"id": "4",
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
},
|
||||
]
|
||||
}
|
||||
perPage={50}
|
||||
query=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="multi-select__help"
|
||||
id="multiSelectHelpMemberInfo"
|
||||
>
|
||||
5 of 8 members
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
aria-live="polite"
|
||||
class="multi-select__wrapper"
|
||||
>
|
||||
<div
|
||||
class="more-modal__list"
|
||||
>
|
||||
<div
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
role="status"
|
||||
>
|
||||
5 results found for your search.
|
||||
</div>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
/>
|
||||
<div
|
||||
class="more-modal__options"
|
||||
id="multiSelectList"
|
||||
role="presentation"
|
||||
>
|
||||
<div>
|
||||
0
|
||||
</div>
|
||||
<div>
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
4
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="multi-select__help"
|
||||
id="multiSelectMessageNote"
|
||||
/>
|
||||
<div
|
||||
className="filter-controls"
|
||||
class="filter-controls"
|
||||
>
|
||||
<button
|
||||
className="btn btn-sm btn-tertiary filter-control filter-control__next"
|
||||
onClick={[Function]}
|
||||
class="btn btn-sm btn-tertiary filter-control filter-control__next"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Next"
|
||||
id="filtered_user_list.next"
|
||||
/>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/multiselect/multiselect should match snapshot 1`] = `
|
||||
<Fragment>
|
||||
<div>
|
||||
<div
|
||||
className="filtered-user-list"
|
||||
class="filtered-user-list"
|
||||
>
|
||||
<div
|
||||
className="filter-row"
|
||||
class="filter-row"
|
||||
>
|
||||
<div
|
||||
className="multi-select__container react-select"
|
||||
class="multi-select__container react-select"
|
||||
>
|
||||
<ForwardRef
|
||||
aria-invalid={false}
|
||||
className=""
|
||||
classNamePrefix="react-select-auto react-select"
|
||||
components={
|
||||
Object {
|
||||
"IndicatorsContainer": [Function],
|
||||
"Menu": [Function],
|
||||
"MultiValueLabel": [Function],
|
||||
"MultiValueRemove": [Function],
|
||||
}
|
||||
}
|
||||
getOptionLabel={[Function]}
|
||||
getOptionValue={[Function]}
|
||||
<div
|
||||
class="css-1dedlln"
|
||||
id="selectItems"
|
||||
inputValue=""
|
||||
isClearable={false}
|
||||
isMulti={true}
|
||||
menuIsOpen={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onInputChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"label": "0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"id": "1",
|
||||
"label": "1",
|
||||
"value": "1",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"label": "2",
|
||||
"value": "2",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"label": "3",
|
||||
"value": "3",
|
||||
},
|
||||
Object {
|
||||
"id": "4",
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
},
|
||||
Object {
|
||||
"id": "5",
|
||||
"label": "5",
|
||||
"value": "5",
|
||||
},
|
||||
Object {
|
||||
"id": "6",
|
||||
"label": "6",
|
||||
"value": "6",
|
||||
},
|
||||
Object {
|
||||
"id": "7",
|
||||
"label": "7",
|
||||
"value": "7",
|
||||
},
|
||||
]
|
||||
}
|
||||
styles={
|
||||
Object {
|
||||
"container": [Function],
|
||||
}
|
||||
}
|
||||
value={
|
||||
Array [
|
||||
Object {
|
||||
"id": "id",
|
||||
"label": "label",
|
||||
"value": "value",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SaveButton
|
||||
disabled={false}
|
||||
>
|
||||
<span
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
id="react-select-2-live-region"
|
||||
/>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
aria-live="polite"
|
||||
aria-relevant="additions text"
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
role="log"
|
||||
/>
|
||||
<div
|
||||
class="react-select-auto react-select__control css-13cymwt-control"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__value-container react-select-auto react-select__value-container--is-multi react-select-auto react-select__value-container--has-value css-1dyz3mf"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__multi-value css-1p3m7a8-multiValue"
|
||||
>
|
||||
<div
|
||||
class="react-select__padded-component"
|
||||
>
|
||||
<span>
|
||||
label
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Remove label"
|
||||
class="react-select-auto react-select__multi-value__remove css-v7duua"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-tj5bde-Svg"
|
||||
focusable="false"
|
||||
height="14"
|
||||
viewBox="0 0 20 20"
|
||||
width="14"
|
||||
>
|
||||
<path
|
||||
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="react-select-auto react-select__input-container css-19bb58m"
|
||||
data-value=""
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-invalid="false"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
class="react-select-auto react-select__input"
|
||||
id="react-select-2-input"
|
||||
role="combobox"
|
||||
spellcheck="false"
|
||||
style="color: inherit; background: 0px; opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||
tabindex="0"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveItems"
|
||||
onClick={[Function]}
|
||||
saving={false}
|
||||
/>
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
class="multi-select__help"
|
||||
id="multiSelectHelpMemberInfo"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{memberOptions, number} of {totalCount, number} members"
|
||||
id="multiselect.numMembers"
|
||||
values={
|
||||
Object {
|
||||
"memberOptions": 5,
|
||||
"totalCount": 8,
|
||||
}
|
||||
}
|
||||
/>
|
||||
5 of 8 members
|
||||
</div>
|
||||
</div>
|
||||
<MultiSelectList
|
||||
ariaLabelRenderer={[Function]}
|
||||
onAction={[Function]}
|
||||
onAdd={[Function]}
|
||||
onSelect={[Function]}
|
||||
optionRenderer={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"label": "0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"id": "1",
|
||||
"label": "1",
|
||||
"value": "1",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"label": "2",
|
||||
"value": "2",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"label": "3",
|
||||
"value": "3",
|
||||
},
|
||||
Object {
|
||||
"id": "4",
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
},
|
||||
]
|
||||
}
|
||||
perPage={50}
|
||||
query=""
|
||||
/>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
aria-live="polite"
|
||||
class="multi-select__wrapper"
|
||||
>
|
||||
<div
|
||||
class="more-modal__list"
|
||||
>
|
||||
<div
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
role="status"
|
||||
>
|
||||
5 results found for your search.
|
||||
</div>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
/>
|
||||
<div
|
||||
class="more-modal__options"
|
||||
id="multiSelectList"
|
||||
role="presentation"
|
||||
>
|
||||
<div>
|
||||
0
|
||||
</div>
|
||||
<div>
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
4
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="multi-select__help"
|
||||
id="multiSelectMessageNote"
|
||||
/>
|
||||
<div
|
||||
className="filter-controls"
|
||||
class="filter-controls"
|
||||
>
|
||||
<button
|
||||
className="btn btn-sm btn-tertiary filter-control filter-control__next"
|
||||
onClick={[Function]}
|
||||
class="btn btn-sm btn-tertiary filter-control filter-control__next"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Next"
|
||||
id="filtered_user_list.next"
|
||||
/>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/multiselect/multiselect should match snapshot for page 2 1`] = `
|
||||
<Fragment>
|
||||
<div>
|
||||
<div
|
||||
className="filtered-user-list"
|
||||
class="filtered-user-list"
|
||||
>
|
||||
<div
|
||||
className="filter-row"
|
||||
class="filter-row"
|
||||
>
|
||||
<div
|
||||
className="multi-select__container react-select"
|
||||
class="multi-select__container react-select"
|
||||
>
|
||||
<ForwardRef
|
||||
aria-invalid={false}
|
||||
className=""
|
||||
classNamePrefix="react-select-auto react-select"
|
||||
components={
|
||||
Object {
|
||||
"IndicatorsContainer": [Function],
|
||||
"Menu": [Function],
|
||||
"MultiValueLabel": [Function],
|
||||
"MultiValueRemove": [Function],
|
||||
}
|
||||
}
|
||||
getOptionLabel={[Function]}
|
||||
getOptionValue={[Function]}
|
||||
<div
|
||||
class="css-1dedlln"
|
||||
id="selectItems"
|
||||
inputValue=""
|
||||
isClearable={false}
|
||||
isMulti={true}
|
||||
menuIsOpen={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onInputChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"label": "0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"id": "1",
|
||||
"label": "1",
|
||||
"value": "1",
|
||||
},
|
||||
Object {
|
||||
"id": "2",
|
||||
"label": "2",
|
||||
"value": "2",
|
||||
},
|
||||
Object {
|
||||
"id": "3",
|
||||
"label": "3",
|
||||
"value": "3",
|
||||
},
|
||||
Object {
|
||||
"id": "4",
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
},
|
||||
Object {
|
||||
"id": "5",
|
||||
"label": "5",
|
||||
"value": "5",
|
||||
},
|
||||
Object {
|
||||
"id": "6",
|
||||
"label": "6",
|
||||
"value": "6",
|
||||
},
|
||||
Object {
|
||||
"id": "7",
|
||||
"label": "7",
|
||||
"value": "7",
|
||||
},
|
||||
]
|
||||
}
|
||||
styles={
|
||||
Object {
|
||||
"container": [Function],
|
||||
}
|
||||
}
|
||||
value={
|
||||
Array [
|
||||
Object {
|
||||
"id": "id",
|
||||
"label": "label",
|
||||
"value": "value",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SaveButton
|
||||
disabled={false}
|
||||
>
|
||||
<span
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
id="react-select-3-live-region"
|
||||
/>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
aria-live="polite"
|
||||
aria-relevant="additions text"
|
||||
class="css-1f43avz-a11yText-A11yText"
|
||||
role="log"
|
||||
/>
|
||||
<div
|
||||
class="react-select-auto react-select__control css-13cymwt-control"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__value-container react-select-auto react-select__value-container--is-multi react-select-auto react-select__value-container--has-value css-1dyz3mf"
|
||||
>
|
||||
<div
|
||||
class="react-select-auto react-select__multi-value css-1p3m7a8-multiValue"
|
||||
>
|
||||
<div
|
||||
class="react-select__padded-component"
|
||||
>
|
||||
<span>
|
||||
label
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Remove label"
|
||||
class="react-select-auto react-select__multi-value__remove css-v7duua"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-tj5bde-Svg"
|
||||
focusable="false"
|
||||
height="14"
|
||||
viewBox="0 0 20 20"
|
||||
width="14"
|
||||
>
|
||||
<path
|
||||
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="react-select-auto react-select__input-container css-19bb58m"
|
||||
data-value=""
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-invalid="false"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
class="react-select-auto react-select__input"
|
||||
id="react-select-3-input"
|
||||
role="combobox"
|
||||
spellcheck="false"
|
||||
style="color: inherit; background: 0px; opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||
tabindex="0"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveItems"
|
||||
onClick={[Function]}
|
||||
saving={false}
|
||||
/>
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
class="multi-select__help"
|
||||
id="multiSelectHelpMemberInfo"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{memberOptions, number} of {totalCount, number} members"
|
||||
id="multiselect.numMembers"
|
||||
values={
|
||||
Object {
|
||||
"memberOptions": 3,
|
||||
"totalCount": 8,
|
||||
}
|
||||
}
|
||||
/>
|
||||
3 of 8 members
|
||||
</div>
|
||||
</div>
|
||||
<MultiSelectList
|
||||
ariaLabelRenderer={[Function]}
|
||||
onAction={[Function]}
|
||||
onAdd={[Function]}
|
||||
onSelect={[Function]}
|
||||
optionRenderer={[Function]}
|
||||
options={
|
||||
Array [
|
||||
Object {
|
||||
"id": "5",
|
||||
"label": "5",
|
||||
"value": "5",
|
||||
},
|
||||
Object {
|
||||
"id": "6",
|
||||
"label": "6",
|
||||
"value": "6",
|
||||
},
|
||||
Object {
|
||||
"id": "7",
|
||||
"label": "7",
|
||||
"value": "7",
|
||||
},
|
||||
]
|
||||
}
|
||||
perPage={50}
|
||||
query=""
|
||||
/>
|
||||
<div
|
||||
className="multi-select__help"
|
||||
aria-live="polite"
|
||||
class="multi-select__wrapper"
|
||||
>
|
||||
<div
|
||||
class="more-modal__list"
|
||||
>
|
||||
<div
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
role="status"
|
||||
>
|
||||
3 results found for your search.
|
||||
</div>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="polite"
|
||||
class="sr-only"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
<div
|
||||
class="more-modal__options"
|
||||
id="multiSelectList"
|
||||
role="presentation"
|
||||
>
|
||||
<div>
|
||||
5
|
||||
</div>
|
||||
<div>
|
||||
6
|
||||
</div>
|
||||
<div>
|
||||
7
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="multi-select__help"
|
||||
id="multiSelectMessageNote"
|
||||
/>
|
||||
<div
|
||||
className="filter-controls"
|
||||
class="filter-controls"
|
||||
>
|
||||
<button
|
||||
className="btn btn-sm btn-tertiary filter-control filter-control__prev"
|
||||
onClick={[Function]}
|
||||
class="btn btn-sm btn-tertiary filter-control filter-control__prev"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Previous"
|
||||
id="filtered_user_list.prev"
|
||||
/>
|
||||
Previous
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,73 +1,70 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
import {createIntl} from 'react-intl';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import MultiSelect from './multiselect';
|
||||
import type {Value} from './multiselect';
|
||||
import MultiSelectList from './multiselect_list';
|
||||
import type {Props as MultiSelectProps} from './multiselect_list';
|
||||
|
||||
const element = () => <div/>;
|
||||
|
||||
describe('components/multiselect/multiselect', () => {
|
||||
const intl = createIntl({locale: 'en'});
|
||||
const totalCount = 8;
|
||||
const optionsNumber = 8;
|
||||
const users = [];
|
||||
const users: Value[] = [];
|
||||
for (let i = 0; i < optionsNumber; i++) {
|
||||
users.push({id: `${i}`, label: `${i}`, value: `${i}`});
|
||||
}
|
||||
|
||||
const baseProps = {
|
||||
ariaLabelRenderer: element as any,
|
||||
ariaLabelRenderer: (option: Value) => option?.label ?? '',
|
||||
handleAdd: jest.fn(),
|
||||
handleDelete: jest.fn(),
|
||||
handleInput: jest.fn(),
|
||||
handleSubmit: jest.fn(),
|
||||
intl: {} as IntlShape,
|
||||
optionRenderer: element,
|
||||
intl,
|
||||
optionRenderer: (option: Value) => <div key={option.id}>{option.label}</div>,
|
||||
options: users,
|
||||
perPage: 5,
|
||||
saving: false,
|
||||
totalCount,
|
||||
users,
|
||||
valueRenderer: element as any,
|
||||
valueRenderer: (props: {data: Value}) => <span>{props.data.label}</span>,
|
||||
values: [{id: 'id', label: 'label', value: 'value'}],
|
||||
valueWithImage: false,
|
||||
focusOnLoad: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<MultiSelect
|
||||
{...baseProps}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot for page 2', () => {
|
||||
const wrapper = shallow(
|
||||
test('should match snapshot for page 2', async () => {
|
||||
const {container} = renderWithContext(
|
||||
<MultiSelect
|
||||
{...baseProps}
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper.find('.filter-control__next').simulate('click');
|
||||
wrapper.update();
|
||||
expect(wrapper.state('page')).toEqual(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
await userEvent.click(screen.getByText('Next'));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('MultiSelectList should match state on next page', () => {
|
||||
test('MultiSelectList should match state on next page', async () => {
|
||||
const renderOption: MultiSelectProps<Value>['optionRenderer'] = (option, isSelected, onAdd, onMouseMove) => {
|
||||
return (
|
||||
<p
|
||||
key={option.id}
|
||||
className={isSelected ? 'option--selected' : ''}
|
||||
onClick={() => onAdd(option)}
|
||||
onMouseMove={() => onMouseMove(option)}
|
||||
>
|
||||
|
|
@ -80,7 +77,7 @@ describe('components/multiselect/multiselect', () => {
|
|||
return props.data.value;
|
||||
};
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
renderWithContext(
|
||||
<MultiSelect
|
||||
{...baseProps}
|
||||
optionRenderer={renderOption}
|
||||
|
|
@ -88,9 +85,14 @@ describe('components/multiselect/multiselect', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(MultiSelectList).state('selected')).toEqual(-1);
|
||||
wrapper.find('.filter-control__next').simulate('click');
|
||||
expect(wrapper.find(MultiSelectList).state('selected')).toEqual(0);
|
||||
// Initially no option should be selected (selected = -1)
|
||||
expect(document.querySelector('.option--selected')).not.toBeInTheDocument();
|
||||
|
||||
// Click next page
|
||||
await userEvent.click(screen.getByText('Next'));
|
||||
|
||||
// After clicking next, the first option on the new page should be selected (selected = 0)
|
||||
expect(document.querySelector('.option--selected')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('MultiSelectList should match snapshot when custom no option message is defined', () => {
|
||||
|
|
@ -100,19 +102,19 @@ describe('components/multiselect/multiselect', () => {
|
|||
</div>
|
||||
);
|
||||
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<MultiSelect
|
||||
{...baseProps}
|
||||
customNoOptionsMessage={customNoOptionsMessage}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Back button should be customizable', () => {
|
||||
test('Back button should be customizable', async () => {
|
||||
const handleBackButtonClick = jest.fn();
|
||||
const wrapper = mountWithIntl(
|
||||
renderWithContext(
|
||||
<MultiSelect
|
||||
{...baseProps}
|
||||
backButtonClick={handleBackButtonClick}
|
||||
|
|
@ -122,11 +124,11 @@ describe('components/multiselect/multiselect', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const backButton = wrapper.find('div.multi-select__footer button.tertiary-button');
|
||||
const backButton = screen.getByRole('button', {name: 'Cancel'});
|
||||
|
||||
backButton.simulate('click');
|
||||
await userEvent.click(backButton);
|
||||
|
||||
expect(backButton).toHaveLength(1);
|
||||
expect(backButton).toBeInTheDocument();
|
||||
|
||||
expect(handleBackButtonClick).toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {fireEvent, renderWithContext} from 'tests/react_testing_utils';
|
||||
|
||||
import type {Value} from './multiselect';
|
||||
import MultiSelectList from './multiselect_list';
|
||||
import type {Props as MultiSelectProps} from './multiselect_list';
|
||||
|
||||
const element = () => <div/>;
|
||||
|
||||
describe('components/multiselect/multiselect', () => {
|
||||
const optionsNumber = 8;
|
||||
const users = [];
|
||||
const users: Value[] = [];
|
||||
for (let i = 0; i < optionsNumber; i++) {
|
||||
users.push({id: `${i}`, label: `${i}`, value: `${i}`});
|
||||
}
|
||||
|
|
@ -28,11 +27,11 @@ describe('components/multiselect/multiselect', () => {
|
|||
} as any;
|
||||
|
||||
const baseProps = {
|
||||
ariaLabelRenderer: element as any,
|
||||
ariaLabelRenderer: (() => <div/>) as any,
|
||||
loading: false,
|
||||
onAdd: jest.fn(),
|
||||
onSelect: jest.fn(),
|
||||
optionRenderer: element,
|
||||
optionRenderer: (() => <div/>) as any,
|
||||
selectedItemRef,
|
||||
options: users,
|
||||
};
|
||||
|
|
@ -42,7 +41,6 @@ describe('components/multiselect/multiselect', () => {
|
|||
return (
|
||||
<p
|
||||
key={option.id}
|
||||
ref={isSelected ? selectedItemRef : option.id}
|
||||
onClick={() => onAdd(option)}
|
||||
onMouseMove={() => onMouseMove(option)}
|
||||
>
|
||||
|
|
@ -51,23 +49,22 @@ describe('components/multiselect/multiselect', () => {
|
|||
);
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
renderWithContext(
|
||||
<MultiSelectList
|
||||
{...baseProps}
|
||||
optionRenderer={renderOption}
|
||||
/>,
|
||||
);
|
||||
|
||||
(wrapper.instance() as any).listRef = {
|
||||
current: {
|
||||
getBoundingClientRect: jest.fn(() => ({
|
||||
bottom: 50,
|
||||
top: 50,
|
||||
})),
|
||||
},
|
||||
} as any;
|
||||
const listEl = document.getElementById('multiSelectList')!;
|
||||
jest.spyOn(listEl, 'getBoundingClientRect').mockReturnValue({
|
||||
bottom: 50,
|
||||
top: 50,
|
||||
} as DOMRect);
|
||||
|
||||
// fireEvent on document used because userEvent.keyboard requires element focus
|
||||
fireEvent.keyDown(document, {key: 'ArrowDown'});
|
||||
|
||||
wrapper.setState({selected: 1});
|
||||
expect(selectedItemRef.current.scrollIntoView).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
|
|
@ -76,7 +73,6 @@ describe('components/multiselect/multiselect', () => {
|
|||
return (
|
||||
<p
|
||||
key={option.id}
|
||||
ref={isSelected ? selectedItemRef : option.id}
|
||||
onClick={() => onAdd(option)}
|
||||
onMouseMove={() => onMouseMove(option)}
|
||||
>
|
||||
|
|
@ -85,23 +81,22 @@ describe('components/multiselect/multiselect', () => {
|
|||
);
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
renderWithContext(
|
||||
<MultiSelectList
|
||||
{...baseProps}
|
||||
optionRenderer={renderOption}
|
||||
/>,
|
||||
);
|
||||
|
||||
(wrapper.instance() as any).listRef = {
|
||||
current: {
|
||||
getBoundingClientRect: jest.fn(() => ({
|
||||
bottom: 200,
|
||||
top: 60,
|
||||
})),
|
||||
},
|
||||
} as any;
|
||||
const listEl = document.getElementById('multiSelectList')!;
|
||||
jest.spyOn(listEl, 'getBoundingClientRect').mockReturnValue({
|
||||
bottom: 200,
|
||||
top: 60,
|
||||
} as DOMRect);
|
||||
|
||||
// fireEvent on document used because userEvent.keyboard requires element focus
|
||||
fireEvent.keyDown(document, {key: 'ArrowDown'});
|
||||
|
||||
wrapper.setState({selected: 1});
|
||||
expect(selectedItemRef.current.scrollIntoView).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,89 +1,81 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/onboarding_tasklist/onboarding_tasklist_completed.tsx should match snapshot 1`] = `
|
||||
<Fragment>
|
||||
<CSSTransition
|
||||
classNames="fade"
|
||||
in={true}
|
||||
timeout={150}
|
||||
<div>
|
||||
<div
|
||||
class="CompletedWrapper-byXeYZ caqIuy"
|
||||
>
|
||||
<CompletedWrapper>
|
||||
<img
|
||||
alt="completed tasks image"
|
||||
src=""
|
||||
/>
|
||||
<h2>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Well done. You’ve completed all of the tasks!"
|
||||
id="onboardingTask.checklist.completed_title"
|
||||
/>
|
||||
</h2>
|
||||
<span
|
||||
className="completed-subtitle"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="We hope Mattermost is more familiar now."
|
||||
id="onboardingTask.checklist.completed_subtitle"
|
||||
/>
|
||||
<img
|
||||
alt="completed tasks image"
|
||||
src=""
|
||||
/>
|
||||
<h2>
|
||||
Well done. You’ve completed all of the tasks!
|
||||
</h2>
|
||||
<span
|
||||
class="completed-subtitle"
|
||||
>
|
||||
We hope Mattermost is more familiar now.
|
||||
</span>
|
||||
<span
|
||||
class="start-trial-text"
|
||||
>
|
||||
Interested in our higher-security features?
|
||||
|
||||
<br />
|
||||
Start your free Enterprise trial now!
|
||||
</span>
|
||||
<a
|
||||
class="btn btn-secondary"
|
||||
id="start_trial_btn"
|
||||
>
|
||||
Start trial
|
||||
</a>
|
||||
<button
|
||||
class="no-thanks-link style-link"
|
||||
>
|
||||
No, thanks
|
||||
</button>
|
||||
<div
|
||||
class="download-apps"
|
||||
>
|
||||
<span>
|
||||
Now that you’re all set up,
|
||||
<a
|
||||
href="https://mattermost.com/download?utm_source=mattermost&utm_medium=in-product&utm_content=onboarding_tasklist_completed&uid=&sid=&edition=team&server_version=#desktop"
|
||||
location="onboarding_tasklist_completed"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
download our apps.
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="start-trial-text"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Interested in our higher-security features?"
|
||||
id="onboardingTask.checklist.higher_security_features"
|
||||
/>
|
||||
|
||||
<br />
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start your free Enterprise trial now!"
|
||||
id="onboardingTask.checklist.start_enterprise_now"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="disclaimer"
|
||||
>
|
||||
<span>
|
||||
By clicking “Start trial”, I agree to the
|
||||
<a
|
||||
href="https://mattermost.com/pl/software-and-services-license-agreement?utm_source=mattermost&utm_medium=in-product&utm_content=onboarding_tasklist_completed&uid=&sid=&edition=team&server_version="
|
||||
location="onboarding_tasklist_completed"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Mattermost Software Evaluation Agreement
|
||||
</a>
|
||||
,
|
||||
<a
|
||||
href="https://mattermost.com/pl/privacy-policy/?utm_source=mattermost&utm_medium=in-product&utm_content=onboarding_tasklist_completed&uid=&sid=&edition=team&server_version="
|
||||
location="onboarding_tasklist_completed"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
privacy policy
|
||||
</a>
|
||||
and receiving product emails.
|
||||
</span>
|
||||
<StartTrialBtn
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
<button
|
||||
className="no-thanks-link style-link"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="No, thanks"
|
||||
id="onboardingTask.checklist.no_thanks"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="download-apps"
|
||||
>
|
||||
<span>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Now that you’re all set up, <link>download our apps.</link>"
|
||||
id="onboardingTask.checklist.downloads"
|
||||
values={
|
||||
Object {
|
||||
"link": [Function],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="disclaimer"
|
||||
>
|
||||
<span>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="By clicking “Start trial”, I agree to the <linkEvaluation>Mattermost Software Evaluation Agreement</linkEvaluation>, <linkPrivacy>privacy policy</linkPrivacy> and receiving product emails."
|
||||
id="onboardingTask.checklist.disclaimer"
|
||||
values={
|
||||
Object {
|
||||
"linkEvaluation": [Function],
|
||||
"linkPrivacy": [Function],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</CompletedWrapper>
|
||||
</CSSTransition>
|
||||
</Fragment>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import Completed from './onboarding_tasklist_completed';
|
||||
|
||||
let mockState: any;
|
||||
const mockDispatch = jest.fn();
|
||||
const dismissMockFn = jest.fn();
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux') as typeof import('react-redux'),
|
||||
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
|
||||
useDispatch: () => mockDispatch,
|
||||
jest.mock('mattermost-redux/actions/admin', () => ({
|
||||
...jest.requireActual('mattermost-redux/actions/admin'),
|
||||
getPrevTrialLicense: () => ({type: 'MOCK_GET_PREV_TRIAL_LICENSE'}),
|
||||
}));
|
||||
|
||||
jest.mock('components/common/hooks/useOpenStartTrialFormModal', () => ({
|
||||
__esModule: true,
|
||||
default: () => jest.fn(),
|
||||
}));
|
||||
|
||||
const dismissMockFn = jest.fn();
|
||||
|
||||
describe('components/onboarding_tasklist/onboarding_tasklist_completed.tsx', () => {
|
||||
const props = {
|
||||
dismissAction: dismissMockFn,
|
||||
|
|
@ -23,51 +26,45 @@ describe('components/onboarding_tasklist/onboarding_tasklist_completed.tsx', ()
|
|||
isFirstAdmin: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockState = {
|
||||
entities: {
|
||||
admin: {
|
||||
prevTrialLicense: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
subscription: {
|
||||
product_id: 'prod_professional',
|
||||
is_free_trial: 'false',
|
||||
trial_end_at: 1,
|
||||
},
|
||||
const initialState = {
|
||||
entities: {
|
||||
admin: {
|
||||
prevTrialLicense: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
subscription: {
|
||||
product_id: 'prod_professional',
|
||||
is_free_trial: 'false',
|
||||
trial_end_at: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(<Completed {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<Completed {...props}/>, initialState);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('finds the completed subtitle', () => {
|
||||
const wrapper = shallow(<Completed {...props}/>);
|
||||
expect(wrapper.find('.completed-subtitle')).toHaveLength(1);
|
||||
const {container} = renderWithContext(<Completed {...props}/>, initialState);
|
||||
expect(container.querySelectorAll('.completed-subtitle')).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('displays the no thanks option to close the onboarding list', () => {
|
||||
const wrapper = shallow(<Completed {...props}/>);
|
||||
const noThanksLink = wrapper.find('.no-thanks-link');
|
||||
test('displays the no thanks option to close the onboarding list', async () => {
|
||||
const {container} = renderWithContext(<Completed {...props}/>, initialState);
|
||||
const noThanksLink = container.querySelectorAll('.no-thanks-link');
|
||||
expect(noThanksLink).toHaveLength(1);
|
||||
|
||||
// calls the dissmiss function on click
|
||||
noThanksLink.simulate('click');
|
||||
await userEvent.click(noThanksLink[0]);
|
||||
expect(dismissMockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import configureStore from 'store';
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
|
||||
import {useTasksList} from './onboarding_tasks_manager';
|
||||
|
||||
|
|
@ -49,49 +47,44 @@ describe('onboarding tasks manager', () => {
|
|||
};
|
||||
|
||||
it('Places all the elements (6 ignoring plugins) when user is first admin or admin', () => {
|
||||
const store = configureStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<WrapperComponent/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<WrapperComponent/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper.find('li')).toHaveLength(6);
|
||||
expect(screen.getAllByRole('listitem')).toHaveLength(6);
|
||||
|
||||
// find the visit system console and start_trial
|
||||
expect(wrapper.findWhere((node) => node.key() === 'visit_system_console')).toHaveLength(1);
|
||||
expect(wrapper.findWhere((node) => node.key() === 'start_trial')).toHaveLength(1);
|
||||
expect(screen.getByText('visit_system_console')).toBeInTheDocument();
|
||||
expect(screen.getByText('start_trial')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Removes start_trial and visit_system_console when user is end user', () => {
|
||||
const endUserState = {...initialState, entities: {...initialState.entities, users: {...initialState.entities.users, currentUserId: user2}}};
|
||||
const store = configureStore(endUserState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<WrapperComponent/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<WrapperComponent/>,
|
||||
endUserState,
|
||||
);
|
||||
expect(wrapper.find('li')).toHaveLength(4);
|
||||
|
||||
expect(screen.getAllByRole('listitem')).toHaveLength(4);
|
||||
|
||||
// verify visit_system_console and start_trial were removed
|
||||
expect(wrapper.findWhere((node) => node.key() === 'visit_system_console')).toHaveLength(0);
|
||||
expect(wrapper.findWhere((node) => node.key() === 'start_trial')).toHaveLength(0);
|
||||
expect(screen.queryByText('visit_system_console')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('start_trial')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Removes invite people task item when user is GUEST user', () => {
|
||||
const endUserState = {...initialState, entities: {...initialState.entities, users: {...initialState.entities.users, currentUserId: user3}}};
|
||||
const store = configureStore(endUserState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<WrapperComponent/>
|
||||
</Provider>,
|
||||
renderWithContext(
|
||||
<WrapperComponent/>,
|
||||
endUserState,
|
||||
);
|
||||
expect(wrapper.find('li')).toHaveLength(3);
|
||||
|
||||
expect(screen.getAllByRole('listitem')).toHaveLength(3);
|
||||
|
||||
// verify visit_system_console and start_trial were removed
|
||||
expect(wrapper.findWhere((node) => node.key() === 'invite_people')).toHaveLength(0);
|
||||
expect(screen.queryByText('invite_people')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,313 +1,151 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`PostAttachmentOpenGraph should match snapshot 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Connect(PostAttachmentOpenGraph)
|
||||
actions={
|
||||
Object {
|
||||
"editPost": [MockFunction],
|
||||
}
|
||||
}
|
||||
currentUserId="1234"
|
||||
link="http://mattermost.com"
|
||||
openGraphData={
|
||||
Object {
|
||||
"description": "description",
|
||||
"images": Array [
|
||||
Object {
|
||||
"secure_url": "",
|
||||
"url": "http://mattermost.com/OpenGraphImage.jpg",
|
||||
},
|
||||
],
|
||||
"site_name": "Mattermost",
|
||||
"title": "Mattermost Private Cloud Messaging",
|
||||
}
|
||||
}
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "https://mattermost.com",
|
||||
"metadata": Object {
|
||||
"images": Object {
|
||||
"http://mattermost.com/OpenGraphImage.jpg": Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 100,
|
||||
"width": 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
postId=""
|
||||
toggleEmbedVisibility={[MockFunction]}
|
||||
<div>
|
||||
<a
|
||||
class="PostAttachmentOpenGraph"
|
||||
href="https://www.mattermost.com/?utm_source=mattermost&utm_medium=in-product&utm_content=post_attachment_opengraph&uid=user-1&sid=&edition=team&server_version="
|
||||
location="post_attachment_opengraph"
|
||||
rel="noopener noreferrer"
|
||||
role="link"
|
||||
target="_blank"
|
||||
title="Mattermost | Open Source Collaboration for Developers"
|
||||
>
|
||||
<PostAttachmentOpenGraph
|
||||
actions={
|
||||
Object {
|
||||
"editPost": [Function],
|
||||
}
|
||||
}
|
||||
currentUserId="user-1"
|
||||
enableLinkPreviews={true}
|
||||
imageCollapsed={false}
|
||||
link="http://mattermost.com"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "https://mattermost.com",
|
||||
"metadata": Object {
|
||||
"images": Object {
|
||||
"http://mattermost.com/OpenGraphImage.jpg": Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 100,
|
||||
"width": 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
postId=""
|
||||
previewEnabled={true}
|
||||
toggleEmbedVisibility={[MockFunction]}
|
||||
/>
|
||||
</Connect(PostAttachmentOpenGraph)>
|
||||
</Provider>
|
||||
<div
|
||||
class="PostAttachmentOpenGraph__body"
|
||||
>
|
||||
<span
|
||||
class="sitename"
|
||||
>
|
||||
Mattermost.com
|
||||
</span>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Mattermost | Open Source Collaboration for Developers
|
||||
</span>
|
||||
<span
|
||||
class="description"
|
||||
>
|
||||
Mattermost is a secure, open source platform for communication, collaboration, and workflow orchestration across tools and teams.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="PostAttachmentOpenGraph__image large"
|
||||
>
|
||||
<div
|
||||
class="AutoHeight"
|
||||
style="transition-property: height; transition-duration: 250ms; transition-timing-function: ease; width: 100%; height: auto; overflow: visible;"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
class="preview-toggle style--none"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="18"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7,10L12,15L17,10H7Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<figure>
|
||||
<img
|
||||
alt="Mattermost | Open Source Collaboration for Developers"
|
||||
src="/api/v4/image?url=http%3A%2F%2Flocalhost%3A8065%2Fapi%2Fv4%2Fimage%3Furl%3Dhttp%253A%252F%252Fmattermo%E2%80%A6t.com%252Fwp-content%252Fuploads%252F2021%252F09%252FHomepage%25402x.png"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostAttachmentOpenGraphBody should match snapshot 1`] = `
|
||||
<Memo()
|
||||
description="test-description"
|
||||
isInPermalink={false}
|
||||
sitename="test-sitename"
|
||||
title="test-title"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="PostAttachmentOpenGraph__body"
|
||||
class="PostAttachmentOpenGraph__body"
|
||||
>
|
||||
<span
|
||||
className="sitename"
|
||||
class="sitename"
|
||||
>
|
||||
test-sitename
|
||||
</span>
|
||||
<span
|
||||
className="title"
|
||||
class="title"
|
||||
>
|
||||
test-title
|
||||
</span>
|
||||
<span
|
||||
className="description"
|
||||
class="description"
|
||||
>
|
||||
test-description
|
||||
</span>
|
||||
</div>
|
||||
</Memo()>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostAttachmentOpenGraphBody should match snapshot 2`] = `
|
||||
<Memo()
|
||||
description="test_description"
|
||||
isInPermalink={false}
|
||||
siteName="test_sitename"
|
||||
title="test_title"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="PostAttachmentOpenGraph__body"
|
||||
class="PostAttachmentOpenGraph__body"
|
||||
>
|
||||
<span
|
||||
className="title"
|
||||
class="title"
|
||||
>
|
||||
test_title
|
||||
</span>
|
||||
<span
|
||||
className="description"
|
||||
class="description"
|
||||
>
|
||||
test_description
|
||||
</span>
|
||||
</div>
|
||||
</Memo()>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Memo()
|
||||
imageMetadata={
|
||||
Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 1256,
|
||||
"secure_url": "http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png",
|
||||
"type": "image/png",
|
||||
"url": "",
|
||||
"width": 2400,
|
||||
}
|
||||
}
|
||||
isEmbedVisible={true}
|
||||
isInPermalink={false}
|
||||
title="test_image"
|
||||
toggleEmbedVisibility={[MockFunction]}
|
||||
<div>
|
||||
<div
|
||||
class="PostAttachmentOpenGraph__image large"
|
||||
>
|
||||
<div
|
||||
className="PostAttachmentOpenGraph__image large"
|
||||
class="AutoHeight"
|
||||
style="transition-property: height; transition-duration: 250ms; transition-timing-function: ease; width: 100%; height: auto; overflow: visible;"
|
||||
>
|
||||
<AutoHeightSwitcher
|
||||
showSlot={1}
|
||||
slot1={
|
||||
<Memo(Connect(Component))
|
||||
imageMetadata={
|
||||
Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 1256,
|
||||
"secure_url": "http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png",
|
||||
"type": "image/png",
|
||||
"url": "",
|
||||
"width": 2400,
|
||||
}
|
||||
}
|
||||
src="http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png"
|
||||
>
|
||||
[Function]
|
||||
</Memo(Connect(Component))>
|
||||
}
|
||||
slot2={
|
||||
<button
|
||||
className="preview-toggle style--none"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<MenuDownIcon
|
||||
color="currentColor"
|
||||
size={18}
|
||||
/>
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<Transition
|
||||
appear={false}
|
||||
enter={true}
|
||||
exit={true}
|
||||
in={false}
|
||||
mountOnEnter={false}
|
||||
onEnter={[Function]}
|
||||
onEntered={[Function]}
|
||||
onEntering={[Function]}
|
||||
onExit={[Function]}
|
||||
onExited={[Function]}
|
||||
onExiting={[Function]}
|
||||
timeout={250}
|
||||
unmountOnExit={false}
|
||||
<div>
|
||||
<button
|
||||
class="preview-toggle style--none"
|
||||
>
|
||||
<div
|
||||
className="AutoHeight"
|
||||
style={
|
||||
Object {
|
||||
"height": "auto",
|
||||
"overflow": "visible",
|
||||
"transitionDuration": "250ms",
|
||||
"transitionProperty": "height",
|
||||
"transitionTimingFunction": "ease",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="18"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<div>
|
||||
<Connect(Component)
|
||||
imageMetadata={
|
||||
Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 1256,
|
||||
"secure_url": "http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png",
|
||||
"type": "image/png",
|
||||
"url": "",
|
||||
"width": 2400,
|
||||
}
|
||||
}
|
||||
src="http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png"
|
||||
>
|
||||
<Memo(ExternalImage)
|
||||
dispatch={[Function]}
|
||||
enableSVGs={true}
|
||||
hasImageProxy={true}
|
||||
imageMetadata={
|
||||
Object {
|
||||
"format": "png",
|
||||
"frameCount": 0,
|
||||
"height": 1256,
|
||||
"secure_url": "http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png",
|
||||
"type": "image/png",
|
||||
"url": "",
|
||||
"width": 2400,
|
||||
}
|
||||
}
|
||||
src="http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png"
|
||||
>
|
||||
<button
|
||||
className="preview-toggle style--none"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<MenuDownIcon
|
||||
color="currentColor"
|
||||
size={18}
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height={18}
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
width={18}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7,10L12,15L17,10H7Z"
|
||||
/>
|
||||
</svg>
|
||||
</MenuDownIcon>
|
||||
</button>
|
||||
<figure>
|
||||
<img
|
||||
alt="test_image"
|
||||
src="/api/v4/image?url=http%3A%2F%2Flocalhost%3A8065%2Fapi%2Fv4%2Fimage%3Furl%3Dhttp%253A%252F%252Fmattermo%E2%80%A6t.com%252Fwp-content%252Fuploads%252F2021%252F09%252FHomepage%25402x.png"
|
||||
/>
|
||||
</figure>
|
||||
</Memo(ExternalImage)>
|
||||
</Connect(Component)>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</AutoHeightSwitcher>
|
||||
<path
|
||||
d="M7,10L12,15L17,10H7Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<figure>
|
||||
<img
|
||||
alt="test_image"
|
||||
src="/api/v4/image?url=http%3A%2F%2Flocalhost%3A8065%2Fapi%2Fv4%2Fimage%3Furl%3Dhttp%253A%252F%252Fmattermo%E2%80%A6t.com%252Fwp-content%252Fuploads%252F2021%252F09%252FHomepage%25402x.png"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</Memo()>
|
||||
</Provider>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostAttachmentOpenGraphImage should not render when used in Permalink 1`] = `Object {}`;
|
||||
exports[`PostAttachmentOpenGraphImage should not render when used in Permalink 1`] = `<div />`;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import set from 'lodash/set';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import type {OpenGraphMetadata, Post} from '@mattermost/types/posts';
|
||||
|
||||
import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import {render, renderWithContext} from 'tests/react_testing_utils';
|
||||
import {Preferences} from 'utils/constants';
|
||||
|
||||
import {getBestImage, getIsLargeImage, PostAttachmentOpenGraphImage, PostAttachmentOpenGraphBody} from './post_attachment_opengraph';
|
||||
|
|
@ -91,18 +87,9 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
|
||||
const baseProps = {
|
||||
post,
|
||||
postId: '',
|
||||
link: 'http://mattermost.com',
|
||||
postId: 'post_id_1',
|
||||
link: openGraphData.url,
|
||||
currentUserId: '1234',
|
||||
openGraphData: {
|
||||
description: 'description',
|
||||
images: [{
|
||||
secure_url: '',
|
||||
url: imageUrl,
|
||||
}],
|
||||
site_name: 'Mattermost',
|
||||
title: 'Mattermost Private Cloud Messaging',
|
||||
},
|
||||
toggleEmbedVisibility,
|
||||
actions: {
|
||||
editPost: jest.fn(),
|
||||
|
|
@ -110,60 +97,72 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraph {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraph {...baseProps}/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render nothing without any data', () => {
|
||||
const state = cloneDeep(initialState);
|
||||
set(state, 'entities.posts.openGraph', {});
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraph {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraph {...baseProps}/>,
|
||||
{
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
posts: {
|
||||
...initialState.entities.posts,
|
||||
openGraph: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(wrapper).toEqual({});
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
test('should render nothing when link previews are disabled on the server', () => {
|
||||
const state = cloneDeep(initialState);
|
||||
set(state, 'entities.config.EnableLinkPreviews', 'false');
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraph {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraph {...baseProps}/>,
|
||||
{
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState.entities.general,
|
||||
config: {
|
||||
...initialState.entities.general.config,
|
||||
EnableLinkPreviews: 'false',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(wrapper).toEqual({});
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
test('should render nothing when link previews are disabled by the user', () => {
|
||||
const state = cloneDeep(initialState);
|
||||
set(state, `entities.preferences.EnableLinkPreviews["${preferenceKeys.LINK_PREVIEW_DISPLAY}"]`, 'false');
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraph {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraph {...baseProps}/>,
|
||||
{
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
preferences: {
|
||||
...initialState.entities.preferences,
|
||||
myPreferences: {
|
||||
...initialState.entities.preferences.myPreferences,
|
||||
[preferenceKeys.LINK_PREVIEW_DISPLAY]: {value: 'false'},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(wrapper).toEqual({});
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -176,62 +175,72 @@ describe('PostAttachmentOpenGraphBody', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...baseProps}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...baseProps}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not render without title', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'title', '');
|
||||
const props = {
|
||||
...baseProps,
|
||||
title: '',
|
||||
};
|
||||
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
|
||||
expect(wrapper).toEqual({});
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
test('should add extra class for permalink view', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'isInPermalink', true);
|
||||
const props = {
|
||||
...baseProps,
|
||||
isInPermalink: true,
|
||||
};
|
||||
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
|
||||
expect(wrapper.find('.isInPermalink').exists()).toBe(true);
|
||||
expect(wrapper.find('.sitename').exists()).toBe(false);
|
||||
expect(container.querySelector('.isInPermalink')).toBeInTheDocument();
|
||||
expect(container.querySelector('.sitename')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render without sitename', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'sitename', '');
|
||||
const props = {
|
||||
...baseProps,
|
||||
sitename: '',
|
||||
};
|
||||
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
|
||||
expect(wrapper.find('.sitename').exists()).toBe(false);
|
||||
expect(wrapper.find('.title').exists()).toBe(true);
|
||||
expect(wrapper.find('.description').exists()).toBe(true);
|
||||
expect(container.querySelector('.sitename')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.title')).toBeInTheDocument();
|
||||
expect(container.querySelector('.description')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render without description', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'description', '');
|
||||
const props = {
|
||||
...baseProps,
|
||||
description: '',
|
||||
};
|
||||
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
|
||||
expect(wrapper.find('.sitename').exists()).toBe(true);
|
||||
expect(wrapper.find('.title').exists()).toBe(true);
|
||||
expect(wrapper.find('.description').exists()).toBe(false);
|
||||
expect(container.querySelector('.sitename')).toBeInTheDocument();
|
||||
expect(container.querySelector('.title')).toBeInTheDocument();
|
||||
expect(container.querySelector('.description')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render with title only', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'sitename', '');
|
||||
set(props, 'description', '');
|
||||
const props = {
|
||||
...baseProps,
|
||||
sitename: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
const wrapper = mount(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
const {container} = render(<PostAttachmentOpenGraphBody {...props}/>);
|
||||
|
||||
expect(wrapper.find('.sitename').exists()).toBe(false);
|
||||
expect(wrapper.find('.title').exists()).toBe(true);
|
||||
expect(wrapper.find('.description').exists()).toBe(false);
|
||||
expect(container.querySelector('.sitename')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.title')).toBeInTheDocument();
|
||||
expect(container.querySelector('.description')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -245,62 +254,57 @@ describe('PostAttachmentOpenGraphImage', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraphImage {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraphImage {...baseProps}/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not render when used in Permalink', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'isInPermalink', true);
|
||||
const props = {
|
||||
...baseProps,
|
||||
isInPermalink: true,
|
||||
};
|
||||
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraphImage {...props}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraphImage {...props}/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot({});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render a large image with toggle', () => {
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraphImage {...baseProps}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraphImage {...baseProps}/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image').exists()).toBe(true);
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image.large').exists()).toBe(true);
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image .preview-toggle').exists()).toBe(true);
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image')).toBeInTheDocument();
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image.large')).toBeInTheDocument();
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image .preview-toggle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a small image without toggle', () => {
|
||||
const props = cloneDeep(baseProps);
|
||||
set(props, 'imageMetadata.height', 90);
|
||||
set(props, 'imageMetadata.width', 120);
|
||||
const props = {
|
||||
...baseProps,
|
||||
imageMetadata: {
|
||||
...baseProps.imageMetadata!,
|
||||
height: 90,
|
||||
width: 120,
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(initialState);
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<PostAttachmentOpenGraphImage {...props}/>
|
||||
</Provider>,
|
||||
const {container} = renderWithContext(
|
||||
<PostAttachmentOpenGraphImage {...props}/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image').exists()).toBe(true);
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image.large').exists()).toBe(false);
|
||||
expect(wrapper.find('.PostAttachmentOpenGraph__image .preview-toggle').exists()).toBe(false);
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image')).toBeInTheDocument();
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image.large')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.PostAttachmentOpenGraph__image .preview-toggle')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -313,24 +317,26 @@ describe('PostAttachmentOpenGraphBody', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = mount(
|
||||
const {container} = render(
|
||||
<PostAttachmentOpenGraphBody {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('permalink preview', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isInPermalink: true,
|
||||
};
|
||||
test('should add extra class for permalink view', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isInPermalink: true,
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<PostAttachmentOpenGraphBody {...props}/>,
|
||||
);
|
||||
const {container} = render(
|
||||
<PostAttachmentOpenGraphBody {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.isInPermalink').exists()).toBe(true);
|
||||
expect(container.querySelector('.isInPermalink')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,372 +2,202 @@
|
|||
|
||||
exports[`PostBodyAdditionalContent with a YouTube video should not render content when isEmbedVisible is false 1`] = `
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded={false}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded="false"
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a YouTube video should render correctly 1`] = `
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded={true}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Connect(YoutubeVideo)
|
||||
link="https://www.youtube.com/watch?v=d-YO3v-wJts"
|
||||
postId="post_id_1"
|
||||
show={true}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded="true"
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="youtube-video"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a YouTube video should render the toggle after a message containing more than just a link 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link "
|
||||
data-expanded={true}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Connect(YoutubeVideo)
|
||||
link="https://www.youtube.com/watch?v=d-YO3v-wJts"
|
||||
postId="post_id_1"
|
||||
show={true}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link "
|
||||
data-expanded="true"
|
||||
/>
|
||||
<div
|
||||
data-testid="youtube-video"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a message attachment should render correctly 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Memo(MessageAttachmentList)
|
||||
attachments={Array []}
|
||||
postId="post_id_1"
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="message-attachment-list"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a normal link Should render nothing if the plugin matches but isEmbedVisible is false 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a normal link Should render nothing if the registered plugins don't match 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a normal link Should render the plugin component if it matches and is not toggeable 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<EmbedMP3
|
||||
embed={
|
||||
Object {
|
||||
"type": "link",
|
||||
"url": "https://example.com/song.mp3",
|
||||
}
|
||||
}
|
||||
webSocketClient={
|
||||
WebSocketClient {
|
||||
"closeCallback": null,
|
||||
"closeListeners": Set {},
|
||||
"config": Object {
|
||||
"clientPingInterval": 30000,
|
||||
"maxWebSocketFails": 7,
|
||||
"maxWebSocketRetryTime": 300000,
|
||||
"minWebSocketRetryTime": 3000,
|
||||
"newWebSocketFn": [Function],
|
||||
"reconnectJitterRange": 2000,
|
||||
},
|
||||
"conn": null,
|
||||
"connectFailCount": 0,
|
||||
"connectionId": "",
|
||||
"errorCallback": null,
|
||||
"errorListeners": Set {},
|
||||
"eventCallback": null,
|
||||
"firstConnectCallback": null,
|
||||
"firstConnectListeners": Set {},
|
||||
"lastErrCode": null,
|
||||
"messageListeners": Set {},
|
||||
"missedEventCallback": null,
|
||||
"missedMessageListeners": Set {},
|
||||
"offlineHandler": null,
|
||||
"onlineHandler": null,
|
||||
"pingInterval": null,
|
||||
"postedAck": false,
|
||||
"reconnectCallback": null,
|
||||
"reconnectListeners": Set {},
|
||||
"reconnectTimeout": null,
|
||||
"responseCallbacks": Object {},
|
||||
"responseSequence": 1,
|
||||
"serverHostname": "",
|
||||
"serverSequence": 0,
|
||||
"waitingForPong": false,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="embed-mp3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a normal link Should render the plugin component if it matches and is toggeable 1`] = `
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded={true}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<EmbedMP3
|
||||
embed={
|
||||
Object {
|
||||
"type": "link",
|
||||
"url": "https://example.com/song.mp3",
|
||||
}
|
||||
}
|
||||
webSocketClient={
|
||||
WebSocketClient {
|
||||
"closeCallback": null,
|
||||
"closeListeners": Set {},
|
||||
"config": Object {
|
||||
"clientPingInterval": 30000,
|
||||
"maxWebSocketFails": 7,
|
||||
"maxWebSocketRetryTime": 300000,
|
||||
"minWebSocketRetryTime": 3000,
|
||||
"newWebSocketFn": [Function],
|
||||
"reconnectJitterRange": 2000,
|
||||
},
|
||||
"conn": null,
|
||||
"connectFailCount": 0,
|
||||
"connectionId": "",
|
||||
"errorCallback": null,
|
||||
"errorListeners": Set {},
|
||||
"eventCallback": null,
|
||||
"firstConnectCallback": null,
|
||||
"firstConnectListeners": Set {},
|
||||
"lastErrCode": null,
|
||||
"messageListeners": Set {},
|
||||
"missedEventCallback": null,
|
||||
"missedMessageListeners": Set {},
|
||||
"offlineHandler": null,
|
||||
"onlineHandler": null,
|
||||
"pingInterval": null,
|
||||
"postedAck": false,
|
||||
"reconnectCallback": null,
|
||||
"reconnectListeners": Set {},
|
||||
"reconnectTimeout": null,
|
||||
"responseCallbacks": Object {},
|
||||
"responseSequence": 1,
|
||||
"serverHostname": "",
|
||||
"serverSequence": 0,
|
||||
"waitingForPong": false,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded="true"
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="embed-mp3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a permalinklink Render permalink preview 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Connect(PostMessagePreview)
|
||||
handleFileDropdownOpened={[MockFunction]}
|
||||
metadata={
|
||||
Object {
|
||||
"channel_display_name": "channel1",
|
||||
"channel_id": "channel_id",
|
||||
"channel_type": "O",
|
||||
"post_id": "post_id123",
|
||||
"team_name": "core",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="post-message-preview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with a permalinklink Render permalink preview with no data 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with an image preview should render correctly 1`] = `
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded={true}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Connect(PostImage)
|
||||
imageMetadata={Object {}}
|
||||
link="https://example.com/image.png"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "https://example.com/image.png",
|
||||
"metadata": Object {
|
||||
"embeds": Array [
|
||||
Object {
|
||||
"type": "image",
|
||||
"url": "https://example.com/image.png",
|
||||
},
|
||||
],
|
||||
"emojis": Array [],
|
||||
"files": Array [],
|
||||
"images": Object {
|
||||
"https://example.com/image.png": Object {},
|
||||
},
|
||||
"reactions": Array [],
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link pull-left"
|
||||
data-expanded="true"
|
||||
/>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-image-metadata="present"
|
||||
data-testid="post-image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with an image preview should render the toggle after a message containing more than just a link 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
className="style--none post__embed-visibility color--link "
|
||||
data-expanded={true}
|
||||
key="toggle"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Connect(PostImage)
|
||||
imageMetadata={Object {}}
|
||||
link="https://example.com/image.png"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "This is an image: https://example.com/image.png",
|
||||
"metadata": Object {
|
||||
"embeds": Array [
|
||||
Object {
|
||||
"type": "image",
|
||||
"url": "https://example.com/image.png",
|
||||
},
|
||||
],
|
||||
"emojis": Array [],
|
||||
"files": Array [],
|
||||
"images": Object {
|
||||
"https://example.com/image.png": Object {},
|
||||
},
|
||||
"reactions": Array [],
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<button
|
||||
aria-label="Toggle Embed Visibility"
|
||||
class="style--none post__embed-visibility color--link "
|
||||
data-expanded="true"
|
||||
/>
|
||||
<div
|
||||
data-image-metadata="present"
|
||||
data-testid="post-image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with an opengraph preview should render correctly 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Connect(PostAttachmentOpenGraph)
|
||||
isEmbedVisible={true}
|
||||
link="https://example.com/image.png"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "https://example.com/image.png",
|
||||
"metadata": Object {
|
||||
"embeds": Array [
|
||||
Object {
|
||||
"type": "opengraph",
|
||||
"url": "https://example.com/image.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
postId="post_id_1"
|
||||
toggleEmbedVisibility={[Function]}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="post-attachment-opengraph"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PostBodyAdditionalContent with an opengraph preview should render the toggle after a message containing more than just a link 1`] = `
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<Connect(PostAttachmentOpenGraph)
|
||||
isEmbedVisible={true}
|
||||
link="https://example.com/image.png"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "channel_id",
|
||||
"create_at": 1,
|
||||
"id": "post_id_1",
|
||||
"message": "This is a link: https://example.com/image.png",
|
||||
"metadata": Object {
|
||||
"embeds": Array [
|
||||
Object {
|
||||
"type": "opengraph",
|
||||
"url": "https://example.com/image.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
"root_id": "root_id",
|
||||
}
|
||||
}
|
||||
postId="post_id_1"
|
||||
toggleEmbedVisibility={[Function]}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
some children
|
||||
</span>
|
||||
<div
|
||||
data-testid="post-attachment-opengraph"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import type {
|
||||
|
|
@ -13,10 +12,7 @@ import type {
|
|||
|
||||
import {getEmbedFromMetadata} from 'mattermost-redux/utils/post_utils';
|
||||
|
||||
import MessageAttachmentList from 'components/post_view/message_attachments/message_attachment_list';
|
||||
import PostAttachmentOpenGraph from 'components/post_view/post_attachment_opengraph';
|
||||
import PostImageComponent from 'components/post_view/post_image';
|
||||
import YoutubeVideo from 'components/youtube_video';
|
||||
import {render, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import PostBodyAdditionalContent from './post_body_additional_content';
|
||||
import type {Props} from './post_body_additional_content';
|
||||
|
|
@ -29,6 +25,52 @@ jest.mock('mattermost-redux/utils/post_utils', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('components/post_view/post_image', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn((props: any) => (
|
||||
<div
|
||||
data-testid='post-image'
|
||||
data-image-metadata={props.imageMetadata ? 'present' : 'absent'}
|
||||
/>
|
||||
)),
|
||||
}));
|
||||
|
||||
jest.mock('components/post_view/message_attachments/message_attachment_list', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => (
|
||||
<div data-testid='message-attachment-list'/>
|
||||
)),
|
||||
}));
|
||||
|
||||
jest.mock('components/post_view/post_attachment_opengraph', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => (
|
||||
<div data-testid='post-attachment-opengraph'/>
|
||||
)),
|
||||
}));
|
||||
|
||||
jest.mock('components/youtube_video', () => {
|
||||
const MockYoutubeVideo: any = jest.fn(() => (
|
||||
<div data-testid='youtube-video'/>
|
||||
));
|
||||
MockYoutubeVideo.isYoutubeLink = (link: string) => {
|
||||
return (/^https?:\/\/((www\.)?youtube\.com|youtu\.be)\/.+/).test(link);
|
||||
};
|
||||
return {__esModule: true, default: MockYoutubeVideo};
|
||||
});
|
||||
|
||||
jest.mock('components/post_view/post_message_preview', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => (
|
||||
<div data-testid='post-message-preview'/>
|
||||
)),
|
||||
}));
|
||||
|
||||
jest.mock('client/web_websocket_client', () => ({
|
||||
__esModule: true,
|
||||
default: {},
|
||||
}));
|
||||
|
||||
describe('PostBodyAdditionalContent', () => {
|
||||
const baseProps: Props = {
|
||||
children: <span>{'some children'}</span>,
|
||||
|
|
@ -73,11 +115,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
};
|
||||
|
||||
test('should render correctly', () => {
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...imageBaseProps}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...imageBaseProps}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find(PostImageComponent).exists()).toBe(true);
|
||||
expect(wrapper.find(PostImageComponent).prop('imageMetadata')).toBe(imageMetadata);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByTestId('post-image')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the toggle after a message containing more than just a link', () => {
|
||||
|
|
@ -89,9 +130,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not render content when isEmbedVisible is false', () => {
|
||||
|
|
@ -100,9 +141,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
isEmbedVisible: false,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper.find(PostImageComponent).exists()).toBe(false);
|
||||
expect(screen.queryByTestId('post-image')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -125,11 +166,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
};
|
||||
|
||||
test('should render correctly', () => {
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...messageAttachmentBaseProps}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...messageAttachmentBaseProps}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find(MessageAttachmentList).exists()).toBe(true);
|
||||
expect(wrapper.find(MessageAttachmentList).prop('attachments')).toBe(attachments);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByTestId('message-attachment-list')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render content when isEmbedVisible is false', () => {
|
||||
|
|
@ -138,9 +178,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
isEmbedVisible: false,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper.find(MessageAttachmentList).exists()).toBe(true);
|
||||
expect(screen.getByTestId('message-attachment-list')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -162,10 +202,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
};
|
||||
|
||||
test('should render correctly', () => {
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...ogBaseProps}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...ogBaseProps}/>);
|
||||
|
||||
expect(wrapper.find(PostAttachmentOpenGraph).exists()).toBe(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(screen.getByTestId('post-attachment-opengraph')).toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render the toggle after a message containing more than just a link', () => {
|
||||
|
|
@ -177,9 +217,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render content when isEmbedVisible is false', () => {
|
||||
|
|
@ -188,9 +228,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
isEmbedVisible: false,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper.find(PostAttachmentOpenGraph).exists()).toBe(true);
|
||||
expect(screen.getByTestId('post-attachment-opengraph')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -212,10 +252,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
};
|
||||
|
||||
test('should render correctly', () => {
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...youtubeBaseProps}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...youtubeBaseProps}/>);
|
||||
|
||||
expect(wrapper.find(YoutubeVideo).exists()).toBe(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(screen.getByTestId('youtube-video')).toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render the toggle after a message containing more than just a link', () => {
|
||||
|
|
@ -227,9 +267,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not render content when isEmbedVisible is false', () => {
|
||||
|
|
@ -238,17 +278,17 @@ describe('PostBodyAdditionalContent', () => {
|
|||
isEmbedVisible: false,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(wrapper.find(YoutubeVideo).exists()).toBe(false);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(screen.queryByTestId('youtube-video')).not.toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a normal link', () => {
|
||||
const mp3Url = 'https://example.com/song.mp3';
|
||||
|
||||
const EmbedMP3 = () => <></>;
|
||||
const EmbedMP3 = () => <div data-testid='embed-mp3'/>;
|
||||
|
||||
const linkBaseProps = {
|
||||
...baseProps,
|
||||
|
|
@ -278,9 +318,9 @@ describe('PostBodyAdditionalContent', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(wrapper.find(EmbedMP3).exists()).toBe(false);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(screen.queryByTestId('embed-mp3')).not.toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Should render the plugin component if it matches and is toggeable', () => {
|
||||
|
|
@ -297,10 +337,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(wrapper.find(EmbedMP3).exists()).toBe(true);
|
||||
expect(wrapper.find('button.post__embed-visibility').exists()).toBe(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(screen.getByTestId('embed-mp3')).toBeInTheDocument();
|
||||
expect(container.querySelector('button.post__embed-visibility')).toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Should render the plugin component if it matches and is not toggeable', () => {
|
||||
|
|
@ -317,10 +357,10 @@ describe('PostBodyAdditionalContent', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(wrapper.find(EmbedMP3).exists()).toBe(true);
|
||||
expect(wrapper.find('button.post__embed-visibility').exists()).toBe(false);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(screen.getByTestId('embed-mp3')).toBeInTheDocument();
|
||||
expect(container.querySelector('button.post__embed-visibility')).not.toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Should render nothing if the plugin matches but isEmbedVisible is false', () => {
|
||||
|
|
@ -338,16 +378,38 @@ describe('PostBodyAdditionalContent', () => {
|
|||
isEmbedVisible: false,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(wrapper.find(EmbedMP3).exists()).toBe(false);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(screen.queryByTestId('embed-mp3')).not.toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call toggleEmbedVisibility with post id', () => {
|
||||
const wrapper = shallow<PostBodyAdditionalContent>(<PostBodyAdditionalContent {...baseProps}/>);
|
||||
test('should call toggleEmbedVisibility with post id', async () => {
|
||||
const imageUrl = 'https://example.com/image.png';
|
||||
const props = {
|
||||
...baseProps,
|
||||
post: {
|
||||
...baseProps.post,
|
||||
message: imageUrl,
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'image',
|
||||
url: imageUrl,
|
||||
}],
|
||||
images: {
|
||||
[imageUrl]: {} as PostImage,
|
||||
},
|
||||
emojis: [],
|
||||
files: [],
|
||||
reactions: [],
|
||||
} as PostMetadata,
|
||||
},
|
||||
};
|
||||
|
||||
wrapper.instance().toggleEmbedVisibility();
|
||||
render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
const toggleButton = screen.getByRole('button', {name: 'Toggle Embed Visibility'});
|
||||
await userEvent.click(toggleButton);
|
||||
|
||||
expect(baseProps.actions.toggleEmbedVisibility).toHaveBeenCalledTimes(1);
|
||||
expect(baseProps.actions.toggleEmbedVisibility).toHaveBeenCalledWith('post_id_1');
|
||||
|
|
@ -367,8 +429,7 @@ describe('PostBodyAdditionalContent', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow<PostBodyAdditionalContent>(<PostBodyAdditionalContent {...props}/>);
|
||||
wrapper.instance().getEmbed();
|
||||
render(<PostBodyAdditionalContent {...props}/>);
|
||||
|
||||
expect(getEmbedFromMetadata).toHaveBeenCalledWith(metadata);
|
||||
});
|
||||
|
|
@ -402,8 +463,8 @@ describe('PostBodyAdditionalContent', () => {
|
|||
};
|
||||
|
||||
test('Render permalink preview', () => {
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...permalinkBaseProps}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...permalinkBaseProps}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Render permalink preview with no data', () => {
|
||||
|
|
@ -421,8 +482,8 @@ describe('PostBodyAdditionalContent', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = render(<PostBodyAdditionalContent {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,275 +1,195 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/post_view/Reaction should apply read-only class if user does not have permission to add reaction 1`] = `
|
||||
<Connect(ReactionTooltip)
|
||||
canAddReactions={false}
|
||||
canRemoveReactions={true}
|
||||
currentUserReacted={false}
|
||||
emojiName="smile"
|
||||
onShow={[Function]}
|
||||
reactions={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_2",
|
||||
},
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_3",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:"
|
||||
className="Reaction Reaction--unreacted Reaction--read-only"
|
||||
id="postReaction-post_id-smile"
|
||||
onClick={[Function]}
|
||||
<div>
|
||||
<div
|
||||
data-testid="reaction-tooltip"
|
||||
>
|
||||
<span
|
||||
className="d-flex align-items-center"
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:"
|
||||
class="Reaction Reaction--unreacted Reaction--read-only"
|
||||
id="postReaction-post_id-smile"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden={true}
|
||||
className="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__count"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__number"
|
||||
class="Reaction__count"
|
||||
>
|
||||
<span
|
||||
className="Reaction__number--display"
|
||||
class="Reaction__number"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--unreacted"
|
||||
onAnimationEnd={[Function]}
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--reacted"
|
||||
>
|
||||
3
|
||||
<span
|
||||
class="Reaction__number--display"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--unreacted"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--reacted"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Connect(ReactionTooltip)>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/Reaction should apply read-only class if user does not have permission to remove reaction 1`] = `
|
||||
<Connect(ReactionTooltip)
|
||||
canAddReactions={true}
|
||||
canRemoveReactions={false}
|
||||
currentUserReacted={true}
|
||||
emojiName="smile"
|
||||
onShow={[Function]}
|
||||
reactions={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_2",
|
||||
},
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_3",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:"
|
||||
className="Reaction Reaction--reacted Reaction--read-only"
|
||||
id="postReaction-post_id-smile"
|
||||
onClick={[Function]}
|
||||
<div>
|
||||
<div
|
||||
data-testid="reaction-tooltip"
|
||||
>
|
||||
<span
|
||||
className="d-flex align-items-center"
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:"
|
||||
class="Reaction Reaction--reacted Reaction--read-only"
|
||||
id="postReaction-post_id-smile"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden={true}
|
||||
className="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__count"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__number"
|
||||
class="Reaction__count"
|
||||
>
|
||||
<span
|
||||
className="Reaction__number--display"
|
||||
class="Reaction__number"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--unreacted"
|
||||
onAnimationEnd={[Function]}
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--reacted"
|
||||
>
|
||||
2
|
||||
<span
|
||||
class="Reaction__number--display"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--unreacted"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--reacted"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Connect(ReactionTooltip)>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/Reaction should match snapshot 1`] = `
|
||||
<Connect(ReactionTooltip)
|
||||
canAddReactions={true}
|
||||
canRemoveReactions={true}
|
||||
currentUserReacted={false}
|
||||
emojiName="smile"
|
||||
onShow={[Function]}
|
||||
reactions={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_2",
|
||||
},
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_3",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:. Click to add."
|
||||
className="Reaction Reaction--unreacted "
|
||||
id="postReaction-post_id-smile"
|
||||
onClick={[Function]}
|
||||
<div>
|
||||
<div
|
||||
data-testid="reaction-tooltip"
|
||||
>
|
||||
<span
|
||||
className="d-flex align-items-center"
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:. Click to add."
|
||||
class="Reaction Reaction--unreacted "
|
||||
id="postReaction-post_id-smile"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden={true}
|
||||
className="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__count"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__number"
|
||||
class="Reaction__count"
|
||||
>
|
||||
<span
|
||||
className="Reaction__number--display"
|
||||
class="Reaction__number"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--unreacted"
|
||||
onAnimationEnd={[Function]}
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--reacted"
|
||||
>
|
||||
3
|
||||
<span
|
||||
class="Reaction__number--display"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--unreacted"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--reacted"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Connect(ReactionTooltip)>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/Reaction should match snapshot when a current user reacted to a post 1`] = `
|
||||
<Connect(ReactionTooltip)
|
||||
canAddReactions={true}
|
||||
canRemoveReactions={true}
|
||||
currentUserReacted={true}
|
||||
emojiName="smile"
|
||||
onShow={[Function]}
|
||||
reactions={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":cry:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_1",
|
||||
},
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"emoji_name": ":smile:",
|
||||
"post_id": "post_id",
|
||||
"user_id": "user_id_3",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:. Click to remove."
|
||||
className="Reaction Reaction--reacted "
|
||||
id="postReaction-post_id-smile"
|
||||
onClick={[Function]}
|
||||
<div>
|
||||
<div
|
||||
data-testid="reaction-tooltip"
|
||||
>
|
||||
<span
|
||||
className="d-flex align-items-center"
|
||||
<button
|
||||
aria-label="2 users reacted with :smile:. Click to remove."
|
||||
class="Reaction Reaction--reacted "
|
||||
id="postReaction-post_id-smile"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden={true}
|
||||
className="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__count"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="Reaction__emoji emoticon"
|
||||
src="emoji_image_url"
|
||||
/>
|
||||
<span
|
||||
className="Reaction__number"
|
||||
class="Reaction__count"
|
||||
>
|
||||
<span
|
||||
className="Reaction__number--display"
|
||||
class="Reaction__number"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--unreacted"
|
||||
onAnimationEnd={[Function]}
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span
|
||||
className="Reaction__number--reacted"
|
||||
>
|
||||
2
|
||||
<span
|
||||
class="Reaction__number--display"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--unreacted"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span
|
||||
class="Reaction__number--reacted"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Connect(ReactionTooltip)>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/Reaction should return null/empty if no emojiImageUrl 1`] = `""`;
|
||||
exports[`components/post_view/Reaction should return null/empty if no emojiImageUrl 1`] = `<div />`;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,28 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import type {Reaction as ReactionType} from '@mattermost/types/reactions';
|
||||
|
||||
import {Reaction as ReactionClass} from 'components/post_view/reaction/reaction';
|
||||
|
||||
import {getIntl} from 'utils/i18n';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import ReactionComponent from './reaction';
|
||||
|
||||
jest.mock('./reaction_tooltip', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: ({children, onShow}: any) => (
|
||||
<div
|
||||
data-testid='reaction-tooltip'
|
||||
onMouseEnter={onShow}
|
||||
>{children}</div>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('components/post_view/Reaction', () => {
|
||||
const intl = getIntl();
|
||||
const post = TestHelper.getPostMock({
|
||||
id: 'post_id',
|
||||
});
|
||||
|
|
@ -43,12 +53,11 @@ describe('components/post_view/Reaction', () => {
|
|||
reactions,
|
||||
emojiImageUrl: 'emoji_image_url',
|
||||
actions,
|
||||
intl,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...baseProps}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ReactionComponent {...baseProps}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot when a current user reacted to a post', () => {
|
||||
|
|
@ -67,20 +76,20 @@ describe('components/post_view/Reaction', () => {
|
|||
currentUserReacted: true,
|
||||
reactions: newReactions,
|
||||
};
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ReactionComponent {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should return null/empty if no emojiImageUrl', () => {
|
||||
const props = {...baseProps, emojiImageUrl: ''};
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ReactionComponent {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should apply read-only class if user does not have permission to add reaction', () => {
|
||||
const props = {...baseProps, canAddReactions: false};
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ReactionComponent {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should apply read-only class if user does not have permission to remove reaction', () => {
|
||||
|
|
@ -89,13 +98,15 @@ describe('components/post_view/Reaction', () => {
|
|||
canRemoveReactions: false,
|
||||
currentUserReacted: true,
|
||||
};
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ReactionComponent {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should have called actions.getMissingProfilesByIds when loadMissingProfiles is called', () => {
|
||||
const wrapper = shallow<ReactionClass>(<ReactionClass {...baseProps}/>);
|
||||
wrapper.instance().loadMissingProfiles();
|
||||
test('should have called actions.getMissingProfilesByIds when loadMissingProfiles is called', async () => {
|
||||
renderWithContext(<ReactionComponent {...baseProps}/>);
|
||||
|
||||
const tooltipTrigger = screen.getByTestId('reaction-tooltip');
|
||||
await userEvent.hover(tooltipTrigger);
|
||||
|
||||
expect(actions.getMissingProfilesByIds).toHaveBeenCalledTimes(1);
|
||||
expect(actions.getMissingProfilesByIds).toHaveBeenCalledWith([reactions[0].user_id, reactions[1].user_id]);
|
||||
|
|
|
|||
|
|
@ -1,83 +1,19 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/ReactionList should render nothing when no reactions 1`] = `""`;
|
||||
exports[`components/ReactionList should render nothing when no reactions 1`] = `<div />`;
|
||||
|
||||
exports[`components/ReactionList should render when there are reactions 1`] = `
|
||||
<div
|
||||
aria-label="reactions"
|
||||
className="post-reaction-list"
|
||||
>
|
||||
<Connect(injectIntl(Reaction))
|
||||
emojiName="expressionless"
|
||||
key="expressionless"
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "",
|
||||
"create_at": 0,
|
||||
"delete_at": 0,
|
||||
"edit_at": 0,
|
||||
"hashtags": "",
|
||||
"id": "post_id",
|
||||
"is_pinned": false,
|
||||
"message": "post message",
|
||||
"metadata": Object {
|
||||
"embeds": Array [],
|
||||
"emojis": Array [],
|
||||
"files": Array [],
|
||||
"images": Object {},
|
||||
"reactions": Array [],
|
||||
},
|
||||
"original_id": "",
|
||||
"pending_post_id": "",
|
||||
"props": Object {},
|
||||
"reply_count": 0,
|
||||
"root_id": "",
|
||||
"type": "system_add_remove",
|
||||
"update_at": 0,
|
||||
"user_id": "user_id",
|
||||
}
|
||||
}
|
||||
reactions={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 1542994995740,
|
||||
"emoji_name": "expressionless",
|
||||
"post_id": "xbqfo5qb4bb4ffmj9hqfji6fiw",
|
||||
"user_id": "1rj9fokoeffrigu7sk5uc8aiih",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<AddReactionButton
|
||||
onEmojiClick={[Function]}
|
||||
post={
|
||||
Object {
|
||||
"channel_id": "",
|
||||
"create_at": 0,
|
||||
"delete_at": 0,
|
||||
"edit_at": 0,
|
||||
"hashtags": "",
|
||||
"id": "post_id",
|
||||
"is_pinned": false,
|
||||
"message": "post message",
|
||||
"metadata": Object {
|
||||
"embeds": Array [],
|
||||
"emojis": Array [],
|
||||
"files": Array [],
|
||||
"images": Object {},
|
||||
"reactions": Array [],
|
||||
},
|
||||
"original_id": "",
|
||||
"pending_post_id": "",
|
||||
"props": Object {},
|
||||
"reply_count": 0,
|
||||
"root_id": "",
|
||||
"type": "system_add_remove",
|
||||
"update_at": 0,
|
||||
"user_id": "user_id",
|
||||
}
|
||||
}
|
||||
teamId="teamId"
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
aria-label="reactions"
|
||||
class="post-reaction-list"
|
||||
>
|
||||
<div
|
||||
data-testid="reaction-expressionless"
|
||||
/>
|
||||
<div
|
||||
data-testid="add-reaction-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import Reaction from 'components/post_view/reaction';
|
||||
|
||||
import {render, screen} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import ReactionList from './reaction_list';
|
||||
|
||||
jest.mock('components/post_view/reaction', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: (props: any) => (
|
||||
<div data-testid={`reaction-${props.emojiName}`}/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./add_reaction_button', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: () => (
|
||||
<div data-testid='add-reaction-button'/>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('components/ReactionList', () => {
|
||||
const reaction = {
|
||||
user_id: '1rj9fokoeffrigu7sk5uc8aiih',
|
||||
|
|
@ -44,19 +60,19 @@ describe('components/ReactionList', () => {
|
|||
reactions: {},
|
||||
};
|
||||
|
||||
const wrapper = shallow<ReactionList>(
|
||||
const {container} = render(
|
||||
<ReactionList {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render when there are reactions', () => {
|
||||
const wrapper = shallow<ReactionList>(
|
||||
const {container} = render(
|
||||
<ReactionList {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
/*
|
||||
|
|
@ -91,14 +107,16 @@ describe('components/ReactionList', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow<ReactionList>(
|
||||
const {rerender} = render(
|
||||
<ReactionList {...propsA}/>,
|
||||
);
|
||||
const firstRender = wrapper.find(Reaction).map((node) => node.key);
|
||||
const firstRender = screen.getAllByTestId(/^reaction-/).map((el) => el.getAttribute('data-testid'));
|
||||
|
||||
wrapper.setProps(propsB);
|
||||
rerender(
|
||||
<ReactionList {...propsB}/>,
|
||||
);
|
||||
|
||||
const secondRender = wrapper.find(Reaction).map((node) => node.key);
|
||||
const secondRender = screen.getAllByTestId(/^reaction-/).map((el) => el.getAttribute('data-testid'));
|
||||
|
||||
expect(firstRender.length).toBe(2);
|
||||
expect(firstRender).toEqual(secondRender);
|
||||
|
|
|
|||
|
|
@ -127,8 +127,10 @@ describe('SuggestionBox', () => {
|
|||
expect(providerSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
expect(screen.queryByRole('listbox')).toBeVisible();
|
||||
expect(screen.getByText('Suggestion: testwordstestwords')).toBeVisible();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('listbox')).toBeVisible();
|
||||
expect(screen.getByText('Suggestion: testwordstestwords')).toBeVisible();
|
||||
});
|
||||
|
||||
// Clearing the textbox hides all suggestions
|
||||
await userEvent.clear(screen.getByPlaceholderText('test input'));
|
||||
|
|
|
|||
|
|
@ -1,88 +1,136 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/user_settings/advanced/JoinLeaveSection should match snapshot 1`] = `
|
||||
<SettingItemMin
|
||||
section="joinLeave"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Enable Join/Leave Messages"
|
||||
id="user.settings.advance.joinLeaveTitle"
|
||||
/>
|
||||
}
|
||||
updateSection={[Function]}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="joinLeaveTitle"
|
||||
>
|
||||
Enable Join/Leave Messages
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="joinLeaveTitle joinLeaveEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="joinLeaveEdit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="joinLeaveDesc"
|
||||
>
|
||||
<span>
|
||||
On
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/user_settings/advanced/JoinLeaveSection should match snapshot 2`] = `
|
||||
<Memo(SettingItemMax)
|
||||
inputs={
|
||||
Array [
|
||||
<fieldset>
|
||||
<legend
|
||||
className="form-legend hidden-label"
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
Enable Join/Leave Messages
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Enable Join/Leave Messages"
|
||||
id="user.settings.advance.joinLeaveTitle"
|
||||
<fieldset>
|
||||
<legend
|
||||
class="form-legend hidden-label"
|
||||
>
|
||||
Enable Join/Leave Messages
|
||||
</legend>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
checked=""
|
||||
class="a11y--active a11y--focused"
|
||||
id="joinLeaveOn"
|
||||
name="joinLeave"
|
||||
type="radio"
|
||||
value="true"
|
||||
/>
|
||||
On
|
||||
</label>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
id="joinLeaveOff"
|
||||
name="joinLeave"
|
||||
type="radio"
|
||||
value="false"
|
||||
/>
|
||||
Off
|
||||
</label>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
class="mt-5"
|
||||
>
|
||||
When "On", System Messages saying a user has joined or left a channel will be visible. When "Off", the System Messages about joining or leaving a channel will be hidden. A message will still show up when you are added to a channel, so you can receive a notification.
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<div
|
||||
role="alert"
|
||||
/>
|
||||
</legend>
|
||||
<div
|
||||
className="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
checked={true}
|
||||
id="joinLeaveOn"
|
||||
name="joinLeave"
|
||||
onChange={[Function]}
|
||||
type="radio"
|
||||
value="true"
|
||||
/>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="On"
|
||||
id="user.settings.advance.on"
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveSetting"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
data-testid="cancelButton"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
checked={false}
|
||||
id="joinLeaveOff"
|
||||
name="joinLeave"
|
||||
onChange={[Function]}
|
||||
type="radio"
|
||||
value="false"
|
||||
/>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Off"
|
||||
id="user.settings.advance.off"
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
className="mt-5"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="When \\"On\\", System Messages saying a user has joined or left a channel will be visible. When \\"Off\\", the System Messages about joining or leaving a channel will be hidden. A message will still show up when you are added to a channel, so you can receive a notification."
|
||||
id="user.settings.advance.joinLeaveDesc"
|
||||
/>
|
||||
</div>
|
||||
</fieldset>,
|
||||
]
|
||||
}
|
||||
setting="joinLeave"
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Enable Join/Leave Messages"
|
||||
id="user.settings.advance.joinLeaveTitle"
|
||||
/>
|
||||
}
|
||||
updateSection={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
import SettingItemMax from 'components/setting_item_max';
|
||||
import SettingItemMin from 'components/setting_item_min';
|
||||
import JoinLeaveSection from 'components/user_settings/advanced/join_leave_section/join_leave_section';
|
||||
|
||||
import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
|
||||
import {AdvancedSections} from 'utils/constants';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
|
|
@ -23,7 +20,7 @@ describe('components/user_settings/advanced/JoinLeaveSection', () => {
|
|||
userId: 'current_user_id',
|
||||
joinLeave: 'true',
|
||||
onUpdateSection: jest.fn(),
|
||||
renderOnOffLabel: jest.fn(),
|
||||
renderOnOffLabel: jest.fn((label: string) => <span>{label === 'true' ? 'On' : 'Off'}</span>),
|
||||
actions: {
|
||||
savePreferences: jest.fn(() => {
|
||||
return new Promise<void>((resolve) => {
|
||||
|
|
@ -34,44 +31,51 @@ describe('components/user_settings/advanced/JoinLeaveSection', () => {
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
const {container, rerender} = renderWithContext(
|
||||
<JoinLeaveSection {...defaultProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find(SettingItemMax).exists()).toEqual(false);
|
||||
expect(wrapper.find(SettingItemMin).exists()).toEqual(true);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.queryByTestId('saveSetting')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Enable Join/Leave Messages')).toBeInTheDocument();
|
||||
|
||||
wrapper.setProps({active: true});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find(SettingItemMax).exists()).toEqual(true);
|
||||
expect(wrapper.find(SettingItemMin).exists()).toEqual(false);
|
||||
rerender(
|
||||
<JoinLeaveSection
|
||||
{...defaultProps}
|
||||
active={true}
|
||||
/>,
|
||||
);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByTestId('saveSetting')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should match state on handleOnChange', () => {
|
||||
const wrapper = shallow<JoinLeaveSection>(
|
||||
<JoinLeaveSection {...defaultProps}/>,
|
||||
test('should match state on handleOnChange', async () => {
|
||||
renderWithContext(
|
||||
<JoinLeaveSection
|
||||
{...defaultProps}
|
||||
active={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper.setState({joinLeaveState: 'true'});
|
||||
const joinLeaveOff = screen.getByRole('radio', {name: /off/i});
|
||||
const joinLeaveOn = screen.getByRole('radio', {name: /on/i});
|
||||
|
||||
let value = 'false';
|
||||
wrapper.instance().handleOnChange({currentTarget: {value}} as any);
|
||||
expect(wrapper.state('joinLeaveState')).toEqual('false');
|
||||
await userEvent.click(joinLeaveOff);
|
||||
expect(joinLeaveOff).toBeChecked();
|
||||
|
||||
value = 'true';
|
||||
wrapper.instance().handleOnChange({currentTarget: {value}} as any);
|
||||
expect(wrapper.state('joinLeaveState')).toEqual('true');
|
||||
await userEvent.click(joinLeaveOn);
|
||||
expect(joinLeaveOn).toBeChecked();
|
||||
});
|
||||
|
||||
test('should call props.actions.savePreferences and props.onUpdateSection on handleSubmit', () => {
|
||||
test('should call props.actions.savePreferences and props.onUpdateSection on handleSubmit', async () => {
|
||||
const actions = {
|
||||
savePreferences: jest.fn().mockImplementation(() => Promise.resolve({data: true})),
|
||||
};
|
||||
const onUpdateSection = jest.fn();
|
||||
const wrapper = shallow<JoinLeaveSection>(
|
||||
renderWithContext(
|
||||
<JoinLeaveSection
|
||||
{...defaultProps}
|
||||
active={true}
|
||||
actions={actions}
|
||||
onUpdateSection={onUpdateSection}
|
||||
/>,
|
||||
|
|
@ -84,39 +88,40 @@ describe('components/user_settings/advanced/JoinLeaveSection', () => {
|
|||
value: 'true',
|
||||
};
|
||||
|
||||
const instance = wrapper.instance();
|
||||
instance.handleSubmit();
|
||||
// Click Save with initial joinLeave value 'true'
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(actions.savePreferences).toHaveBeenCalledTimes(1);
|
||||
expect(actions.savePreferences).toHaveBeenCalledWith('current_user_id', [joinLeavePreference]);
|
||||
expect(onUpdateSection).toHaveBeenCalledTimes(1);
|
||||
|
||||
wrapper.setState({joinLeaveState: 'false'});
|
||||
// Change to 'false' and save again
|
||||
await userEvent.click(screen.getByRole('radio', {name: /off/i}));
|
||||
joinLeavePreference.value = 'false';
|
||||
instance.handleSubmit();
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(actions.savePreferences).toHaveBeenCalledTimes(2);
|
||||
expect(actions.savePreferences).toHaveBeenCalledWith('current_user_id', [joinLeavePreference]);
|
||||
});
|
||||
|
||||
test('should match state and call props.onUpdateSection on handleUpdateSection', () => {
|
||||
test('should match state and call props.onUpdateSection on handleUpdateSection', async () => {
|
||||
const onUpdateSection = jest.fn();
|
||||
const wrapper = shallow<JoinLeaveSection>(
|
||||
renderWithContext(
|
||||
<JoinLeaveSection
|
||||
{...defaultProps}
|
||||
active={true}
|
||||
onUpdateSection={onUpdateSection}
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper.setState({joinLeaveState: 'false'});
|
||||
// Change the radio to 'false'
|
||||
await userEvent.click(screen.getByRole('radio', {name: /off/i}));
|
||||
expect(screen.getByRole('radio', {name: /off/i})).toBeChecked();
|
||||
|
||||
const instance = wrapper.instance();
|
||||
instance.handleUpdateSection();
|
||||
expect(wrapper.state('joinLeaveState')).toEqual(defaultProps.joinLeave);
|
||||
// Click Cancel → handleUpdateSection() resets state and calls onUpdateSection
|
||||
await userEvent.click(screen.getByTestId('cancelButton'));
|
||||
expect(onUpdateSection).toHaveBeenCalledTimes(1);
|
||||
|
||||
wrapper.setState({joinLeaveState: 'false'});
|
||||
instance.handleUpdateSection(AdvancedSections.JOIN_LEAVE);
|
||||
expect(onUpdateSection).toHaveBeenCalledTimes(2);
|
||||
expect(onUpdateSection).toHaveBeenCalledWith(AdvancedSections.JOIN_LEAVE);
|
||||
// After cancel, re-render as active to verify the state was reset
|
||||
// The joinLeave prop is 'true', so after reset the On radio should be checked
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import type {ComponentProps} from 'react';
|
||||
|
||||
import AdvancedSettingsDisplay from 'components/user_settings/advanced/user_settings_advanced';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import {Preferences} from 'utils/constants';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
import {isMac} from 'utils/user_agent';
|
||||
|
|
@ -51,67 +51,93 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
test('should have called handleSubmit', async () => {
|
||||
const updateSection = jest.fn();
|
||||
|
||||
const props = {...requiredProps, updateSection};
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
const props = {...requiredProps, updateSection, activeSection: 'formatting'};
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
|
||||
await wrapper.instance().handleSubmit([]);
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(updateSection).toHaveBeenCalledWith('');
|
||||
});
|
||||
|
||||
test('should have called updateSection', () => {
|
||||
test('should have called updateSection', async () => {
|
||||
const updateSection = jest.fn();
|
||||
const props = {...requiredProps, updateSection};
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
const props = {...requiredProps, updateSection, activeSection: 'formatting'};
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
|
||||
wrapper.instance().handleUpdateSection('');
|
||||
// Click Save → handleSubmit → handleUpdateSection('') → updateSection('')
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(updateSection).toHaveBeenCalledWith('');
|
||||
|
||||
wrapper.instance().handleUpdateSection('linkpreview');
|
||||
expect(updateSection).toHaveBeenCalledWith('linkpreview');
|
||||
});
|
||||
|
||||
test('should have called updateUserActive', () => {
|
||||
test('should have called updateUserActive', async () => {
|
||||
const updateUserActive = jest.fn(() => Promise.resolve({}));
|
||||
const props = {...requiredProps, actions: {...requiredProps.actions, updateUserActive}};
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
const revokeAllSessionsForUser = jest.fn().mockResolvedValue({data: true});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
enableUserDeactivation: true,
|
||||
activeSection: 'deactivateAccount',
|
||||
actions: {...requiredProps.actions, updateUserActive, revokeAllSessionsForUser},
|
||||
};
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
|
||||
// Click "Deactivate" button in SettingItemMax to show the modal
|
||||
await userEvent.click(screen.getByText('Deactivate'));
|
||||
|
||||
// Click the confirm button in the modal
|
||||
await userEvent.click(screen.getByText('Yes, deactivate my account'));
|
||||
|
||||
wrapper.instance().handleDeactivateAccountSubmit();
|
||||
expect(updateUserActive).toHaveBeenCalled();
|
||||
expect(updateUserActive).toHaveBeenCalledWith(requiredProps.user.id, false);
|
||||
});
|
||||
|
||||
test('handleDeactivateAccountSubmit() should have called revokeAllSessions', () => {
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...requiredProps}/>);
|
||||
test('handleDeactivateAccountSubmit() should have called revokeAllSessions', async () => {
|
||||
const revokeAllSessionsForUser = jest.fn().mockResolvedValue({data: true});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
enableUserDeactivation: true,
|
||||
activeSection: 'deactivateAccount',
|
||||
actions: {...requiredProps.actions, revokeAllSessionsForUser},
|
||||
};
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
|
||||
wrapper.instance().handleDeactivateAccountSubmit();
|
||||
expect(requiredProps.actions.revokeAllSessionsForUser).toHaveBeenCalled();
|
||||
expect(requiredProps.actions.revokeAllSessionsForUser).toHaveBeenCalledWith(requiredProps.user.id);
|
||||
// Click "Deactivate" button then confirm
|
||||
await userEvent.click(screen.getByText('Deactivate'));
|
||||
await userEvent.click(screen.getByText('Yes, deactivate my account'));
|
||||
|
||||
expect(revokeAllSessionsForUser).toHaveBeenCalled();
|
||||
expect(revokeAllSessionsForUser).toHaveBeenCalledWith(requiredProps.user.id);
|
||||
});
|
||||
|
||||
test('handleDeactivateAccountSubmit() should have updated state.serverError', async () => {
|
||||
const error = {message: 'error'};
|
||||
const revokeAllSessionsForUser = () => Promise.resolve({error});
|
||||
const props = {...requiredProps, actions: {...requiredProps.actions, revokeAllSessionsForUser}};
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
const revokeAllSessionsForUser = jest.fn(() => Promise.resolve({error}));
|
||||
const props = {
|
||||
...requiredProps,
|
||||
enableUserDeactivation: true,
|
||||
activeSection: 'deactivateAccount',
|
||||
actions: {...requiredProps.actions, revokeAllSessionsForUser},
|
||||
};
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
|
||||
await wrapper.instance().handleDeactivateAccountSubmit();
|
||||
// Click "Deactivate" button then confirm
|
||||
await userEvent.click(screen.getByText('Deactivate'));
|
||||
await userEvent.click(screen.getByText('Yes, deactivate my account'));
|
||||
|
||||
expect(wrapper.state().serverError).toEqual(error.message);
|
||||
expect(await screen.findByText(error.message)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('function getCtrlSendText should return correct value for Mac', () => {
|
||||
(isMac as jest.Mock).mockReturnValue(true);
|
||||
const props = {...requiredProps};
|
||||
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
expect(wrapper.instance().getCtrlSendText().ctrlSendTitle.defaultMessage).toEqual('Send Messages on ⌘+ENTER');
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
expect(screen.getByText('Send Messages on ⌘+ENTER')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('function getCtrlSendText should return correct value for Windows', () => {
|
||||
(isMac as jest.Mock).mockReturnValue(false);
|
||||
const props = {...requiredProps};
|
||||
|
||||
const wrapper = shallow<AdvancedSettingsDisplay>(<AdvancedSettingsDisplay {...props}/>);
|
||||
expect(wrapper.instance().getCtrlSendText().ctrlSendTitle.defaultMessage).toEqual('Send Messages on CTRL+ENTER');
|
||||
renderWithContext(<AdvancedSettingsDisplay {...props}/>);
|
||||
expect(screen.getByText('Send Messages on CTRL+ENTER')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,18 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import ManageTimezones from './manage_timezones';
|
||||
|
||||
jest.mock('utils/timezone', () => ({
|
||||
getBrowserTimezone: jest.fn(() => 'America/New_York'),
|
||||
}));
|
||||
|
||||
describe('components/user_settings/display/manage_timezones/manage_timezones', () => {
|
||||
const user = {
|
||||
id: 'user_id',
|
||||
|
|
@ -30,22 +35,44 @@ describe('components/user_settings/display/manage_timezones/manage_timezones', (
|
|||
|
||||
test('submitUser() should have called [updateMe, updateSection]', async () => {
|
||||
const updateMe = jest.fn(() => Promise.resolve({data: true}));
|
||||
const props = {...requiredProps, actions: {...requiredProps.actions, updateMe}};
|
||||
const wrapper = shallow(<ManageTimezones {...props}/>);
|
||||
const updateSection = jest.fn();
|
||||
const props = {
|
||||
...requiredProps,
|
||||
updateSection,
|
||||
useAutomaticTimezone: false,
|
||||
automaticTimezone: 'Europe/London',
|
||||
manualTimezone: 'Europe/London',
|
||||
timezones: [{
|
||||
value: 'GMT Standard Time',
|
||||
abbr: 'GMT',
|
||||
offset: 0,
|
||||
isdst: false,
|
||||
text: '(UTC) London',
|
||||
utc: ['Europe/London'],
|
||||
}],
|
||||
actions: {...requiredProps.actions, updateMe},
|
||||
};
|
||||
renderWithContext(<ManageTimezones {...props}/>);
|
||||
|
||||
await (wrapper.instance() as ManageTimezones).submitUser();
|
||||
// Toggle the automatic timezone checkbox to create a state diff
|
||||
await userEvent.click(screen.getByRole('checkbox', {name: /automatic/i}));
|
||||
|
||||
const expected = {...props.user,
|
||||
// Click Save to trigger changeTimezone → submitUser
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
|
||||
const expected = {
|
||||
...props.user,
|
||||
timezone: {
|
||||
useAutomaticTimezone: props.useAutomaticTimezone.toString(),
|
||||
manualTimezone: props.manualTimezone,
|
||||
automaticTimezone: props.automaticTimezone,
|
||||
}};
|
||||
useAutomaticTimezone: 'true',
|
||||
manualTimezone: 'Europe/London',
|
||||
automaticTimezone: 'America/New_York',
|
||||
},
|
||||
};
|
||||
|
||||
expect(props.actions.updateMe).toHaveBeenCalled();
|
||||
expect(props.actions.updateMe).toHaveBeenCalledWith(expected);
|
||||
expect(updateMe).toHaveBeenCalled();
|
||||
expect(updateMe).toHaveBeenCalledWith(expected);
|
||||
|
||||
expect(props.updateSection).toHaveBeenCalled();
|
||||
expect(props.updateSection).toHaveBeenCalledWith('');
|
||||
expect(updateSection).toHaveBeenCalled();
|
||||
expect(updateSection).toHaveBeenCalledWith('');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,20 +1,39 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import configureStore from 'store';
|
||||
|
||||
import {getAllLanguages} from 'i18n/i18n';
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {act} from 'tests/react_testing_utils';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
import UserSettingsDisplay from './user_settings_display';
|
||||
|
||||
jest.mock('./manage_timezones', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid='manage-timezones'/>,
|
||||
}));
|
||||
|
||||
jest.mock('./manage_languages', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid='manage-languages'/>,
|
||||
}));
|
||||
|
||||
jest.mock('components/user_settings/display/user_settings_theme', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid='theme-setting'/>,
|
||||
}));
|
||||
|
||||
jest.mock('./render_emoticons_as_emoji', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid='render-emoticons'/>,
|
||||
}));
|
||||
|
||||
jest.mock('utils/timezone', () => ({
|
||||
getBrowserTimezone: jest.fn(() => 'America/New_York'),
|
||||
}));
|
||||
|
||||
describe('components/user_settings/display/UserSettingsDisplay', () => {
|
||||
const user = {
|
||||
id: 'user_id',
|
||||
|
|
@ -101,20 +120,15 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
lastActiveTimeEnabled: true,
|
||||
};
|
||||
|
||||
let store: ReturnType<typeof configureStore>;
|
||||
beforeEach(() => {
|
||||
store = configureStore();
|
||||
});
|
||||
|
||||
test('should match snapshot, no active section', () => {
|
||||
const wrapper = shallow(<UserSettingsDisplay {...requiredProps}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...requiredProps}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, collapse section', () => {
|
||||
const props = {...requiredProps, activeSection: 'collapse'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, link preview section with EnableLinkPreviews is false', () => {
|
||||
|
|
@ -123,8 +137,8 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
activeSection: 'linkpreview',
|
||||
enableLinkPreviews: false,
|
||||
};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, link preview section with EnableLinkPreviews is true', () => {
|
||||
|
|
@ -133,44 +147,44 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
activeSection: 'linkpreview',
|
||||
enableLinkPreviews: true,
|
||||
};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, clock section', () => {
|
||||
const props = {...requiredProps, activeSection: 'clock'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, teammate name display section', () => {
|
||||
const props = {...requiredProps, activeSection: 'teammate_name_display'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, timezone section', () => {
|
||||
const props = {...requiredProps, activeSection: 'timezone'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, message display section', () => {
|
||||
const props = {...requiredProps, activeSection: 'message_display'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, channel display mode section', () => {
|
||||
const props = {...requiredProps, activeSection: 'channel_display_mode'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, languages section', () => {
|
||||
const props = {...requiredProps, activeSection: 'languages'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, theme section with EnableThemeSelection is false', () => {
|
||||
|
|
@ -179,8 +193,8 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
activeSection: 'theme',
|
||||
enableThemeSelection: false,
|
||||
};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, theme section with EnableThemeSelection is true', () => {
|
||||
|
|
@ -189,252 +203,199 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
activeSection: 'theme',
|
||||
enableThemeSelection: true,
|
||||
};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, clickToReply section', () => {
|
||||
const props = {...requiredProps, activeSection: 'click_to_reply'};
|
||||
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should have called handleSubmit', async () => {
|
||||
const updateSection = jest.fn();
|
||||
|
||||
const props = {...requiredProps, updateSection};
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...props}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
const props = {...requiredProps, updateSection, activeSection: 'clock'};
|
||||
renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
await act(() => (wrapper.instance() as UserSettingsDisplay).handleSubmit());
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(updateSection).toHaveBeenCalledWith('');
|
||||
});
|
||||
|
||||
test('should have called updateSection', () => {
|
||||
test('should have called updateSection', async () => {
|
||||
const updateSection = jest.fn();
|
||||
|
||||
const props = {...requiredProps, updateSection};
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...props}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
const props = {...requiredProps, updateSection, activeSection: 'clock'};
|
||||
renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).updateSection('');
|
||||
});
|
||||
// Click Save → handleSubmit → updateSection('')
|
||||
await userEvent.click(screen.getByTestId('saveSetting'));
|
||||
expect(updateSection).toHaveBeenCalledWith('');
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).updateSection('linkpreview');
|
||||
});
|
||||
expect(updateSection).toHaveBeenCalledWith('linkpreview');
|
||||
// Click Cancel → updateSection is called again
|
||||
updateSection.mockClear();
|
||||
await userEvent.click(screen.getByTestId('cancelButton'));
|
||||
expect(updateSection).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should have called closeModal', () => {
|
||||
test('should have called closeModal', async () => {
|
||||
const closeModal = jest.fn();
|
||||
const props = {...requiredProps, closeModal};
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...props}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
wrapper.find('#closeButton').simulate('click');
|
||||
await userEvent.click(container.querySelector('#closeButton')!);
|
||||
expect(closeModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should have called collapseModal', () => {
|
||||
test('should have called collapseModal', async () => {
|
||||
const collapseModal = jest.fn();
|
||||
const props = {...requiredProps, collapseModal};
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...props}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
wrapper.find('.fa-angle-left').simulate('click');
|
||||
await userEvent.click(container.querySelector('.fa-angle-left')!);
|
||||
expect(collapseModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should update militaryTime state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update militaryTime state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'clock'};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleClockRadio('false');
|
||||
});
|
||||
expect(wrapper.state('militaryTime')).toBe('false');
|
||||
const radioA = container.querySelector('#clockFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#clockFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleClockRadio('true');
|
||||
});
|
||||
expect(wrapper.state('militaryTime')).toBe('true');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update teammateNameDisplay state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update teammateNameDisplay state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'name_format'};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleTeammateNameDisplayRadio('username');
|
||||
});
|
||||
expect(wrapper.state('teammateNameDisplay')).toBe('username');
|
||||
const radioA = container.querySelector('#name_formatFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#name_formatFormatB') as HTMLInputElement;
|
||||
const radioC = container.querySelector('#name_formatFormatC') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleTeammateNameDisplayRadio('nickname_full_name');
|
||||
});
|
||||
expect(wrapper.state('teammateNameDisplay')).toBe('nickname_full_name');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleTeammateNameDisplayRadio('full_name');
|
||||
});
|
||||
expect(wrapper.state('teammateNameDisplay')).toBe('full_name');
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
|
||||
await userEvent.click(radioC);
|
||||
expect(radioC).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update channelDisplayMode state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update channelDisplayMode state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'channel_display_mode'};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleChannelDisplayModeRadio('full');
|
||||
});
|
||||
expect(wrapper.state('channelDisplayMode')).toBe('full');
|
||||
const radioA = container.querySelector('#channel_display_modeFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#channel_display_modeFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleChannelDisplayModeRadio('centered');
|
||||
});
|
||||
expect(wrapper.state('channelDisplayMode')).toBe('centered');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update messageDisplay state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update messageDisplay state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'message_display'};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handlemessageDisplayRadio('clean');
|
||||
});
|
||||
expect(wrapper.state('messageDisplay')).toBe('clean');
|
||||
const radioA = container.querySelector('#message_displayFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#message_displayFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handlemessageDisplayRadio('compact');
|
||||
});
|
||||
expect(wrapper.state('messageDisplay')).toBe('compact');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update collapseDisplay state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update collapseDisplay state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'collapse'};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleCollapseRadio('false');
|
||||
});
|
||||
expect(wrapper.state('collapseDisplay')).toBe('false');
|
||||
const radioA = container.querySelector('#collapseFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#collapseFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleCollapseRadio('true');
|
||||
});
|
||||
expect(wrapper.state('collapseDisplay')).toBe('true');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update linkPreviewDisplay state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update linkPreviewDisplay state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'linkpreview', enableLinkPreviews: true};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleLinkPreviewRadio('false');
|
||||
});
|
||||
expect(wrapper.state('linkPreviewDisplay')).toBe('false');
|
||||
const radioA = container.querySelector('#linkpreviewFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#linkpreviewFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleLinkPreviewRadio('true');
|
||||
});
|
||||
expect(wrapper.state('linkPreviewDisplay')).toBe('true');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update display state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update display state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'linkpreview', enableLinkPreviews: true};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleOnChange({} as React.ChangeEvent, {display: 'linkPreviewDisplay'});
|
||||
});
|
||||
expect(wrapper.state('display')).toBe('linkPreviewDisplay');
|
||||
// Click radio buttons in the link preview section to test handleOnChange
|
||||
const radioA = container.querySelector('#linkpreviewFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#linkpreviewFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleOnChange({} as React.ChangeEvent, {display: 'collapseDisplay'});
|
||||
});
|
||||
expect(wrapper.state('display')).toBe('collapseDisplay');
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update collapsed reply threads state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update collapsed reply threads state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'collapsed_reply_threads', collapsedReplyThreadsAllowUserPreference: true};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleCollapseReplyThreadsRadio('off');
|
||||
});
|
||||
expect(wrapper.state('collapsedReplyThreads')).toBe('off');
|
||||
const radioA = container.querySelector('#collapsed_reply_threadsFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#collapsed_reply_threadsFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleCollapseReplyThreadsRadio('on');
|
||||
});
|
||||
expect(wrapper.state('collapsedReplyThreads')).toBe('on');
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
});
|
||||
|
||||
test('should update last active state', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<UserSettingsDisplay {...requiredProps}/>
|
||||
</Provider>,
|
||||
).find(UserSettingsDisplay);
|
||||
test('should update last active state', async () => {
|
||||
const props = {...requiredProps, activeSection: 'lastactive', lastActiveTimeEnabled: true};
|
||||
const {container} = renderWithContext(<UserSettingsDisplay {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleLastActiveRadio('false');
|
||||
});
|
||||
expect(wrapper.state('lastActiveDisplay')).toBe('false');
|
||||
const radioA = container.querySelector('#lastactiveFormatA') as HTMLInputElement;
|
||||
const radioB = container.querySelector('#lastactiveFormatB') as HTMLInputElement;
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as UserSettingsDisplay).handleLastActiveRadio('true');
|
||||
});
|
||||
expect(wrapper.state('lastActiveDisplay')).toBe('true');
|
||||
await userEvent.click(radioB);
|
||||
expect(radioB).toBeChecked();
|
||||
|
||||
await userEvent.click(radioA);
|
||||
expect(radioA).toBeChecked();
|
||||
});
|
||||
|
||||
test('should not show last active section', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<UserSettingsDisplay
|
||||
{...requiredProps}
|
||||
lastActiveTimeEnabled={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,20 +1,39 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/user_settings/display/user_settings_theme/user_settings_theme should match snapshot 1`] = `
|
||||
<SettingItemMin
|
||||
describe={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Open to manage your theme"
|
||||
id="user.settings.display.theme.describe"
|
||||
/>
|
||||
}
|
||||
section="theme"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Theme"
|
||||
id="user.settings.display.theme.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[Function]}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="themeTitle"
|
||||
>
|
||||
Theme
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="themeTitle themeEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="themeEdit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="themeDesc"
|
||||
>
|
||||
Open to manage your theme
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,34 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`components/user_settings/display/ColorChooser should match, init 1`] = `
|
||||
<Fragment>
|
||||
<div>
|
||||
<label
|
||||
className="custom-label"
|
||||
htmlFor="choose-color-inputColorValue"
|
||||
class="custom-label"
|
||||
for="choose-color-inputColorValue"
|
||||
>
|
||||
Choose color
|
||||
</label>
|
||||
<ColorInput
|
||||
id="choose-color"
|
||||
onChange={[Function]}
|
||||
value="#ffeec0"
|
||||
/>
|
||||
</Fragment>
|
||||
<div
|
||||
class="color-input input-group"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
data-testid="color-inputColorValue"
|
||||
id="choose-color-inputColorValue"
|
||||
maxlength="7"
|
||||
type="text"
|
||||
value="#ffeec0"
|
||||
/>
|
||||
<span
|
||||
class="input-group-addon color-pad"
|
||||
id="choose-color-squareColorIcon"
|
||||
>
|
||||
<i
|
||||
class="color-icon"
|
||||
id="choose-color-squareColorIconValue"
|
||||
style="background-color: rgb(255, 238, 192);"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {render} from 'tests/react_testing_utils';
|
||||
|
||||
import ColorChooser from './color_chooser';
|
||||
|
||||
describe('components/user_settings/display/ColorChooser', () => {
|
||||
it('should match, init', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = render(
|
||||
<ColorChooser
|
||||
label='Choose color'
|
||||
id='choose-color'
|
||||
|
|
@ -16,6 +17,6 @@ describe('components/user_settings/display/ColorChooser', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,47 +1,36 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import type {ChangeEvent} from 'react';
|
||||
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
|
||||
import {CustomThemeChooser} from 'components/user_settings/display/user_settings_theme/custom_theme_chooser/custom_theme_chooser';
|
||||
import CustomThemeChooser from 'components/user_settings/display/user_settings_theme/custom_theme_chooser/custom_theme_chooser';
|
||||
|
||||
import {type MockIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
|
||||
describe('components/user_settings/display/CustomThemeChooser', () => {
|
||||
const baseProps = {
|
||||
theme: Preferences.THEMES.denim,
|
||||
updateTheme: jest.fn(),
|
||||
intl: {formatMessage: jest.fn()} as MockIntl,
|
||||
};
|
||||
|
||||
it('should match, init', () => {
|
||||
const elementMock = {addEventListener: jest.fn()};
|
||||
jest.spyOn(document, 'querySelector').mockImplementation(() => elementMock as unknown as HTMLElement);
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<CustomThemeChooser {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should create a custom theme when the code theme changes', () => {
|
||||
const elementMock = {addEventListener: jest.fn()};
|
||||
jest.spyOn(document, 'querySelector').mockImplementation(() => elementMock as unknown as HTMLElement);
|
||||
const wrapper = shallow<CustomThemeChooser>(
|
||||
it('should create a custom theme when the code theme changes', async () => {
|
||||
renderWithContext(
|
||||
<CustomThemeChooser {...baseProps}/>,
|
||||
);
|
||||
|
||||
const event = {
|
||||
target: {
|
||||
value: 'monokai',
|
||||
},
|
||||
} as ChangeEvent<HTMLSelectElement>;
|
||||
const codeThemeSelect = screen.getByRole('combobox', {name: 'Code Theme'});
|
||||
await userEvent.selectOptions(codeThemeSelect, 'monokai');
|
||||
|
||||
wrapper.instance().onCodeThemeChange(event);
|
||||
expect(baseProps.updateTheme).toHaveBeenCalledTimes(1);
|
||||
expect(baseProps.updateTheme).toHaveBeenCalledWith({
|
||||
...baseProps.theme,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import type {ComponentProps} from 'react';
|
||||
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
|
||||
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||
|
||||
import UserSettingsTheme from './user_settings_theme';
|
||||
|
||||
jest.mock('utils/utils', () => ({
|
||||
|
|
@ -34,32 +35,39 @@ describe('components/user_settings/display/user_settings_theme/user_settings_the
|
|||
};
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
const {container} = renderWithContext(
|
||||
<UserSettingsTheme {...requiredProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should saveTheme', async () => {
|
||||
const wrapper = shallow<UserSettingsTheme>(
|
||||
<UserSettingsTheme {...requiredProps}/>,
|
||||
const props = {
|
||||
...requiredProps,
|
||||
selected: true,
|
||||
};
|
||||
|
||||
renderWithContext(
|
||||
<UserSettingsTheme {...props}/>,
|
||||
);
|
||||
|
||||
await wrapper.instance().submitTheme();
|
||||
// Click the Save button to trigger submitTheme
|
||||
const saveButton = screen.getByText('Save');
|
||||
await userEvent.click(saveButton);
|
||||
|
||||
expect(requiredProps.setRequireConfirm).toHaveBeenCalledTimes(1);
|
||||
expect(requiredProps.setRequireConfirm).toHaveBeenCalledWith(false);
|
||||
await waitFor(() => {
|
||||
expect(requiredProps.setRequireConfirm).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
expect(requiredProps.updateSection).toHaveBeenCalledTimes(1);
|
||||
expect(requiredProps.updateSection).toHaveBeenCalledWith('');
|
||||
|
||||
expect(requiredProps.actions.saveTheme).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should deleteTeamSpecificThemes if applyToAllTeams is enabled', async () => {
|
||||
const props = {
|
||||
...requiredProps,
|
||||
selected: true,
|
||||
actions: {
|
||||
saveTheme: jest.fn().mockResolvedValue({data: true}),
|
||||
deleteTeamSpecificThemes: jest.fn().mockResolvedValue({data: true}),
|
||||
|
|
@ -67,13 +75,20 @@ describe('components/user_settings/display/user_settings_theme/user_settings_the
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow<UserSettingsTheme>(
|
||||
renderWithContext(
|
||||
<UserSettingsTheme {...props}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().setState({applyToAllTeams: true});
|
||||
await wrapper.instance().submitTheme();
|
||||
// The applyToAllTeams checkbox should be checked by default (from props)
|
||||
const checkbox = screen.getByRole('checkbox');
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
expect(props.actions.deleteTeamSpecificThemes).toHaveBeenCalled();
|
||||
// Click Save to trigger submitTheme
|
||||
const saveButton = screen.getByText('Save');
|
||||
await userEvent.click(saveButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.actions.deleteTeamSpecificThemes).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,14 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import type {ComponentProps} from 'react';
|
||||
|
||||
import EmailNotificationSetting from 'components/user_settings/notifications/email_notification_setting/email_notification_setting';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {act} from 'tests/react_testing_utils';
|
||||
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||
import {Preferences, NotificationLevels} from 'utils/constants';
|
||||
|
||||
describe('components/user_settings/notifications/EmailNotificationSetting', () => {
|
||||
|
|
@ -35,13 +33,13 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...requiredProps}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...requiredProps}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('#emailNotificationImmediately').exists()).toBe(true);
|
||||
expect(wrapper.find('#emailNotificationNever').exists()).toBe(true);
|
||||
expect(wrapper.find('#emailNotificationMinutes').exists()).toBe(false);
|
||||
expect(wrapper.find('#emailNotificationHour').exists()).toBe(false);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(document.getElementById('emailNotificationImmediately')).toBeInTheDocument();
|
||||
expect(document.getElementById('emailNotificationNever')).toBeInTheDocument();
|
||||
expect(document.getElementById('emailNotificationMinutes')).not.toBeInTheDocument();
|
||||
expect(document.getElementById('emailNotificationHour')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should match snapshot, enabled email batching', () => {
|
||||
|
|
@ -49,11 +47,11 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
...requiredProps,
|
||||
enableEmailBatching: true,
|
||||
};
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('#emailNotificationMinutes').exists()).toBe(true);
|
||||
expect(wrapper.find('#emailNotificationHour').exists()).toBe(true);
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(document.getElementById('emailNotificationMinutes')).toBeInTheDocument();
|
||||
expect(document.getElementById('emailNotificationHour')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should match snapshot, not send email notifications', () => {
|
||||
|
|
@ -61,9 +59,9 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
...requiredProps,
|
||||
sendEmailNotifications: false,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, active section != email and SendEmailNotifications !== true', () => {
|
||||
|
|
@ -72,9 +70,9 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
sendEmailNotifications: false,
|
||||
active: false,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, active section != email and SendEmailNotifications = true', () => {
|
||||
|
|
@ -83,9 +81,9 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
sendEmailNotifications: true,
|
||||
active: false,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, active section != email, SendEmailNotifications = true and enableEmail = true', () => {
|
||||
|
|
@ -95,16 +93,16 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
active: false,
|
||||
enableEmail: true,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on serverError', () => {
|
||||
const newServerError = 'serverError';
|
||||
const props = {...requiredProps, error: newServerError};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, when CRT on and email set to immediately', () => {
|
||||
|
|
@ -113,9 +111,9 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
enableEmail: true,
|
||||
isCollapsedThreadsEnabled: true,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, when CRT on and email set to never', () => {
|
||||
|
|
@ -124,17 +122,16 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
enableEmail: false,
|
||||
isCollapsedThreadsEnabled: true,
|
||||
};
|
||||
const wrapper = shallow(<EmailNotificationSetting {...props}/>);
|
||||
const {container} = renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should pass handleChange', () => {
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...requiredProps}/>);
|
||||
wrapper.find('#emailNotificationImmediately').simulate('change');
|
||||
test('should pass handleChange', async () => {
|
||||
renderWithContext(<EmailNotificationSetting {...requiredProps}/>);
|
||||
|
||||
await userEvent.click(document.getElementById('emailNotificationImmediately')!);
|
||||
|
||||
expect(wrapper.state('enableEmail')).toBe(true);
|
||||
expect(wrapper.state('newInterval')).toBe(Preferences.INTERVAL_IMMEDIATE);
|
||||
expect(requiredProps.onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
|
@ -149,21 +146,26 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
actions: {savePreferences: newSavePreference},
|
||||
};
|
||||
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...props}/>);
|
||||
renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
await (wrapper.instance() as EmailNotificationSetting).handleSubmit();
|
||||
expect(wrapper.state('newInterval')).toBe(Preferences.INTERVAL_NEVER);
|
||||
expect(newOnSubmit).toHaveBeenCalled();
|
||||
// Submit with default state (never)
|
||||
await userEvent.click(screen.getByText('Save'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(newOnSubmit).toHaveBeenCalled();
|
||||
});
|
||||
expect(newUpdateSection).toHaveBeenCalledTimes(1);
|
||||
expect(newUpdateSection).toHaveBeenCalledWith('');
|
||||
|
||||
const newInterval = Preferences.INTERVAL_IMMEDIATE;
|
||||
wrapper.find('#emailNotificationImmediately').simulate('change');
|
||||
await (wrapper.instance() as EmailNotificationSetting).handleSubmit();
|
||||
// Change to immediately and submit again
|
||||
await userEvent.click(document.getElementById('emailNotificationImmediately')!);
|
||||
await userEvent.click(screen.getByText('Save'));
|
||||
|
||||
expect(wrapper.state('newInterval')).toBe(newInterval);
|
||||
expect(newOnSubmit).toHaveBeenCalled();
|
||||
expect(newOnSubmit).toHaveBeenCalledTimes(2);
|
||||
const newInterval = Preferences.INTERVAL_IMMEDIATE;
|
||||
|
||||
await waitFor(() => {
|
||||
expect(newOnSubmit).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
const expectedPref = [{
|
||||
category: 'notifications',
|
||||
|
|
@ -176,54 +178,45 @@ describe('components/user_settings/notifications/EmailNotificationSetting', () =
|
|||
expect(newSavePreference).toHaveBeenCalledWith('current_user_id', expectedPref);
|
||||
});
|
||||
|
||||
test('should pass handleUpdateSection', () => {
|
||||
test('should pass handleUpdateSection', async () => {
|
||||
const newUpdateSection = jest.fn();
|
||||
const newOnCancel = jest.fn();
|
||||
const props = {...requiredProps, updateSection: newUpdateSection, onCancel: newOnCancel};
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...props}/>);
|
||||
renderWithContext(<EmailNotificationSetting {...props}/>);
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as EmailNotificationSetting).handleUpdateSection('email');
|
||||
});
|
||||
expect(newUpdateSection).toHaveBeenCalledWith('email');
|
||||
expect(newUpdateSection).toHaveBeenCalledTimes(1);
|
||||
expect(newOnCancel).not.toHaveBeenCalled();
|
||||
// Click Cancel to trigger handleUpdateSection without section arg
|
||||
await userEvent.click(screen.getByText('Cancel'));
|
||||
|
||||
act(() => {
|
||||
(wrapper.instance() as EmailNotificationSetting).handleUpdateSection();
|
||||
});
|
||||
expect(newUpdateSection).toHaveBeenCalled();
|
||||
expect(newUpdateSection).toHaveBeenCalledTimes(2);
|
||||
expect(newUpdateSection).toHaveBeenCalledWith('');
|
||||
expect(newOnCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should derived state from props', () => {
|
||||
const {container, rerender} = renderWithContext(<EmailNotificationSetting {...requiredProps}/>);
|
||||
|
||||
// Verify initial render matches props
|
||||
expect(container).toBeTruthy();
|
||||
|
||||
// Rerender with new props
|
||||
const nextProps = {
|
||||
...requiredProps,
|
||||
enableEmail: false,
|
||||
emailInterval: Preferences.INTERVAL_IMMEDIATE,
|
||||
enableEmailBatching: true,
|
||||
sendEmailNotifications: true,
|
||||
};
|
||||
const wrapper = mountWithIntl(<EmailNotificationSetting {...requiredProps}/>);
|
||||
expect(wrapper.state('emailInterval')).toBe(requiredProps.emailInterval);
|
||||
expect(wrapper.state('enableEmailBatching')).toBe(requiredProps.enableEmailBatching);
|
||||
expect(wrapper.state('sendEmailNotifications')).toBe(requiredProps.sendEmailNotifications);
|
||||
expect(wrapper.state('newInterval')).toBe(Preferences.INTERVAL_NEVER);
|
||||
|
||||
wrapper.setProps(nextProps);
|
||||
expect(wrapper.state('emailInterval')).toBe(nextProps.emailInterval);
|
||||
expect(wrapper.state('enableEmailBatching')).toBe(nextProps.enableEmailBatching);
|
||||
expect(wrapper.state('sendEmailNotifications')).toBe(nextProps.sendEmailNotifications);
|
||||
expect(wrapper.state('newInterval')).toBe(Preferences.INTERVAL_NEVER);
|
||||
rerender(<EmailNotificationSetting {...nextProps}/>);
|
||||
expect(container).toBeTruthy();
|
||||
|
||||
nextProps.enableEmail = true;
|
||||
nextProps.emailInterval = Preferences.INTERVAL_FIFTEEN_MINUTES;
|
||||
wrapper.setProps(nextProps);
|
||||
expect(wrapper.state('emailInterval')).toBe(nextProps.emailInterval);
|
||||
expect(wrapper.state('enableEmail')).toBe(nextProps.enableEmail);
|
||||
expect(wrapper.state('enableEmailBatching')).toBe(nextProps.enableEmailBatching);
|
||||
expect(wrapper.state('sendEmailNotifications')).toBe(nextProps.sendEmailNotifications);
|
||||
expect(wrapper.state('newInterval')).toBe(Preferences.INTERVAL_FIFTEEN_MINUTES);
|
||||
// Rerender again with updated props
|
||||
const updatedProps = {
|
||||
...nextProps,
|
||||
enableEmail: true,
|
||||
emailInterval: Preferences.INTERVAL_FIFTEEN_MINUTES,
|
||||
};
|
||||
|
||||
rerender(<EmailNotificationSetting {...updatedProps}/>);
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,100 +1,168 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`plugin tab all props are properly passed to the children 1`] = `
|
||||
<div
|
||||
aria-labelledby="pluginAButton"
|
||||
id="pluginASettings"
|
||||
role="tabpanel"
|
||||
>
|
||||
<SettingMobileHeader
|
||||
closeModal={[MockFunction]}
|
||||
collapseModal={[MockFunction]}
|
||||
text="plugin A Settings"
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
className="user-settings"
|
||||
aria-labelledby="pluginAButton"
|
||||
id="pluginASettings"
|
||||
role="tabpanel"
|
||||
>
|
||||
<SettingDesktopHeader
|
||||
text="plugin A Settings"
|
||||
/>
|
||||
<PluginAction
|
||||
action={
|
||||
Object {
|
||||
"buttonText": "buttonText",
|
||||
"onClick": [MockFunction],
|
||||
"text": "actionText",
|
||||
"title": "actionTitle",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="divider-dark first"
|
||||
/>
|
||||
<PluginSetting
|
||||
activeSection=""
|
||||
pluginId="pluginA"
|
||||
section={
|
||||
Object {
|
||||
"onSubmit": [MockFunction],
|
||||
"settings": Array [
|
||||
Object {
|
||||
"default": "0",
|
||||
"name": "0",
|
||||
"options": Array [
|
||||
Object {
|
||||
"text": "Option 0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"text": "Option 1",
|
||||
"value": "1",
|
||||
},
|
||||
],
|
||||
"type": "radio",
|
||||
},
|
||||
],
|
||||
"title": "section 1",
|
||||
}
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
id="closeButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
</button>
|
||||
<h4
|
||||
class="modal-title"
|
||||
>
|
||||
<div
|
||||
class="modal-back"
|
||||
>
|
||||
<i
|
||||
aria-label="Collapse Icon"
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
plugin A Settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
className="divider-light"
|
||||
/>
|
||||
<PluginSetting
|
||||
activeSection=""
|
||||
pluginId="pluginA"
|
||||
section={
|
||||
Object {
|
||||
"onSubmit": [MockFunction],
|
||||
"settings": Array [
|
||||
Object {
|
||||
"default": "1",
|
||||
"name": "1",
|
||||
"options": Array [
|
||||
Object {
|
||||
"text": "Option 0",
|
||||
"value": "0",
|
||||
},
|
||||
Object {
|
||||
"text": "Option 1",
|
||||
"value": "1",
|
||||
},
|
||||
],
|
||||
"type": "radio",
|
||||
},
|
||||
],
|
||||
"title": "section 2",
|
||||
}
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
<div
|
||||
className="divider-light"
|
||||
/>
|
||||
<div
|
||||
className="divider-dark"
|
||||
/>
|
||||
class="user-settings"
|
||||
>
|
||||
<div
|
||||
class="userSettingDesktopHeader"
|
||||
>
|
||||
<h3
|
||||
class="tab-header"
|
||||
>
|
||||
plugin A Settings
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="pluginActionContainer"
|
||||
>
|
||||
<div
|
||||
class="sectionNoticeContainer info"
|
||||
>
|
||||
<div
|
||||
class="sectionNoticeContent"
|
||||
>
|
||||
<i
|
||||
class="icon sectionNoticeIcon icon-information-outline info"
|
||||
/>
|
||||
<div
|
||||
class="sectionNoticeBody"
|
||||
>
|
||||
<h4
|
||||
class="sectionNoticeTitle"
|
||||
>
|
||||
actionTitle
|
||||
</h4>
|
||||
<p>
|
||||
actionText
|
||||
</p>
|
||||
<div
|
||||
class="sectionNoticeActions"
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm sectionNoticeButton btn-primary"
|
||||
>
|
||||
buttonText
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-dark first"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="section 1Title"
|
||||
>
|
||||
section 1
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="section 1Title section 1Edit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="section 1Edit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="section 1Desc"
|
||||
>
|
||||
Option 0
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="section 2Title"
|
||||
>
|
||||
section 2
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="section 2Title section 2Edit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="section 2Edit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="section 2Desc"
|
||||
>
|
||||
Option 1
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="divider-dark"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {screen} from '@testing-library/react';
|
||||
import {shallow} from 'enzyme';
|
||||
import type {ComponentProps} from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext} from 'tests/react_testing_utils';
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
|
||||
import PluginTab from './index';
|
||||
|
||||
|
|
@ -90,8 +88,8 @@ function CustomSectionThrows() {
|
|||
|
||||
describe('plugin tab', () => {
|
||||
it('all props are properly passed to the children', () => {
|
||||
const wrapper = shallow(<PluginTab {...getBaseProps()}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<PluginTab {...getBaseProps()}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('setting name is properly set', () => {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,185 +1,267 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`MfaSection rendering should render nothing when MFA is not available 1`] = `""`;
|
||||
exports[`MfaSection rendering should render nothing when MFA is not available 1`] = `<div />`;
|
||||
|
||||
exports[`MfaSection rendering when section is collapsed and MFA is active 1`] = `
|
||||
<SettingItemMin
|
||||
describe={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Active"
|
||||
id="user.settings.security.active"
|
||||
/>
|
||||
}
|
||||
section="mfa"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="mfaTitle"
|
||||
>
|
||||
Multi-factor Authentication
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="mfaTitle mfaEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="mfaEdit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="mfaDesc"
|
||||
>
|
||||
Active
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MfaSection rendering when section is collapsed and MFA is not active 1`] = `
|
||||
<SettingItemMin
|
||||
describe={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Inactive"
|
||||
id="user.settings.security.inactive"
|
||||
/>
|
||||
}
|
||||
section="mfa"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="section-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="mfaTitle"
|
||||
>
|
||||
Multi-factor Authentication
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="mfaTitle mfaEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="mfaEdit"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="mfaDesc"
|
||||
>
|
||||
Inactive
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MfaSection rendering when section is expanded and MFA is active and enforced 1`] = `
|
||||
<Memo(SettingItemMax)
|
||||
extraInfo={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor authentication is required on this server. Resetting is only recommended when you need to switch code generation to a new mobile device. You will be required to set it up again immediately."
|
||||
id="user.settings.mfa.requiredHelp"
|
||||
/>
|
||||
}
|
||||
inputs={
|
||||
<div
|
||||
className="pt-2"
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
<a
|
||||
className="btn btn-primary"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
Multi-factor Authentication
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Reset MFA on Account"
|
||||
id="user.settings.mfa.reset"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<div
|
||||
class="pt-2"
|
||||
>
|
||||
<a
|
||||
class="btn btn-primary a11y--active a11y--focused"
|
||||
href="#"
|
||||
>
|
||||
Reset MFA on Account
|
||||
</a>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
class="setting-list__hint"
|
||||
id="extraInfo"
|
||||
>
|
||||
Multi-factor authentication is required on this server. Resetting is only recommended when you need to switch code generation to a new mobile device. You will be required to set it up again immediately.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<div
|
||||
role="alert"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
data-testid="cancelButton"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
serverError={null}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MfaSection rendering when section is expanded and MFA is active but not enforced 1`] = `
|
||||
<Memo(SettingItemMax)
|
||||
extraInfo={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Removing multi-factor authentication means you will no longer require a phone-based passcode to sign-in to your account."
|
||||
id="user.settings.mfa.removeHelp"
|
||||
/>
|
||||
}
|
||||
inputs={
|
||||
<div
|
||||
className="pt-2"
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
<a
|
||||
className="btn btn-primary"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
Multi-factor Authentication
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Remove MFA from Account"
|
||||
id="user.settings.mfa.remove"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<div
|
||||
class="pt-2"
|
||||
>
|
||||
<a
|
||||
class="btn btn-primary a11y--active a11y--focused"
|
||||
href="#"
|
||||
>
|
||||
Remove MFA from Account
|
||||
</a>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
class="setting-list__hint"
|
||||
id="extraInfo"
|
||||
>
|
||||
Removing multi-factor authentication means you will no longer require a phone-based passcode to sign-in to your account.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<div
|
||||
role="alert"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
data-testid="cancelButton"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
serverError={null}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MfaSection rendering when section is expanded and MFA is not active 1`] = `
|
||||
<Memo(SettingItemMax)
|
||||
extraInfo={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Adding multi-factor authentication will make your account more secure by requiring a code from your mobile phone each time you sign in."
|
||||
id="user.settings.mfa.addHelp"
|
||||
/>
|
||||
}
|
||||
inputs={
|
||||
<div
|
||||
className="pt-2"
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
<a
|
||||
className="btn btn-primary"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
Multi-factor Authentication
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Add MFA to Account"
|
||||
id="user.settings.mfa.add"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<div
|
||||
class="pt-2"
|
||||
>
|
||||
<a
|
||||
class="btn btn-primary a11y--active a11y--focused"
|
||||
href="#"
|
||||
>
|
||||
Add MFA to Account
|
||||
</a>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
class="setting-list__hint"
|
||||
id="extraInfo"
|
||||
>
|
||||
Adding multi-factor authentication will make your account more secure by requiring a code from your mobile phone each time you sign in.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<div
|
||||
role="alert"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
data-testid="cancelButton"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
serverError={null}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MfaSection rendering when section is expanded with a server error 1`] = `
|
||||
<Memo(SettingItemMax)
|
||||
extraInfo={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Adding multi-factor authentication will make your account more secure by requiring a code from your mobile phone each time you sign in."
|
||||
id="user.settings.mfa.addHelp"
|
||||
/>
|
||||
}
|
||||
inputs={
|
||||
<div
|
||||
className="pt-2"
|
||||
>
|
||||
<a
|
||||
className="btn btn-primary"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Add MFA to Account"
|
||||
id="user.settings.mfa.add"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
serverError="An error has occurred"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Multi-factor Authentication"
|
||||
id="user.settings.mfa.title"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
/>
|
||||
<span
|
||||
id="serverError"
|
||||
>
|
||||
An error has occurred
|
||||
</span>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@
|
|||
|
||||
jest.mock('utils/browser_history');
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import MfaSection from 'components/user_settings/security/mfa_section/mfa_section';
|
||||
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {act} from 'tests/react_testing_utils';
|
||||
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
|
||||
describe('MfaSection', () => {
|
||||
|
|
@ -31,9 +29,9 @@ describe('MfaSection', () => {
|
|||
...baseProps,
|
||||
mfaAvailable: false,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is collapsed and MFA is not active', () => {
|
||||
|
|
@ -42,9 +40,9 @@ describe('MfaSection', () => {
|
|||
active: false,
|
||||
mfaActive: false,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is collapsed and MFA is active', () => {
|
||||
|
|
@ -53,9 +51,9 @@ describe('MfaSection', () => {
|
|||
active: false,
|
||||
mfaActive: true,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is expanded and MFA is not active', () => {
|
||||
|
|
@ -63,9 +61,9 @@ describe('MfaSection', () => {
|
|||
...baseProps,
|
||||
mfaActive: false,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is expanded and MFA is active but not enforced', () => {
|
||||
|
|
@ -73,9 +71,9 @@ describe('MfaSection', () => {
|
|||
...baseProps,
|
||||
mfaActive: true,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is expanded and MFA is active and enforced', () => {
|
||||
|
|
@ -84,33 +82,36 @@ describe('MfaSection', () => {
|
|||
mfaActive: true,
|
||||
mfaEnforced: true,
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
const {container} = renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when section is expanded with a server error', () => {
|
||||
test('when section is expanded with a server error', async () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
serverError: 'An error occurred',
|
||||
mfaActive: true,
|
||||
actions: {
|
||||
deactivateMfa: jest.fn(() => Promise.resolve({error: {message: 'An error has occurred'}})),
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(<MfaSection {...props}/>);
|
||||
renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
wrapper.setState({serverError: 'An error has occurred'});
|
||||
await userEvent.click(screen.getByText('Remove MFA from Account'));
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('An error has occurred')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByText('An error has occurred')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setupMfa', () => {
|
||||
it('should send to setup page', () => {
|
||||
const wrapper = mountWithIntl(<MfaSection {...baseProps}/>);
|
||||
it('should send to setup page', async () => {
|
||||
renderWithContext(<MfaSection {...baseProps}/>);
|
||||
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
} as unknown as React.MouseEvent<HTMLElement>;
|
||||
|
||||
(wrapper.instance() as MfaSection).setupMfa(mockEvent);
|
||||
await userEvent.click(screen.getByText('Add MFA to Account'));
|
||||
|
||||
expect(getHistory().push).toHaveBeenCalledWith('/mfa/setup');
|
||||
});
|
||||
|
|
@ -118,62 +119,58 @@ describe('MfaSection', () => {
|
|||
|
||||
describe('removeMfa', () => {
|
||||
it('on success, should close section and clear state', async () => {
|
||||
const wrapper = mountWithIntl(<MfaSection {...baseProps}/>);
|
||||
const props = {
|
||||
...baseProps,
|
||||
mfaActive: true,
|
||||
};
|
||||
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
} as unknown as React.MouseEvent<HTMLElement>;
|
||||
renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
act(() => {
|
||||
wrapper.setState({serverError: 'An error has occurred'});
|
||||
await userEvent.click(screen.getByText('Remove MFA from Account'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(baseProps.updateSection).toHaveBeenCalledWith('');
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await (wrapper.instance() as MfaSection).removeMfa(mockEvent);
|
||||
});
|
||||
|
||||
expect(baseProps.updateSection).toHaveBeenCalledWith('');
|
||||
expect(wrapper.state('serverError')).toEqual(null);
|
||||
expect(screen.queryByText(/error/i)).not.toBeInTheDocument();
|
||||
expect(getHistory().push).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('on success, should send to setup page if MFA enforcement is enabled', async () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
mfaActive: true,
|
||||
mfaEnforced: true,
|
||||
};
|
||||
|
||||
const wrapper = mountWithIntl(<MfaSection {...props}/>);
|
||||
renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
} as unknown as React.MouseEvent<HTMLElement>;
|
||||
await userEvent.click(screen.getByText('Reset MFA on Account'));
|
||||
|
||||
await act(async () => {
|
||||
await (wrapper.instance() as MfaSection).removeMfa(mockEvent);
|
||||
await waitFor(() => {
|
||||
expect(getHistory().push).toHaveBeenCalledWith('/mfa/setup');
|
||||
});
|
||||
|
||||
expect(baseProps.updateSection).not.toHaveBeenCalled();
|
||||
expect(getHistory().push).toHaveBeenCalledWith('/mfa/setup');
|
||||
});
|
||||
|
||||
it('on error, should show error', async () => {
|
||||
const error = {message: 'An error occurred'};
|
||||
|
||||
const wrapper = mountWithIntl(<MfaSection {...baseProps}/>);
|
||||
const props = {
|
||||
...baseProps,
|
||||
mfaActive: true,
|
||||
actions: {
|
||||
deactivateMfa: jest.fn(() => Promise.resolve({error})),
|
||||
},
|
||||
};
|
||||
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
} as unknown as React.MouseEvent<HTMLElement>;
|
||||
renderWithContext(<MfaSection {...props}/>);
|
||||
|
||||
baseProps.actions.deactivateMfa.mockImplementation(() => Promise.resolve({error}));
|
||||
await userEvent.click(screen.getByText('Remove MFA from Account'));
|
||||
|
||||
await act(async () => {
|
||||
await (wrapper.instance() as MfaSection).removeMfa(mockEvent);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('An error occurred')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(baseProps.updateSection).not.toHaveBeenCalled();
|
||||
expect(wrapper.state('serverError')).toEqual(error.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import type {OAuthApp} from '@mattermost/types/integrations';
|
||||
|
|
@ -9,19 +8,24 @@ import type {UserProfile} from '@mattermost/types/users';
|
|||
|
||||
import type {PasswordConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import type {MockIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {renderWithContext, screen, userEvent, waitFor, fireEvent} from 'tests/react_testing_utils';
|
||||
import Constants from 'utils/constants';
|
||||
|
||||
import {SecurityTab} from './user_settings_security';
|
||||
import SecurityTab from './user_settings_security';
|
||||
|
||||
jest.mock('utils/password', () => {
|
||||
const original = jest.requireActual('utils/password');
|
||||
return {...original, isValidPassword: () => ({valid: true})};
|
||||
});
|
||||
|
||||
jest.mock('./mfa_section', () => () => <div data-testid='mfa-section'/>);
|
||||
jest.mock('./user_access_token_section', () => () => <div data-testid='tokens-section'/>);
|
||||
|
||||
describe('components/user_settings/display/UserSettingsDisplay', () => {
|
||||
const user = {
|
||||
id: 'user_id',
|
||||
auth_service: '',
|
||||
last_password_update: 1234567890000,
|
||||
};
|
||||
|
||||
const requiredProps = {
|
||||
|
|
@ -49,37 +53,34 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
experimentalEnableAuthenticationTransfer: true,
|
||||
passwordConfig: {} as PasswordConfig,
|
||||
militaryTime: false,
|
||||
intl: {
|
||||
formatMessage: jest.fn(({id, defaultMessage}) => defaultMessage || id),
|
||||
} as MockIntl,
|
||||
};
|
||||
|
||||
test('should match snapshot, enable google', () => {
|
||||
const props = {...requiredProps, enableSaml: false};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<SecurityTab {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, enable gitlab', () => {
|
||||
const props = {...requiredProps, enableSignUpWithGoogle: false, enableSaml: false, enableSignUpWithGitLab: true};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<SecurityTab {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, enable office365', () => {
|
||||
const props = {...requiredProps, enableSignUpWithGoogle: false, enableSaml: false, enableSignUpWithOffice365: true};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<SecurityTab {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, enable openID', () => {
|
||||
const props = {...requiredProps, enableSignUpWithGoogle: false, enableSaml: false, enableSignUpWithOpenId: true};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<SecurityTab {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, to email', () => {
|
||||
|
|
@ -90,56 +91,64 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
|
||||
const props = {...requiredProps, user: user as UserProfile};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<SecurityTab {...props}/>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('componentDidMount() should have called getAuthorizedOAuthApps', () => {
|
||||
const props = {...requiredProps, enableOAuthServiceProvider: true};
|
||||
|
||||
shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
expect(requiredProps.actions.getAuthorizedOAuthApps).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('componentDidMount() should have updated state.authorizedApps', async () => {
|
||||
const apps = [{name: 'app1'}];
|
||||
const promise = Promise.resolve({data: apps});
|
||||
const getAuthorizedOAuthApps = () => promise;
|
||||
const apps = [{id: 'app1', name: 'app1'}];
|
||||
const getAuthorizedOAuthApps = jest.fn().mockResolvedValue({data: apps});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, getAuthorizedOAuthApps},
|
||||
enableOAuthServiceProvider: true,
|
||||
activeSection: 'apps',
|
||||
};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
await promise;
|
||||
|
||||
expect(wrapper.state().authorizedApps).toEqual(apps);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('app1')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('componentDidMount() should have updated state.serverError', async () => {
|
||||
const error = {message: 'error'};
|
||||
const promise = Promise.resolve({error});
|
||||
const getAuthorizedOAuthApps = () => promise;
|
||||
const getAuthorizedOAuthApps = jest.fn().mockResolvedValue({error});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, getAuthorizedOAuthApps},
|
||||
enableOAuthServiceProvider: true,
|
||||
activeSection: 'apps',
|
||||
};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
await promise;
|
||||
|
||||
expect(wrapper.state('serverError')).toEqual(error.message);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('submitPassword() should not have called updateUserPassword', async () => {
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...requiredProps}/>);
|
||||
test('submitPassword() should not have called updateUserPassword', () => {
|
||||
const props = {
|
||||
...requiredProps,
|
||||
activeSection: 'password',
|
||||
};
|
||||
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
// Save button is disabled because fields are empty (isValid is false)
|
||||
const saveButton = screen.getByText('Save');
|
||||
fireEvent.click(saveButton);
|
||||
|
||||
await wrapper.instance().submitPassword();
|
||||
expect(requiredProps.actions.updateUserPassword).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
|
|
@ -148,40 +157,50 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, updateUserPassword},
|
||||
activeSection: 'password',
|
||||
};
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
const password = 'psw';
|
||||
const state = {
|
||||
currentPassword: 'currentPassword',
|
||||
newPassword: password,
|
||||
confirmPassword: password,
|
||||
};
|
||||
wrapper.setState(state);
|
||||
|
||||
await wrapper.instance().submitPassword();
|
||||
await userEvent.type(screen.getByLabelText('Current Password'), 'currentPassword');
|
||||
await userEvent.type(screen.getByLabelText('New Password'), password);
|
||||
await userEvent.type(screen.getByLabelText('Retype New Password'), password);
|
||||
|
||||
expect(updateUserPassword).toHaveBeenCalled();
|
||||
await userEvent.click(screen.getByText('Save'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateUserPassword).toHaveBeenCalled();
|
||||
});
|
||||
expect(updateUserPassword).toHaveBeenCalledWith(
|
||||
user.id,
|
||||
state.currentPassword,
|
||||
state.newPassword,
|
||||
'currentPassword',
|
||||
password,
|
||||
);
|
||||
|
||||
expect(requiredProps.updateSection).toHaveBeenCalled();
|
||||
expect(requiredProps.updateSection).toHaveBeenCalledWith('');
|
||||
});
|
||||
|
||||
test('deauthorizeApp() should have called deauthorizeOAuthApp', () => {
|
||||
test('deauthorizeApp() should have called deauthorizeOAuthApp', async () => {
|
||||
const appId = 'appId';
|
||||
const event: any = {
|
||||
currentTarget: {getAttribute: jest.fn().mockReturnValue(appId)},
|
||||
preventDefault: jest.fn(),
|
||||
const apps = [{id: appId, name: 'TestApp', homepage: 'http://test.com', description: 'test', icon_url: ''}] as OAuthApp[];
|
||||
const getAuthorizedOAuthApps = jest.fn().mockResolvedValue({data: apps});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, getAuthorizedOAuthApps},
|
||||
enableOAuthServiceProvider: true,
|
||||
activeSection: 'apps',
|
||||
};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...requiredProps}/>);
|
||||
wrapper.setState({authorizedApps: []});
|
||||
wrapper.instance().deauthorizeApp(event);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Deauthorize')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await userEvent.click(screen.getByText('Deauthorize'));
|
||||
|
||||
expect(requiredProps.actions.deauthorizeOAuthApp).toHaveBeenCalled();
|
||||
expect(requiredProps.actions.deauthorizeOAuthApp).toHaveBeenCalledWith(
|
||||
|
|
@ -190,46 +209,54 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
|||
});
|
||||
|
||||
test('deauthorizeApp() should have updated state.authorizedApps', async () => {
|
||||
const promise = Promise.resolve({data: true});
|
||||
const appId = 'appId';
|
||||
const apps = [{id: appId, name: 'App1', homepage: 'http://test.com', description: '', icon_url: ''}, {id: '2', name: 'App2', homepage: 'http://test2.com', description: '', icon_url: ''}] as OAuthApp[];
|
||||
const deauthorizeOAuthApp = jest.fn().mockResolvedValue({data: true});
|
||||
const getAuthorizedOAuthApps = jest.fn().mockResolvedValue({data: apps});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, deauthorizeOAuthApp: () => promise},
|
||||
actions: {...requiredProps.actions, deauthorizeOAuthApp, getAuthorizedOAuthApps},
|
||||
enableOAuthServiceProvider: true,
|
||||
activeSection: 'apps',
|
||||
};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
const appId = 'appId';
|
||||
const apps = [{id: appId}, {id: '2'}] as OAuthApp[];
|
||||
const event: any = {
|
||||
currentTarget: {getAttribute: jest.fn().mockReturnValue(appId)},
|
||||
preventDefault: jest.fn(),
|
||||
};
|
||||
wrapper.setState({authorizedApps: apps});
|
||||
wrapper.instance().deauthorizeApp(event);
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByText('Deauthorize')).toHaveLength(2);
|
||||
});
|
||||
|
||||
await promise;
|
||||
await userEvent.click(screen.getAllByText('Deauthorize')[0]);
|
||||
|
||||
expect(wrapper.state().authorizedApps).toEqual(apps.slice(1));
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('App1')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('App2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('deauthorizeApp() should have updated state.serverError', async () => {
|
||||
const appId = 'appId';
|
||||
const apps = [{id: appId, name: 'TestApp', homepage: 'http://test.com', description: '', icon_url: ''}] as OAuthApp[];
|
||||
const error = {message: 'error'};
|
||||
const promise = Promise.resolve({error});
|
||||
const deauthorizeOAuthApp = jest.fn().mockResolvedValue({error});
|
||||
const getAuthorizedOAuthApps = jest.fn().mockResolvedValue({data: apps});
|
||||
const props = {
|
||||
...requiredProps,
|
||||
actions: {...requiredProps.actions, deauthorizeOAuthApp: () => promise},
|
||||
actions: {...requiredProps.actions, deauthorizeOAuthApp, getAuthorizedOAuthApps},
|
||||
enableOAuthServiceProvider: true,
|
||||
activeSection: 'apps',
|
||||
};
|
||||
|
||||
const wrapper = shallow<SecurityTab>(<SecurityTab {...props}/>);
|
||||
renderWithContext(<SecurityTab {...props}/>);
|
||||
|
||||
const event: any = {
|
||||
currentTarget: {getAttribute: jest.fn().mockReturnValue('appId')},
|
||||
preventDefault: jest.fn(),
|
||||
};
|
||||
wrapper.instance().deauthorizeApp(event);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Deauthorize')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await promise;
|
||||
await userEvent.click(screen.getByText('Deauthorize'));
|
||||
|
||||
expect(wrapper.state('serverError')).toEqual(error.message);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue