mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Remove access to global state from some files (#26752)
* admin_console/license_settings/trial_banner * invitation_modal and associated utils * overlay trigger * Change TrialBanner to not use makeGetCategory * Address feedback * Fixing unit tests D: * Address further feedback * Fix one last test
This commit is contained in:
parent
7429ddaf04
commit
22d72b6df8
@ -161,7 +161,6 @@
|
|||||||
"jest-cli": "29.7.0",
|
"jest-cli": "29.7.0",
|
||||||
"jest-environment-jsdom": "29.7.0",
|
"jest-environment-jsdom": "29.7.0",
|
||||||
"jest-junit": "16.0.0",
|
"jest-junit": "16.0.0",
|
||||||
"jest-styled-components": "7.2.0",
|
|
||||||
"jest-watch-typeahead": "2.2.2",
|
"jest-watch-typeahead": "2.2.2",
|
||||||
"mmjstool": "github:mattermost/mattermost-utilities#73e61d2ede0ebf802492df4cfbac481d35efed54",
|
"mmjstool": "github:mattermost/mattermost-utilities#73e61d2ede0ebf802492df4cfbac481d35efed54",
|
||||||
"nock": "13.2.8",
|
"nock": "13.2.8",
|
||||||
|
@ -6,14 +6,10 @@ import type {ReactNode} from 'react';
|
|||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
import type {PreferenceType} from '@mattermost/types/preferences';
|
|
||||||
|
|
||||||
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||||
import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
|
import {getBool as getBoolPreference} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
|
||||||
import store from 'stores/redux_store';
|
|
||||||
|
|
||||||
import AlertBanner from 'components/alert_banner';
|
import AlertBanner from 'components/alert_banner';
|
||||||
import withOpenStartTrialFormModal from 'components/common/hocs/cloud/with_open_start_trial_form_modal';
|
import withOpenStartTrialFormModal from 'components/common/hocs/cloud/with_open_start_trial_form_modal';
|
||||||
import type {TelemetryProps} from 'components/common/hooks/useOpenPricingModal';
|
import type {TelemetryProps} from 'components/common/hooks/useOpenPricingModal';
|
||||||
@ -93,14 +89,9 @@ const TrialBanner = ({
|
|||||||
let gettingTrialErrorMsg;
|
let gettingTrialErrorMsg;
|
||||||
|
|
||||||
const {formatMessage} = useIntl();
|
const {formatMessage} = useIntl();
|
||||||
const state = store.getState();
|
|
||||||
const getCategory = makeGetCategory();
|
|
||||||
const preferences = getCategory(state, Preferences.UNIQUE);
|
|
||||||
const restartedAfterUpgradePrefValue = preferences.find((pref: PreferenceType) => pref.name === Unique.REQUEST_TRIAL_AFTER_SERVER_UPGRADE);
|
|
||||||
const clickedUpgradeAndStartTrialBtn = preferences.find((pref: PreferenceType) => pref.name === Unique.CLICKED_UPGRADE_AND_TRIAL_BTN);
|
|
||||||
|
|
||||||
const restartedAfterUpgradePrefs = restartedAfterUpgradePrefValue?.value === 'true';
|
const restartedAfterUpgradePrefs = useSelector<GlobalState>((state) => getBoolPreference(state, Preferences.UNIQUE, Unique.REQUEST_TRIAL_AFTER_SERVER_UPGRADE));
|
||||||
const clickedUpgradeAndTrialBtn = clickedUpgradeAndStartTrialBtn?.value === 'true';
|
const clickedUpgradeAndTrialBtn = useSelector<GlobalState>((state) => getBoolPreference(state, Preferences.UNIQUE, Unique.CLICKED_UPGRADE_AND_TRIAL_BTN));
|
||||||
|
|
||||||
const userId = useSelector((state: GlobalState) => getCurrentUserId(state));
|
const userId = useSelector((state: GlobalState) => getCurrentUserId(state));
|
||||||
|
|
||||||
|
@ -1,335 +1,79 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`components/app_bar/app_bar should match snapshot on mount 1`] = `
|
exports[`components/app_bar/app_bar should match snapshot on mount 1`] = `
|
||||||
<AppBar>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
className="app-bar"
|
class="app-bar"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="app-bar__top"
|
class="app-bar__top"
|
||||||
>
|
>
|
||||||
<AppBarPluginComponent
|
<div
|
||||||
component={
|
class="app-bar__icon"
|
||||||
Object {
|
id="app-bar-icon-playbooks"
|
||||||
"action": [MockFunction],
|
|
||||||
"icon": "fallback_component",
|
|
||||||
"id": "the_component_id",
|
|
||||||
"pluginId": "playbooks",
|
|
||||||
"tooltipText": "Playbooks Tooltip",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key="the_component_id"
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<div
|
||||||
defaultOverlayShown={false}
|
class="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
||||||
delayShow={400}
|
role="button"
|
||||||
overlay={
|
tabindex="0"
|
||||||
<Tooltip
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="pluginTooltip-app-bar-icon-playbooks"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Playbooks Tooltip
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
fallback_component
|
||||||
defaultOverlayShown={false}
|
</div>
|
||||||
delayShow={400}
|
</div>
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="pluginTooltip-app-bar-icon-playbooks"
|
|
||||||
intl={null}
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Playbooks Tooltip
|
|
||||||
</span>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__icon"
|
|
||||||
id="app-bar-icon-playbooks"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
fallback_component
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</AppBarPluginComponent>
|
|
||||||
<hr
|
<hr
|
||||||
className="app-bar__divider"
|
class="app-bar__divider"
|
||||||
key="divider"
|
|
||||||
/>
|
/>
|
||||||
<AppBarBinding
|
<div
|
||||||
binding={
|
aria-label="Create Subscription"
|
||||||
Object {
|
class="app-bar__icon"
|
||||||
"app_id": "com.mattermost.zendesk",
|
id="app-bar-icon-com.mattermost.zendesk"
|
||||||
"label": "Create Subscription",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key="com.mattermost.zendesk_Create Subscription"
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<div
|
||||||
defaultOverlayShown={false}
|
class="app-bar__icon-inner"
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="tooltip-app-bar-icon-com.mattermost.zendesk"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Create Subscription
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<img />
|
||||||
defaultOverlayShown={false}
|
</div>
|
||||||
delayShow={400}
|
</div>
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="tooltip-app-bar-icon-com.mattermost.zendesk"
|
|
||||||
intl={null}
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Create Subscription
|
|
||||||
</span>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
aria-label="Create Subscription"
|
|
||||||
className="app-bar__icon"
|
|
||||||
id="app-bar-icon-com.mattermost.zendesk"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__icon-inner"
|
|
||||||
>
|
|
||||||
<img />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</AppBarBinding>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppBar>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/app_bar/app_bar should match snapshot on mount when App Bar is disabled 1`] = `
|
exports[`components/app_bar/app_bar should match snapshot on mount when App Bar is disabled 1`] = `
|
||||||
<AppBar>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
className="app-bar"
|
class="app-bar"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="app-bar__top"
|
class="app-bar__top"
|
||||||
>
|
>
|
||||||
<AppBarPluginComponent
|
<div
|
||||||
component={
|
class="app-bar__icon"
|
||||||
Object {
|
id="app-bar-icon-playbooks"
|
||||||
"action": [MockFunction],
|
|
||||||
"icon": "fallback_component",
|
|
||||||
"id": "the_component_id",
|
|
||||||
"pluginId": "playbooks",
|
|
||||||
"tooltipText": "Playbooks Tooltip",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key="the_component_id"
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<div
|
||||||
defaultOverlayShown={false}
|
class="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
||||||
delayShow={400}
|
role="button"
|
||||||
overlay={
|
tabindex="0"
|
||||||
<Tooltip
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="pluginTooltip-app-bar-icon-playbooks"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Playbooks Tooltip
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
fallback_component
|
||||||
defaultOverlayShown={false}
|
</div>
|
||||||
delayShow={400}
|
</div>
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="pluginTooltip-app-bar-icon-playbooks"
|
|
||||||
intl={null}
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Playbooks Tooltip
|
|
||||||
</span>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__icon"
|
|
||||||
id="app-bar-icon-playbooks"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
fallback_component
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</AppBarPluginComponent>
|
|
||||||
<hr
|
<hr
|
||||||
className="app-bar__divider"
|
class="app-bar__divider"
|
||||||
key="divider"
|
|
||||||
/>
|
/>
|
||||||
<AppBarBinding
|
<div
|
||||||
binding={
|
aria-label="Create Subscription"
|
||||||
Object {
|
class="app-bar__icon"
|
||||||
"app_id": "com.mattermost.zendesk",
|
id="app-bar-icon-com.mattermost.zendesk"
|
||||||
"label": "Create Subscription",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key="com.mattermost.zendesk_Create Subscription"
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<div
|
||||||
defaultOverlayShown={false}
|
class="app-bar__icon-inner"
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="tooltip-app-bar-icon-com.mattermost.zendesk"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Create Subscription
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<img />
|
||||||
defaultOverlayShown={false}
|
</div>
|
||||||
delayShow={400}
|
</div>
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
bsClass="tooltip"
|
|
||||||
id="tooltip-app-bar-icon-com.mattermost.zendesk"
|
|
||||||
intl={null}
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Create Subscription
|
|
||||||
</span>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
aria-label="Create Subscription"
|
|
||||||
className="app-bar__icon"
|
|
||||||
id="app-bar-icon-com.mattermost.zendesk"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="app-bar__icon-inner"
|
|
||||||
>
|
|
||||||
<img />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</AppBarBinding>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppBar>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {mount, shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type {AppBinding} from '@mattermost/types/apps';
|
import type {AppBinding} from '@mattermost/types/apps';
|
||||||
@ -9,100 +8,15 @@ import type {AppBinding} from '@mattermost/types/apps';
|
|||||||
import {Permissions} from 'mattermost-redux/constants';
|
import {Permissions} from 'mattermost-redux/constants';
|
||||||
import {AppBindingLocations} from 'mattermost-redux/constants/apps';
|
import {AppBindingLocations} from 'mattermost-redux/constants/apps';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
|
||||||
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
|
import {TestHelper} from 'utils/test_helper';
|
||||||
|
|
||||||
import type {PluginComponent} from 'types/store/plugins';
|
import type {PluginComponent} from 'types/store/plugins';
|
||||||
|
|
||||||
import AppBar from './app_bar';
|
import AppBar from './app_bar';
|
||||||
|
|
||||||
import 'jest-styled-components';
|
|
||||||
|
|
||||||
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('react-router-dom', () => ({
|
|
||||||
...jest.requireActual('react-router-dom') as typeof import('react-router-dom'),
|
|
||||||
useLocation: () => {
|
|
||||||
return {
|
|
||||||
pathname: '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('components/app_bar/app_bar', () => {
|
describe('components/app_bar/app_bar', () => {
|
||||||
beforeEach(() => {
|
|
||||||
mockState = {
|
|
||||||
views: {
|
|
||||||
rhs: {
|
|
||||||
isSidebarOpen: true,
|
|
||||||
rhsState: 'plugin',
|
|
||||||
pluggableId: 'the_rhs_plugin_component',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
components: {
|
|
||||||
AppBar: channelHeaderComponents,
|
|
||||||
RightHandSidebarComponent: rhsComponents,
|
|
||||||
Product: [],
|
|
||||||
} as {[componentName: string]: PluginComponent[]},
|
|
||||||
},
|
|
||||||
entities: {
|
|
||||||
apps: {
|
|
||||||
main: {
|
|
||||||
bindings: channelHeaderAppBindings,
|
|
||||||
} as {bindings: AppBinding[]},
|
|
||||||
pluginEnabled: true,
|
|
||||||
},
|
|
||||||
general: {
|
|
||||||
config: {
|
|
||||||
DisableAppBar: 'false',
|
|
||||||
FeatureFlagAppsEnabled: 'true',
|
|
||||||
} as any,
|
|
||||||
},
|
|
||||||
channels: {
|
|
||||||
currentChannelId: 'currentchannel',
|
|
||||||
channels: {
|
|
||||||
currentchannel: {
|
|
||||||
id: 'currentchannel',
|
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
myMembers: {
|
|
||||||
currentchannel: {
|
|
||||||
id: 'memberid',
|
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
},
|
|
||||||
teams: {
|
|
||||||
currentTeamId: 'currentteam',
|
|
||||||
},
|
|
||||||
preferences: {
|
|
||||||
myPreferences: {
|
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
users: {
|
|
||||||
currentUserId: 'user1',
|
|
||||||
profiles: {
|
|
||||||
user1: {
|
|
||||||
roles: 'system_user',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
roles: {
|
|
||||||
roles: {
|
|
||||||
system_user: {
|
|
||||||
permissions: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
},
|
|
||||||
} as GlobalState;
|
|
||||||
});
|
|
||||||
|
|
||||||
const channelHeaderComponents: PluginComponent[] = [
|
const channelHeaderComponents: PluginComponent[] = [
|
||||||
{
|
{
|
||||||
id: 'the_component_id',
|
id: 'the_component_id',
|
||||||
@ -134,65 +48,141 @@ describe('components/app_bar/app_bar', () => {
|
|||||||
},
|
},
|
||||||
] as AppBinding[];
|
] as AppBinding[];
|
||||||
|
|
||||||
test('should match snapshot on mount', async () => {
|
const initialState = {
|
||||||
const wrapper = mount(
|
views: {
|
||||||
<AppBar/>,
|
rhs: {
|
||||||
);
|
isSidebarOpen: true,
|
||||||
|
rhsState: 'plugin',
|
||||||
expect(wrapper).toMatchSnapshot();
|
pluggableId: 'the_rhs_plugin_component',
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot on mount when App Bar is disabled', async () => {
|
|
||||||
mockState.entities.general.config.DisableAppBar = 'false';
|
|
||||||
|
|
||||||
const wrapper = mount(
|
|
||||||
<AppBar/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not show marketplace if disabled or user does not have SYSCONSOLE_WRITE_PLUGINS permission', async () => {
|
|
||||||
mockState.entities.general = {
|
|
||||||
config: {
|
|
||||||
DisableAppBar: 'true',
|
|
||||||
FeatureFlagAppsEnabled: 'true',
|
|
||||||
EnableMarketplace: 'true',
|
|
||||||
PluginsEnabled: 'true',
|
|
||||||
},
|
},
|
||||||
} as any;
|
},
|
||||||
|
plugins: {
|
||||||
const wrapper = shallow(
|
components: {
|
||||||
<AppBar/>,
|
AppBar: channelHeaderComponents,
|
||||||
);
|
RightHandSidebarComponent: rhsComponents,
|
||||||
|
Product: [],
|
||||||
expect(wrapper.find('AppBarMarketplace').exists()).toEqual(false);
|
} as {[componentName: string]: PluginComponent[]},
|
||||||
});
|
},
|
||||||
|
entities: {
|
||||||
test('should show marketplace if enabled and user has SYSCONSOLE_WRITE_PLUGINS permission', async () => {
|
apps: {
|
||||||
mockState.entities.general = {
|
main: {
|
||||||
config: {
|
bindings: channelHeaderAppBindings,
|
||||||
DisableAppBar: 'false',
|
} as {bindings: AppBinding[]},
|
||||||
FeatureFlagAppsEnabled: 'true',
|
pluginEnabled: true,
|
||||||
EnableMarketplace: 'true',
|
|
||||||
PluginsEnabled: 'true',
|
|
||||||
},
|
},
|
||||||
} as any;
|
general: {
|
||||||
|
config: {
|
||||||
mockState.entities.roles = {
|
DisableAppBar: 'false',
|
||||||
roles: {
|
FeatureFlagAppsEnabled: 'true',
|
||||||
system_user: {
|
|
||||||
permissions: [
|
|
||||||
Permissions.SYSCONSOLE_WRITE_PLUGINS,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as any;
|
channels: {
|
||||||
|
currentChannelId: 'currentchannel',
|
||||||
|
channels: {
|
||||||
|
currentchannel: TestHelper.getChannelMock({
|
||||||
|
id: 'currentchannel',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
currentchannel: TestHelper.getChannelMembershipMock({
|
||||||
|
channel_id: 'currentchannel',
|
||||||
|
user_id: 'user1',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
teams: {
|
||||||
|
currentTeamId: 'currentteam',
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
currentUserId: 'user1',
|
||||||
|
profiles: {
|
||||||
|
user1: TestHelper.getUserMock({
|
||||||
|
roles: 'system_user',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const wrapper = shallow(
|
test('should match snapshot on mount', () => {
|
||||||
|
const testState = initialState;
|
||||||
|
const {asFragment} = renderWithContext(
|
||||||
<AppBar/>,
|
<AppBar/>,
|
||||||
|
testState,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('AppBarMarketplace').exists()).toEqual(true);
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should match snapshot on mount when App Bar is disabled', () => {
|
||||||
|
const testState = mergeObjects(initialState, {
|
||||||
|
entities: {
|
||||||
|
general: {
|
||||||
|
config: {
|
||||||
|
DisableAppbar: 'false',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {asFragment} = renderWithContext(
|
||||||
|
<AppBar/>,
|
||||||
|
testState,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show marketplace if disabled or user does not have SYSCONSOLE_WRITE_PLUGINS permission', () => {
|
||||||
|
const testState = mergeObjects(initialState, {
|
||||||
|
entities: {
|
||||||
|
general: {
|
||||||
|
config: {
|
||||||
|
DisableAppBar: 'true',
|
||||||
|
FeatureFlagAppsEnabled: 'true',
|
||||||
|
EnableMarketplace: 'true',
|
||||||
|
PluginsEnabled: 'true',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithContext(
|
||||||
|
<AppBar/>,
|
||||||
|
testState,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText('App Marketplace')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show marketplace if enabled and user has SYSCONSOLE_WRITE_PLUGINS permission', () => {
|
||||||
|
const testState = mergeObjects(initialState, {
|
||||||
|
entities: {
|
||||||
|
general: {
|
||||||
|
config: {
|
||||||
|
DisableAppBar: 'false',
|
||||||
|
FeatureFlagAppsEnabled: 'true',
|
||||||
|
EnableMarketplace: 'true',
|
||||||
|
PluginsEnabled: 'true',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
roles: {
|
||||||
|
system_user: {
|
||||||
|
permissions: [
|
||||||
|
Permissions.SYSCONSOLE_WRITE_PLUGINS,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithContext(
|
||||||
|
<AppBar/>,
|
||||||
|
testState,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText('App Marketplace')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, {useRef, useState} from 'react';
|
import React, {useRef, useState} from 'react';
|
||||||
import {Tooltip} from 'react-bootstrap';
|
import {Tooltip} from 'react-bootstrap';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
|
|
||||||
import OverlayTrigger from 'components/overlay_trigger';
|
import OverlayTrigger from 'components/overlay_trigger';
|
||||||
|
|
||||||
@ -21,6 +21,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CopyButton: React.FC<Props> = (props: Props) => {
|
const CopyButton: React.FC<Props> = (props: Props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
@ -74,16 +76,16 @@ const CopyButton: React.FC<Props> = (props: Props) => {
|
|||||||
<span
|
<span
|
||||||
className={spanClassName}
|
className={spanClassName}
|
||||||
onClick={copyText}
|
onClick={copyText}
|
||||||
|
aria-label={intl.formatMessage({id: getId(), defaultMessage: getDefaultMessage()})}
|
||||||
|
role='button'
|
||||||
>
|
>
|
||||||
{!isCopied &&
|
{!isCopied &&
|
||||||
<i
|
<i
|
||||||
role='button'
|
|
||||||
className='icon icon-content-copy'
|
className='icon icon-content-copy'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{isCopied &&
|
{isCopied &&
|
||||||
<i
|
<i
|
||||||
role='button'
|
|
||||||
className='icon icon-check'
|
className='icon icon-check'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,10 @@
|
|||||||
|
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {Button} from 'react-bootstrap';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {Provider} from 'react-redux';
|
|
||||||
|
|
||||||
import TeamUrl from 'components/create_team/components/team_url/team_url';
|
import TeamUrl from 'components/create_team/components/team_url/team_url';
|
||||||
|
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
|
||||||
import mockStore from 'tests/test_store';
|
|
||||||
import Constants from 'utils/constants';
|
import Constants from 'utils/constants';
|
||||||
|
|
||||||
jest.mock('images/logo.png', () => 'logo.png');
|
jest.mock('images/logo.png', () => 'logo.png');
|
||||||
@ -30,31 +26,20 @@ describe('/components/create_team/components/display_name', () => {
|
|||||||
history: {push: jest.fn()},
|
history: {push: jest.fn()},
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatLengthError = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='create_team.team_url.charLength'
|
|
||||||
defaultMessage='Name must be {min} or more characters up to a maximum of {max}'
|
|
||||||
values={{
|
|
||||||
min: Constants.MIN_TEAMNAME_LENGTH,
|
|
||||||
max: Constants.MAX_TEAMNAME_LENGTH,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
test('should match snapshot', () => {
|
test('should match snapshot', () => {
|
||||||
const wrapper = shallow(<TeamUrl {...defaultProps}/>);
|
const wrapper = shallow(<TeamUrl {...defaultProps}/>);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return to display_name.jsx page', () => {
|
test('should return to display_name.jsx page', async () => {
|
||||||
const wrapper = mountWithIntl(<TeamUrl {...defaultProps}/>);
|
renderWithContext(<TeamUrl {...defaultProps}/>);
|
||||||
|
|
||||||
wrapper.find('a').simulate('click', {
|
screen.getByText('Back to previous step').click();
|
||||||
preventDefault: () => jest.fn(),
|
|
||||||
|
expect(defaultProps.updateParent).toHaveBeenCalledWith({
|
||||||
|
...defaultProps.state,
|
||||||
|
wizard: 'display_name',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.prop('state').wizard).toBe('display_name');
|
|
||||||
expect(wrapper.prop('updateParent')).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should successfully submit', async () => {
|
test('should successfully submit', async () => {
|
||||||
@ -65,81 +50,80 @@ describe('/components/create_team/components/display_name', () => {
|
|||||||
const actions = {...defaultProps.actions, checkIfTeamExists};
|
const actions = {...defaultProps.actions, checkIfTeamExists};
|
||||||
const props = {...defaultProps, actions};
|
const props = {...defaultProps, actions};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
renderWithContext(
|
||||||
<TeamUrl {...props}/>,
|
<TeamUrl {...props}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
await (wrapper.instance() as unknown as TeamUrl).submitNext({preventDefault: jest.fn()} as unknown as React.MouseEvent<Button, MouseEvent>);
|
screen.getByText('Finish').click();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('This URL is taken or unavailable. Please try another.')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
expect(actions.checkIfTeamExists).toHaveBeenCalledTimes(1);
|
expect(actions.checkIfTeamExists).toHaveBeenCalledTimes(1);
|
||||||
expect(actions.createTeam).not.toHaveBeenCalled();
|
expect(actions.createTeam).not.toHaveBeenCalled();
|
||||||
|
|
||||||
await (wrapper.instance() as unknown as TeamUrl).submitNext({preventDefault: jest.fn()} as unknown as React.MouseEvent<Button, MouseEvent>);
|
screen.getByText('Finish').click();
|
||||||
expect(actions.checkIfTeamExists).toHaveBeenCalledTimes(2);
|
|
||||||
expect(actions.createTeam).toHaveBeenCalledTimes(1);
|
await waitFor(() => {
|
||||||
expect(actions.createTeam).toBeCalledWith({display_name: 'test-team', name: 'test-team', type: 'O'});
|
expect(actions.checkIfTeamExists).toHaveBeenCalledTimes(2);
|
||||||
expect(props.history.push).toHaveBeenCalledTimes(1);
|
expect(actions.createTeam).toHaveBeenCalledTimes(1);
|
||||||
expect(props.history.push).toBeCalledWith('/test-team/channels/town-square');
|
expect(actions.createTeam).toBeCalledWith({display_name: 'test-team', name: 'test-team', type: 'O'});
|
||||||
|
expect(props.history.push).toHaveBeenCalledTimes(1);
|
||||||
|
expect(props.history.push).toBeCalledWith('/test-team/channels/town-square');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display isRequired error', () => {
|
test('should display isRequired error', () => {
|
||||||
const wrapper = mountWithIntl(<TeamUrl {...defaultProps}/>);
|
renderWithContext(
|
||||||
(wrapper.find('.form-control').instance() as unknown as HTMLInputElement).value = '';
|
<TeamUrl {...defaultProps}/>,
|
||||||
wrapper.find('.form-control').simulate('change');
|
|
||||||
wrapper.find('button').simulate('click', {preventDefault: () => jest.fn()});
|
|
||||||
|
|
||||||
expect(wrapper.state('nameError')).toEqual(
|
|
||||||
<FormattedMessage
|
|
||||||
id='create_team.team_url.required'
|
|
||||||
defaultMessage='This field is required'
|
|
||||||
/>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
userEvent.clear(screen.getByRole('textbox'));
|
||||||
|
screen.getByText('Finish').click();
|
||||||
|
|
||||||
|
expect(screen.getByText('This field is required')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display charLength error', () => {
|
test('should display charLength error', () => {
|
||||||
const wrapper = mountWithIntl(<TeamUrl {...defaultProps}/>);
|
const lengthError = `Name must be ${Constants.MIN_TEAMNAME_LENGTH} or more characters up to a maximum of ${Constants.MAX_TEAMNAME_LENGTH}`;
|
||||||
(wrapper.find('.form-control').instance() as unknown as HTMLInputElement).value = 'a';
|
|
||||||
wrapper.find('.form-control').simulate('change');
|
|
||||||
wrapper.find('button').simulate('click', {preventDefault: () => jest.fn()});
|
|
||||||
expect(wrapper.state('nameError')).toEqual(chatLengthError);
|
|
||||||
|
|
||||||
(wrapper.find('.form-control').instance() as unknown as HTMLInputElement).value = 'a'.repeat(Constants.MAX_TEAMNAME_LENGTH + 1);
|
renderWithContext(
|
||||||
wrapper.find('.form-control').simulate('change');
|
<TeamUrl {...defaultProps}/>,
|
||||||
wrapper.find('button').simulate('click', {preventDefault: () => jest.fn()});
|
);
|
||||||
expect(wrapper.state('nameError')).toEqual(chatLengthError);
|
|
||||||
|
expect(screen.queryByText(lengthError)).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.type(screen.getByRole('textbox'), 'a');
|
||||||
|
screen.getByText('Finish').click();
|
||||||
|
|
||||||
|
expect(screen.getByText(lengthError)).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.type(screen.getByRole('textbox'), 'a'.repeat(Constants.MAX_TEAMNAME_LENGTH + 1));
|
||||||
|
screen.getByText('Finish').click();
|
||||||
|
|
||||||
|
expect(screen.getByText(lengthError)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display teamUrl regex error', () => {
|
test('should display teamUrl regex error', () => {
|
||||||
const wrapper = mountWithIntl(<TeamUrl {...defaultProps}/>);
|
renderWithContext(
|
||||||
(wrapper.find('.form-control').instance() as unknown as HTMLInputElement).value = '!!wrongName1';
|
<TeamUrl {...defaultProps}/>,
|
||||||
wrapper.find('.form-control').simulate('change');
|
|
||||||
wrapper.find('button').simulate('click', {preventDefault: () => jest.fn()});
|
|
||||||
expect(wrapper.state('nameError')).toEqual(
|
|
||||||
<FormattedMessage
|
|
||||||
id='create_team.team_url.regex'
|
|
||||||
defaultMessage="Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
|
|
||||||
/>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
userEvent.type(screen.getByRole('textbox'), '!!wrongName1');
|
||||||
|
screen.getByText('Finish').click();
|
||||||
|
|
||||||
|
expect(screen.getByText("Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash.")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display teamUrl taken error', () => {
|
test('should display teamUrl taken error', () => {
|
||||||
const store = mockStore({
|
renderWithContext(
|
||||||
entities: {
|
<TeamUrl {...defaultProps}/>,
|
||||||
general: {
|
);
|
||||||
config: {},
|
|
||||||
license: {
|
|
||||||
Cloud: 'false',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
currentUserId: 'currentUserId',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const wrapper = mountWithIntl(<Provider store={store}><TeamUrl {...defaultProps}/></Provider>);
|
userEvent.type(screen.getByRole('textbox'), 'channel');
|
||||||
(wrapper.find('.form-control').instance() as unknown as HTMLInputElement).value = 'channel';
|
screen.getByText('Finish').click();
|
||||||
wrapper.find('.form-control').simulate('change');
|
|
||||||
wrapper.find('button').simulate('click', {preventDefault: () => jest.fn()});
|
expect(screen.getByText('Please try another.', {exact: false})).toBeInTheDocument();
|
||||||
expect((wrapper as any).find(TeamUrl).state('nameError').props.id).toEqual('create_team.team_url.taken');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {IntlProvider} from 'react-intl';
|
|
||||||
|
|
||||||
import type {SystemEmoji} from '@mattermost/types/emojis';
|
import type {SystemEmoji} from '@mattermost/types/emojis';
|
||||||
|
|
||||||
import {render, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import EmojiMap from 'utils/emoji_map';
|
import EmojiMap from 'utils/emoji_map';
|
||||||
|
|
||||||
import EmojiPicker from './emoji_picker';
|
import EmojiPicker from './emoji_picker';
|
||||||
@ -19,11 +18,6 @@ jest.mock('components/emoji_picker/components/emoji_picker_preview', () => ({emo
|
|||||||
));
|
));
|
||||||
|
|
||||||
describe('components/emoji_picker/EmojiPicker', () => {
|
describe('components/emoji_picker/EmojiPicker', () => {
|
||||||
const intlProviderProps = {
|
|
||||||
defaultLocale: 'en',
|
|
||||||
locale: 'en',
|
|
||||||
};
|
|
||||||
|
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
filter: '',
|
filter: '',
|
||||||
visible: true,
|
visible: true,
|
||||||
@ -45,20 +39,16 @@ describe('components/emoji_picker/EmojiPicker', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('should match snapshot', () => {
|
test('should match snapshot', () => {
|
||||||
const {asFragment} = render(
|
const {asFragment} = renderWithContext(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<EmojiPicker {...baseProps}/>,
|
||||||
<EmojiPicker {...baseProps}/>
|
|
||||||
</IntlProvider>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Recent category should not exist if there are no recent emojis', () => {
|
test('Recent category should not exist if there are no recent emojis', () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<EmojiPicker {...baseProps}/>,
|
||||||
<EmojiPicker {...baseProps}/>
|
|
||||||
</IntlProvider>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.queryByLabelText('emoji_picker.recent')).toBeNull();
|
expect(screen.queryByLabelText('emoji_picker.recent')).toBeNull();
|
||||||
@ -70,10 +60,8 @@ describe('components/emoji_picker/EmojiPicker', () => {
|
|||||||
recentEmojis: ['smile'],
|
recentEmojis: ['smile'],
|
||||||
};
|
};
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<EmojiPicker {...props}/>,
|
||||||
<EmojiPicker {...props}/>
|
|
||||||
</IntlProvider>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.queryByLabelText('emoji_picker.recent')).not.toBeNull();
|
expect(screen.queryByLabelText('emoji_picker.recent')).not.toBeNull();
|
||||||
@ -85,10 +73,8 @@ describe('components/emoji_picker/EmojiPicker', () => {
|
|||||||
filter: 'wave',
|
filter: 'wave',
|
||||||
};
|
};
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<EmojiPicker {...props}/>,
|
||||||
<EmojiPicker {...props}/>
|
|
||||||
</IntlProvider>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.queryByText('Preview for wave emoji')).not.toBeNull();
|
expect(screen.queryByText('Preview for wave emoji')).not.toBeNull();
|
||||||
|
@ -22,7 +22,6 @@ exports[`components/file_attachment/FilenameOverlay should match snapshot, compa
|
|||||||
<a
|
<a
|
||||||
className="post-image__name"
|
className="post-image__name"
|
||||||
href="#"
|
href="#"
|
||||||
id="file-attachment-link"
|
|
||||||
onClick={[MockFunction]}
|
onClick={[MockFunction]}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
@ -7,7 +7,6 @@ import React from 'react';
|
|||||||
import type {GlobalState} from '@mattermost/types/store';
|
import type {GlobalState} from '@mattermost/types/store';
|
||||||
import type {DeepPartial} from '@mattermost/types/utilities';
|
import type {DeepPartial} from '@mattermost/types/utilities';
|
||||||
|
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
|
||||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
import FileAttachment from './file_attachment';
|
import FileAttachment from './file_attachment';
|
||||||
@ -168,15 +167,12 @@ describe('FileAttachment', () => {
|
|||||||
|
|
||||||
test('should blur file attachment link after click', () => {
|
test('should blur file attachment link after click', () => {
|
||||||
const props = {...baseProps, compactDisplay: true};
|
const props = {...baseProps, compactDisplay: true};
|
||||||
const wrapper = mountWithIntl(<FileAttachment {...props}/>);
|
renderWithContext(<FileAttachment {...props}/>);
|
||||||
const e = {
|
|
||||||
preventDefault: jest.fn(),
|
|
||||||
target: {blur: jest.fn()},
|
|
||||||
};
|
|
||||||
|
|
||||||
const a = wrapper.find('#file-attachment-link');
|
const link = screen.getByText(baseProps.fileInfo.name);
|
||||||
a.simulate('click', e);
|
const blur = jest.spyOn(link, 'blur');
|
||||||
expect(e.target.blur).toHaveBeenCalled();
|
screen.getByText(baseProps.fileInfo.name).click();
|
||||||
|
expect(blur).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('archived file', () => {
|
describe('archived file', () => {
|
||||||
|
@ -71,7 +71,6 @@ export default class FilenameOverlay extends React.PureComponent<Props> {
|
|||||||
overlay={<Tooltip id='file-name__tooltip'>{fileName}</Tooltip>}
|
overlay={<Tooltip id='file-name__tooltip'>{fileName}</Tooltip>}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
id='file-attachment-link'
|
|
||||||
href='#'
|
href='#'
|
||||||
onClick={handleImageClick}
|
onClick={handleImageClick}
|
||||||
className='post-image__name'
|
className='post-image__name'
|
||||||
|
@ -1,337 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions should match snapshot for external image with public links enabled 1`] = `
|
|
||||||
<div
|
|
||||||
className="file-preview-modal-main-actions__actions"
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="download"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="download-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Download"
|
|
||||||
id="view_image_popover.download"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ExternalLink
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
download="img.png"
|
|
||||||
href="http://example.com/img.png"
|
|
||||||
location="file_preview_modal_main_actions"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-download-outline"
|
|
||||||
/>
|
|
||||||
</ExternalLink>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="publicLink"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="close-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Close"
|
|
||||||
id="full_screen_modal.close"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions should match snapshot when copy content is enabled 1`] = `
|
|
||||||
<div
|
|
||||||
className="file-preview-modal-main-actions__actions"
|
|
||||||
>
|
|
||||||
<CopyButton
|
|
||||||
afterCopyText="Copied"
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
content="test content"
|
|
||||||
placement="bottom"
|
|
||||||
/>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="download"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="download-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Download"
|
|
||||||
id="view_image_popover.download"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ExternalLink
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
download="img.png"
|
|
||||||
href="http://example.com/img.png"
|
|
||||||
location="file_preview_modal_main_actions"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-download-outline"
|
|
||||||
/>
|
|
||||||
</ExternalLink>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="publicLink"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="close-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Close"
|
|
||||||
id="full_screen_modal.close"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions should match snapshot with public links disabled 1`] = `
|
|
||||||
<div
|
|
||||||
className="file-preview-modal-main-actions__actions"
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="download"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="download-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Download"
|
|
||||||
id="view_image_popover.download"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ExternalLink
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
download="img.png"
|
|
||||||
href="http://example.com/img.png"
|
|
||||||
location="file_preview_modal_main_actions"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-download-outline"
|
|
||||||
/>
|
|
||||||
</ExternalLink>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="publicLink"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="close-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Close"
|
|
||||||
id="full_screen_modal.close"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions should match snapshot with public links enabled 1`] = `
|
|
||||||
<div
|
|
||||||
className="file-preview-modal-main-actions__actions"
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="filePreviewPublicLink"
|
|
||||||
onExit={[Function]}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="link-variant-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Get a public link"
|
|
||||||
id="view_image_popover.publicLink"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
shouldUpdatePosition={true}
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
href="#"
|
|
||||||
onClick={[Function]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-link-variant"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="download"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="download-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Download"
|
|
||||||
id="view_image_popover.download"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ExternalLink
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
download="img.png"
|
|
||||||
href="http://example.com/img.png"
|
|
||||||
location="file_preview_modal_main_actions"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-download-outline"
|
|
||||||
/>
|
|
||||||
</ExternalLink>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
key="publicLink"
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="close-icon-tooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Close"
|
|
||||||
id="full_screen_modal.close"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions should match snapshot with public links enabled 2`] = `
|
|
||||||
<a
|
|
||||||
className="file-preview-modal-main-actions__action-item"
|
|
||||||
href="#"
|
|
||||||
onClick={[Function]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-link-variant"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
`;
|
|
@ -1,30 +1,17 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {mount, shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {ComponentProps} from 'react';
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
import * as fileActions from 'mattermost-redux/actions/files';
|
import * as fileActions from 'mattermost-redux/actions/files';
|
||||||
|
|
||||||
import OverlayTrigger from 'components/overlay_trigger';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import Tooltip from 'components/tooltip';
|
|
||||||
|
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
import * as Utils from 'utils/utils';
|
import * as Utils from 'utils/utils';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
|
||||||
|
|
||||||
import FilePreviewModalMainActions from './file_preview_modal_main_actions';
|
import FilePreviewModalMainActions from './file_preview_modal_main_actions';
|
||||||
|
|
||||||
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,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions', () => {
|
describe('components/file_preview_modal/file_preview_modal_main_actions/FilePreviewModalMainActions', () => {
|
||||||
let defaultProps: ComponentProps<typeof FilePreviewModalMainActions>;
|
let defaultProps: ComponentProps<typeof FilePreviewModalMainActions>;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -39,22 +26,6 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
content: 'test content',
|
content: 'test content',
|
||||||
canCopyContent: false,
|
canCopyContent: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
mockState = {
|
|
||||||
entities: {
|
|
||||||
general: {config: {}},
|
|
||||||
users: {profiles: {}},
|
|
||||||
channels: {channels: {}},
|
|
||||||
preferences: {
|
|
||||||
myPreferences: {
|
|
||||||
|
|
||||||
},
|
|
||||||
},
|
|
||||||
files: {
|
|
||||||
filePublicLink: {link: 'http://example.com/img.png'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as GlobalState;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot with public links disabled', () => {
|
test('should match snapshot with public links disabled', () => {
|
||||||
@ -63,8 +34,11 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
enablePublicLink: false,
|
enablePublicLink: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
expect(wrapper).toMatchSnapshot();
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText('Get a public link')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot with public links enabled', () => {
|
test('should match snapshot with public links enabled', () => {
|
||||||
@ -73,32 +47,38 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
enablePublicLink: true,
|
enablePublicLink: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
expect(wrapper).toMatchSnapshot();
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
const overlayWrapper = wrapper.find(OverlayTrigger).first();
|
);
|
||||||
expect(overlayWrapper.prop('overlay').type).toEqual(Tooltip);
|
|
||||||
expect(overlayWrapper.prop('children')).toMatchSnapshot();
|
expect(screen.queryByLabelText('Get a public link')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot for external image with public links enabled', () => {
|
test('should not show public link button for external image with public links enabled', () => {
|
||||||
const props = {
|
const props = {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
enablePublicLink: true,
|
enablePublicLink: true,
|
||||||
showPublicLink: false,
|
showPublicLink: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
expect(wrapper).toMatchSnapshot();
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText('Get a public link')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot when copy content is enabled', () => {
|
test('should show copy button when copy content is enabled', () => {
|
||||||
const props = {
|
const props = {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
canCopyContent: true,
|
canCopyContent: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
expect(wrapper).toMatchSnapshot();
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Copy code')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should call public link callback', () => {
|
test('should call public link callback', () => {
|
||||||
@ -107,17 +87,22 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
...defaultProps,
|
...defaultProps,
|
||||||
enablePublicLink: true,
|
enablePublicLink: true,
|
||||||
};
|
};
|
||||||
const wrapper = shallow(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
expect(wrapper.find(OverlayTrigger)).toHaveLength(3);
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
const overlayWrapper = wrapper.find(OverlayTrigger).first().children('a');
|
);
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledTimes(0);
|
expect(spy).toHaveBeenCalledTimes(0);
|
||||||
overlayWrapper.simulate('click');
|
|
||||||
|
screen.getByLabelText('Get a public link').click();
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledTimes(1);
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not get public api when public links is disabled', async () => {
|
test('should not get public api when public links is disabled', async () => {
|
||||||
const spy = jest.spyOn(fileActions, 'getFilePublicLink');
|
const spy = jest.spyOn(fileActions, 'getFilePublicLink');
|
||||||
mount(<FilePreviewModalMainActions {...defaultProps}/>);
|
renderWithContext(
|
||||||
|
<FilePreviewModalMainActions {...defaultProps}/>,
|
||||||
|
);
|
||||||
expect(spy).toHaveBeenCalledTimes(0);
|
expect(spy).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -127,7 +112,9 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
...defaultProps,
|
...defaultProps,
|
||||||
enablePublicLink: true,
|
enablePublicLink: true,
|
||||||
};
|
};
|
||||||
mount(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
|
);
|
||||||
expect(spy).toHaveBeenCalledTimes(1);
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,9 +124,11 @@ describe('components/file_preview_modal/file_preview_modal_main_actions/FilePrev
|
|||||||
...defaultProps,
|
...defaultProps,
|
||||||
canCopyContent: true,
|
canCopyContent: true,
|
||||||
};
|
};
|
||||||
const wrapper = mount(<FilePreviewModalMainActions {...props}/>);
|
renderWithContext(
|
||||||
|
<FilePreviewModalMainActions {...props}/>,
|
||||||
|
);
|
||||||
expect(spy).toHaveBeenCalledTimes(0);
|
expect(spy).toHaveBeenCalledTimes(0);
|
||||||
wrapper.find('.icon-content-copy').simulate('click');
|
screen.getByLabelText('Copy code').click();
|
||||||
expect(spy).toHaveBeenCalledTimes(1);
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {memo, useEffect, useState} from 'react';
|
import React, {memo, useEffect, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
import type {FileInfo} from '@mattermost/types/files';
|
import type {FileInfo} from '@mattermost/types/files';
|
||||||
@ -25,10 +25,6 @@ import type {LinkInfo} from '../types';
|
|||||||
|
|
||||||
import './file_preview_modal_main_actions.scss';
|
import './file_preview_modal_main_actions.scss';
|
||||||
|
|
||||||
interface DownloadLinkProps {
|
|
||||||
download?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
usedInside?: 'Header' | 'Footer';
|
usedInside?: 'Header' | 'Footer';
|
||||||
showOnlyClose?: boolean;
|
showOnlyClose?: boolean;
|
||||||
@ -45,6 +41,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const tooltipPlacement = props.usedInside === 'Header' ? 'bottom' : 'top';
|
const tooltipPlacement = props.usedInside === 'Header' ? 'bottom' : 'top';
|
||||||
const selectedFilePublicLink = useSelector((state: GlobalState) => selectFilePublicLink(state)?.link);
|
const selectedFilePublicLink = useSelector((state: GlobalState) => selectFilePublicLink(state)?.link);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -60,6 +58,10 @@ const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
|||||||
setPublicLinkCopied(true);
|
setPublicLinkCopied(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeMessage = intl.formatMessage({
|
||||||
|
id: 'full_screen_modal.close',
|
||||||
|
defaultMessage: 'Close',
|
||||||
|
});
|
||||||
const closeButton = (
|
const closeButton = (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||||
@ -67,34 +69,31 @@ const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
|||||||
placement={tooltipPlacement}
|
placement={tooltipPlacement}
|
||||||
overlay={
|
overlay={
|
||||||
<Tooltip id='close-icon-tooltip'>
|
<Tooltip id='close-icon-tooltip'>
|
||||||
<FormattedMessage
|
{closeMessage}
|
||||||
id='full_screen_modal.close'
|
|
||||||
defaultMessage='Close'
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className='file-preview-modal-main-actions__action-item'
|
className='file-preview-modal-main-actions__action-item'
|
||||||
onClick={props.handleModalClose}
|
onClick={props.handleModalClose}
|
||||||
|
aria-label={closeMessage}
|
||||||
>
|
>
|
||||||
<i className='icon icon-close'/>
|
<i className='icon icon-close'/>
|
||||||
</button>
|
</button>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
);
|
);
|
||||||
let publicTooltipMessage = (
|
|
||||||
<FormattedMessage
|
let publicTooltipMessage;
|
||||||
id='view_image_popover.publicLink'
|
|
||||||
defaultMessage='Get a public link'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (publicLinkCopied) {
|
if (publicLinkCopied) {
|
||||||
publicTooltipMessage = (
|
publicTooltipMessage = intl.formatMessage({
|
||||||
<FormattedMessage
|
id: 'file_preview_modal_main_actions.public_link-copied',
|
||||||
id='file_preview_modal_main_actions.public_link-copied'
|
defaultMessage: 'Public link copied',
|
||||||
defaultMessage='Public link copied'
|
});
|
||||||
/>
|
} else {
|
||||||
);
|
publicTooltipMessage = intl.formatMessage({
|
||||||
|
id: 'view_image_popover.publicLink',
|
||||||
|
defaultMessage: 'Get a public link',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const publicLink = (
|
const publicLink = (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
@ -113,13 +112,17 @@ const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
|||||||
href='#'
|
href='#'
|
||||||
className='file-preview-modal-main-actions__action-item'
|
className='file-preview-modal-main-actions__action-item'
|
||||||
onClick={copyPublicLink}
|
onClick={copyPublicLink}
|
||||||
|
aria-label={publicTooltipMessage}
|
||||||
>
|
>
|
||||||
<i className='icon icon-link-variant'/>
|
<i className='icon icon-link-variant'/>
|
||||||
</a>
|
</a>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
);
|
);
|
||||||
const downloadLinkProps: DownloadLinkProps = {};
|
|
||||||
downloadLinkProps.download = props.filename;
|
const downloadMessage = intl.formatMessage({
|
||||||
|
id: 'view_image_popover.download',
|
||||||
|
defaultMessage: 'Download',
|
||||||
|
});
|
||||||
const download = (
|
const download = (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||||
@ -127,10 +130,7 @@ const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
|||||||
placement={tooltipPlacement}
|
placement={tooltipPlacement}
|
||||||
overlay={
|
overlay={
|
||||||
<Tooltip id='download-icon-tooltip'>
|
<Tooltip id='download-icon-tooltip'>
|
||||||
<FormattedMessage
|
{downloadMessage}
|
||||||
id='view_image_popover.download'
|
|
||||||
defaultMessage='Download'
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -139,6 +139,7 @@ const FilePreviewModalMainActions: React.FC<Props> = (props: Props) => {
|
|||||||
className='file-preview-modal-main-actions__action-item'
|
className='file-preview-modal-main-actions__action-item'
|
||||||
location='file_preview_modal_main_actions'
|
location='file_preview_modal_main_actions'
|
||||||
download={props.filename}
|
download={props.filename}
|
||||||
|
aria-label={downloadMessage}
|
||||||
>
|
>
|
||||||
<i className='icon icon-download-outline'/>
|
<i className='icon icon-download-outline'/>
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
import {makeAsyncComponent} from 'components/async_load';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
|
|
||||||
import {Constants} from 'utils/constants';
|
import {Constants} from 'utils/constants';
|
||||||
|
import {getRoleForTrackFlow} from 'utils/utils';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
@ -86,6 +87,7 @@ export function mapStateToProps(state: GlobalState, props: OwnProps) {
|
|||||||
isAdmin: isAdmin(getCurrentUser(state).roles),
|
isAdmin: isAdmin(getCurrentUser(state).roles),
|
||||||
currentChannel,
|
currentChannel,
|
||||||
townSquareDisplayName,
|
townSquareDisplayName,
|
||||||
|
roleForTrackFlow: getRoleForTrackFlow(state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ import {Provider} from 'react-redux';
|
|||||||
|
|
||||||
import type {Team} from '@mattermost/types/teams';
|
import type {Team} from '@mattermost/types/teams';
|
||||||
|
|
||||||
|
import {General} from 'mattermost-redux/constants';
|
||||||
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
||||||
|
|
||||||
import store from 'stores/redux_store';
|
|
||||||
|
|
||||||
import {mountWithThemedIntl} from 'tests/helpers/themed-intl-test-helper';
|
import {mountWithThemedIntl} from 'tests/helpers/themed-intl-test-helper';
|
||||||
|
import mockStore from 'tests/test_store';
|
||||||
import {SelfHostedProducts} from 'utils/constants';
|
import {SelfHostedProducts} from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
import {generateId} from 'utils/utils';
|
import {generateId} from 'utils/utils';
|
||||||
@ -47,6 +47,7 @@ const defaultProps: Props = deepFreeze({
|
|||||||
intl: {} as IntlShape,
|
intl: {} as IntlShape,
|
||||||
townSquareDisplayName: '',
|
townSquareDisplayName: '',
|
||||||
onExited: jest.fn(),
|
onExited: jest.fn(),
|
||||||
|
roleForTrackFlow: {started_by_role: General.SYSTEM_USER_ROLE},
|
||||||
});
|
});
|
||||||
|
|
||||||
let props = defaultProps;
|
let props = defaultProps;
|
||||||
@ -107,7 +108,7 @@ describe('InvitationModal', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
store.getState = () => (state);
|
const store = mockStore(state);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
props = defaultProps;
|
props = defaultProps;
|
||||||
|
@ -17,8 +17,6 @@ import {isEmail} from 'mattermost-redux/utils/helpers';
|
|||||||
|
|
||||||
import {trackEvent} from 'actions/telemetry_actions';
|
import {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
import {getRoleForTrackFlow} from 'utils/utils';
|
|
||||||
|
|
||||||
import {InviteType} from './invite_as';
|
import {InviteType} from './invite_as';
|
||||||
import InviteView, {initializeInviteState} from './invite_view';
|
import InviteView, {initializeInviteState} from './invite_view';
|
||||||
import type {InviteState} from './invite_view';
|
import type {InviteState} from './invite_view';
|
||||||
@ -73,6 +71,7 @@ export type Props = {
|
|||||||
channelToInvite?: Channel;
|
channelToInvite?: Channel;
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
inviteAsGuest?: boolean;
|
inviteAsGuest?: boolean;
|
||||||
|
roleForTrackFlow: {started_by_role: string};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const View = {
|
export const View = {
|
||||||
@ -162,12 +161,11 @@ export class InvitationModal extends React.PureComponent<Props, State> {
|
|||||||
if (!this.props.currentTeam) {
|
if (!this.props.currentTeam) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const roleForTrackFlow = getRoleForTrackFlow();
|
|
||||||
const inviteAs = this.state.invite.inviteType;
|
const inviteAs = this.state.invite.inviteType;
|
||||||
if (inviteAs === InviteType.MEMBER && this.props.isCloud) {
|
if (inviteAs === InviteType.MEMBER && this.props.isCloud) {
|
||||||
trackEvent('cloud_invite_users', 'click_send_invitations', {num_invitations: this.state.invite.usersEmails.length, ...roleForTrackFlow});
|
trackEvent('cloud_invite_users', 'click_send_invitations', {num_invitations: this.state.invite.usersEmails.length, ...this.props.roleForTrackFlow});
|
||||||
}
|
}
|
||||||
trackEvent('invite_users', 'click_invite', roleForTrackFlow);
|
trackEvent('invite_users', 'click_invite', this.props.roleForTrackFlow);
|
||||||
|
|
||||||
const users: UserProfile[] = [];
|
const users: UserProfile[] = [];
|
||||||
const emails: string[] = [];
|
const emails: string[] = [];
|
||||||
|
@ -9,9 +9,8 @@ import type {Team} from '@mattermost/types/teams';
|
|||||||
|
|
||||||
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
||||||
|
|
||||||
import store from 'stores/redux_store';
|
|
||||||
|
|
||||||
import {mountWithThemedIntl} from 'tests/helpers/themed-intl-test-helper';
|
import {mountWithThemedIntl} from 'tests/helpers/themed-intl-test-helper';
|
||||||
|
import mockStore from 'tests/test_store';
|
||||||
import {SelfHostedProducts} from 'utils/constants';
|
import {SelfHostedProducts} from 'utils/constants';
|
||||||
import {TestHelper as TH} from 'utils/test_helper';
|
import {TestHelper as TH} from 'utils/test_helper';
|
||||||
import {generateId} from 'utils/utils';
|
import {generateId} from 'utils/utils';
|
||||||
@ -120,7 +119,7 @@ describe('InviteView', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
store.getState = () => (state);
|
const store = mockStore(state);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
props = defaultProps;
|
props = defaultProps;
|
||||||
|
@ -5,6 +5,7 @@ import classNames from 'classnames';
|
|||||||
import React, {useEffect, useMemo} from 'react';
|
import React, {useEffect, useMemo} from 'react';
|
||||||
import {Modal} from 'react-bootstrap';
|
import {Modal} from 'react-bootstrap';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
|
import {useSelector} from 'react-redux';
|
||||||
|
|
||||||
import type {Channel} from '@mattermost/types/channels';
|
import type {Channel} from '@mattermost/types/channels';
|
||||||
import type {Team} from '@mattermost/types/teams';
|
import type {Team} from '@mattermost/types/teams';
|
||||||
@ -76,6 +77,9 @@ export type Props = InviteState & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function InviteView(props: Props) {
|
export default function InviteView(props: Props) {
|
||||||
|
const trackFlowRole = useSelector(getTrackFlowRole);
|
||||||
|
const roleForTrackFlow = useSelector(getRoleForTrackFlow);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.currentTeam.invite_id) {
|
if (!props.currentTeam.invite_id) {
|
||||||
props.regenerateTeamInviteId(props.currentTeam.id);
|
props.regenerateTeamInviteId(props.currentTeam.id);
|
||||||
@ -85,11 +89,11 @@ export default function InviteView(props: Props) {
|
|||||||
const {formatMessage} = useIntl();
|
const {formatMessage} = useIntl();
|
||||||
|
|
||||||
const inviteURL = useMemo(() => {
|
const inviteURL = useMemo(() => {
|
||||||
return `${getSiteURL()}/signup_user_complete/?id=${props.currentTeam.invite_id}&md=link&sbr=${getTrackFlowRole()}`;
|
return `${getSiteURL()}/signup_user_complete/?id=${props.currentTeam.invite_id}&md=link&sbr=${trackFlowRole}`;
|
||||||
}, [props.currentTeam.invite_id]);
|
}, [props.currentTeam.invite_id, trackFlowRole]);
|
||||||
|
|
||||||
const copyText = useCopyText({
|
const copyText = useCopyText({
|
||||||
trackCallback: () => trackEvent(getAnalyticsCategory(props.isAdmin), 'click_copy_invite_link', {...getRoleForTrackFlow(), ...getSourceForTrackFlow()}),
|
trackCallback: () => trackEvent(getAnalyticsCategory(props.isAdmin), 'click_copy_invite_link', {...roleForTrackFlow, ...getSourceForTrackFlow()}),
|
||||||
text: inviteURL,
|
text: inviteURL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {act} from 'react-dom/test-utils';
|
import {act} from 'react-dom/test-utils';
|
||||||
|
|
||||||
|
import type {DeepPartial} from '@mattermost/types/utilities';
|
||||||
|
|
||||||
import {createChannel} from 'mattermost-redux/actions/channels';
|
import {createChannel} from 'mattermost-redux/actions/channels';
|
||||||
import Permissions from 'mattermost-redux/constants/permissions';
|
import Permissions from 'mattermost-redux/constants/permissions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
render,
|
|
||||||
renderWithContext,
|
renderWithContext,
|
||||||
screen,
|
screen,
|
||||||
userEvent,
|
userEvent,
|
||||||
@ -23,97 +24,89 @@ import NewChannelModal from './new_channel_modal';
|
|||||||
|
|
||||||
jest.mock('mattermost-redux/actions/channels');
|
jest.mock('mattermost-redux/actions/channels');
|
||||||
|
|
||||||
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,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('components/new_channel_modal', () => {
|
describe('components/new_channel_modal', () => {
|
||||||
beforeEach(() => {
|
const initialState: DeepPartial<GlobalState> = {
|
||||||
mockState = {
|
entities: {
|
||||||
entities: {
|
general: {
|
||||||
general: {
|
config: {},
|
||||||
config: {},
|
},
|
||||||
},
|
channels: {
|
||||||
|
currentChannelId: 'current_channel_id',
|
||||||
channels: {
|
channels: {
|
||||||
currentChannelId: 'current_channel_id',
|
current_channel_id: {
|
||||||
channels: {
|
id: 'current_channel_id',
|
||||||
current_channel_id: {
|
display_name: 'Current channel',
|
||||||
id: 'current_channel_id',
|
name: 'current_channel',
|
||||||
display_name: 'Current channel',
|
|
||||||
name: 'current_channel',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
current_channel_id: [
|
|
||||||
'channel_user',
|
|
||||||
'channel_admin',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
teams: {
|
|
||||||
currentTeamId: 'current_team_id',
|
|
||||||
myMembers: {
|
|
||||||
current_team_id: {
|
|
||||||
roles: 'team_user team_admin',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
teams: {
|
|
||||||
current_team_id: {
|
|
||||||
id: 'current_team_id',
|
|
||||||
description: 'Curent team description',
|
|
||||||
name: 'current-team',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
preferences: {
|
|
||||||
myPreferences: {},
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
currentUserId: 'current_user_id',
|
|
||||||
profiles: {
|
|
||||||
current_user_id: {roles: 'system_admin system_user'},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
roles: {
|
roles: {
|
||||||
roles: {
|
current_channel_id: new Set([
|
||||||
channel_admin: {
|
'channel_user',
|
||||||
permissions: [],
|
'channel_admin',
|
||||||
},
|
]),
|
||||||
channel_user: {
|
},
|
||||||
permissions: [],
|
},
|
||||||
},
|
teams: {
|
||||||
team_admin: {
|
currentTeamId: 'current_team_id',
|
||||||
permissions: [],
|
myMembers: {
|
||||||
},
|
current_team_id: {
|
||||||
team_user: {
|
roles: 'team_user team_admin',
|
||||||
permissions: [
|
},
|
||||||
Permissions.CREATE_PRIVATE_CHANNEL,
|
},
|
||||||
],
|
teams: {
|
||||||
},
|
current_team_id: {
|
||||||
system_admin: {
|
id: 'current_team_id',
|
||||||
permissions: [
|
description: 'Curent team description',
|
||||||
Permissions.CREATE_PUBLIC_CHANNEL,
|
name: 'current-team',
|
||||||
],
|
|
||||||
},
|
|
||||||
system_user: {
|
|
||||||
permissions: [],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
preferences: {
|
||||||
plugins: {focalboard: {id: suitePluginIds.focalboard}},
|
myPreferences: {},
|
||||||
},
|
},
|
||||||
} as unknown as GlobalState;
|
users: {
|
||||||
});
|
currentUserId: 'current_user_id',
|
||||||
|
profiles: {
|
||||||
|
current_user_id: {roles: 'system_admin system_user'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
roles: {
|
||||||
|
channel_admin: {
|
||||||
|
permissions: [],
|
||||||
|
},
|
||||||
|
channel_user: {
|
||||||
|
permissions: [],
|
||||||
|
},
|
||||||
|
team_admin: {
|
||||||
|
permissions: [],
|
||||||
|
},
|
||||||
|
team_user: {
|
||||||
|
permissions: [
|
||||||
|
Permissions.CREATE_PRIVATE_CHANNEL,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
system_admin: {
|
||||||
|
permissions: [
|
||||||
|
Permissions.CREATE_PUBLIC_CHANNEL,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
system_user: {
|
||||||
|
permissions: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {focalboard: {id: suitePluginIds.focalboard}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
test('should match component state with given props', () => {
|
test('should match component state with given props', () => {
|
||||||
render(<NewChannelModal/>);
|
renderWithContext(
|
||||||
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
|
);
|
||||||
|
|
||||||
const heading = screen.getByRole('heading');
|
const heading = screen.getByRole('heading');
|
||||||
expect(heading).toBeInTheDocument();
|
expect(heading).toBeInTheDocument();
|
||||||
@ -172,8 +165,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
test('should handle display name change', () => {
|
test('should handle display name change', () => {
|
||||||
const value = 'Channel name';
|
const value = 'Channel name';
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change display name
|
// Change display name
|
||||||
@ -196,8 +190,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
|
|
||||||
const url = 'channel-name-new';
|
const url = 'channel-name-new';
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change display name
|
// Change display name
|
||||||
@ -235,8 +230,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should handle type changes', () => {
|
test('should handle type changes', () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change type to private
|
// Change type to private
|
||||||
@ -261,8 +257,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
test('should handle purpose changes', () => {
|
test('should handle purpose changes', () => {
|
||||||
const value = 'Purpose';
|
const value = 'Purpose';
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change purpose
|
// Change purpose
|
||||||
@ -277,8 +274,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should enable confirm button when having valid display name, url and type', () => {
|
test('should enable confirm button when having valid display name, url and type', () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Confirm button should be disabled
|
// Confirm button should be disabled
|
||||||
@ -304,8 +302,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should disable confirm button when display name in error', () => {
|
test('should disable confirm button when display name in error', () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change display name
|
// Change display name
|
||||||
@ -333,8 +332,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should disable confirm button when url in error', () => {
|
test('should disable confirm button when url in error', () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change display name
|
// Change display name
|
||||||
@ -369,8 +369,9 @@ describe('components/new_channel_modal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should disable confirm button when server error', async () => {
|
test('should disable confirm button when server error', async () => {
|
||||||
render(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Confirm button should be disabled
|
// Confirm button should be disabled
|
||||||
@ -406,6 +407,7 @@ describe('components/new_channel_modal', () => {
|
|||||||
|
|
||||||
renderWithContext(
|
renderWithContext(
|
||||||
<NewChannelModal/>,
|
<NewChannelModal/>,
|
||||||
|
initialState,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Confirm button should be disabled
|
// Confirm button should be disabled
|
||||||
|
@ -5,7 +5,10 @@ import {mount} from 'enzyme';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {OverlayTrigger as BaseOverlayTrigger} from 'react-bootstrap'; // eslint-disable-line no-restricted-imports
|
import {OverlayTrigger as BaseOverlayTrigger} from 'react-bootstrap'; // eslint-disable-line no-restricted-imports
|
||||||
import {FormattedMessage, IntlProvider} from 'react-intl';
|
import {FormattedMessage, IntlProvider} from 'react-intl';
|
||||||
|
import {Provider as ReduxProvider} from 'react-redux';
|
||||||
|
import type {Store} from 'redux';
|
||||||
|
|
||||||
|
import testConfigureStore from 'packages/mattermost-redux/test/test_store';
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||||
|
|
||||||
import OverlayTrigger from './overlay_trigger';
|
import OverlayTrigger from './overlay_trigger';
|
||||||
@ -13,6 +16,8 @@ import OverlayTrigger from './overlay_trigger';
|
|||||||
describe('OverlayTrigger', () => {
|
describe('OverlayTrigger', () => {
|
||||||
const testId = 'test.value';
|
const testId = 'test.value';
|
||||||
|
|
||||||
|
let store: Store;
|
||||||
|
|
||||||
const intlProviderProps = {
|
const intlProviderProps = {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
@ -33,6 +38,7 @@ describe('OverlayTrigger', () => {
|
|||||||
let originalConsoleError: () => void;
|
let originalConsoleError: () => void;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
store = testConfigureStore();
|
||||||
originalConsoleError = console.error;
|
originalConsoleError = console.error;
|
||||||
console.error = jest.fn();
|
console.error = jest.fn();
|
||||||
});
|
});
|
||||||
@ -43,11 +49,13 @@ describe('OverlayTrigger', () => {
|
|||||||
|
|
||||||
test('base OverlayTrigger should fail to pass intl to overlay', () => {
|
test('base OverlayTrigger should fail to pass intl to overlay', () => {
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<ReduxProvider store={store}>
|
||||||
<BaseOverlayTrigger {...baseProps}>
|
<IntlProvider {...intlProviderProps}>
|
||||||
<span/>
|
<BaseOverlayTrigger {...baseProps}>
|
||||||
</BaseOverlayTrigger>
|
<span/>
|
||||||
</IntlProvider>,
|
</BaseOverlayTrigger>
|
||||||
|
</IntlProvider>
|
||||||
|
</ReduxProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// console.error will have been called by FormattedMessage because its intl context is missing
|
// console.error will have been called by FormattedMessage because its intl context is missing
|
||||||
@ -58,11 +66,13 @@ describe('OverlayTrigger', () => {
|
|||||||
|
|
||||||
test('custom OverlayTrigger should pass intl to overlay', () => {
|
test('custom OverlayTrigger should pass intl to overlay', () => {
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<ReduxProvider store={store}>
|
||||||
<OverlayTrigger {...baseProps}>
|
<IntlProvider {...intlProviderProps}>
|
||||||
<span/>
|
<OverlayTrigger {...baseProps}>
|
||||||
</OverlayTrigger>
|
<span/>
|
||||||
</IntlProvider>,
|
</OverlayTrigger>
|
||||||
|
</IntlProvider>
|
||||||
|
</ReduxProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const overlay = mount(wrapper.find(BaseOverlayTrigger).prop('overlay'));
|
const overlay = mount(wrapper.find(BaseOverlayTrigger).prop('overlay'));
|
||||||
@ -79,11 +89,13 @@ describe('OverlayTrigger', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<ReduxProvider store={store}>
|
||||||
<OverlayTrigger {...props}>
|
<IntlProvider {...intlProviderProps}>
|
||||||
<span/>
|
<OverlayTrigger {...props}>
|
||||||
</OverlayTrigger>
|
<span/>
|
||||||
</IntlProvider>,
|
</OverlayTrigger>
|
||||||
|
</IntlProvider>
|
||||||
|
</ReduxProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(ref.current).toBe(wrapper.find(BaseOverlayTrigger).instance());
|
expect(ref.current).toBe(wrapper.find(BaseOverlayTrigger).instance());
|
||||||
@ -104,11 +116,13 @@ describe('OverlayTrigger', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<ReduxProvider store={store}>
|
||||||
<OverlayTrigger {...props}>
|
<IntlProvider {...intlProviderProps}>
|
||||||
<span/>
|
<OverlayTrigger {...props}>
|
||||||
</OverlayTrigger>
|
<span/>
|
||||||
</IntlProvider>,
|
</OverlayTrigger>
|
||||||
|
</IntlProvider>
|
||||||
|
</ReduxProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dive into the react-bootstrap internals to find our overlay
|
// Dive into the react-bootstrap internals to find our overlay
|
||||||
@ -143,11 +157,13 @@ describe('OverlayTrigger', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<IntlProvider {...intlProviderProps}>
|
<ReduxProvider store={store}>
|
||||||
<OverlayTrigger {...props}>
|
<IntlProvider {...intlProviderProps}>
|
||||||
<span/>
|
<OverlayTrigger {...props}>
|
||||||
</OverlayTrigger>
|
<span/>
|
||||||
</IntlProvider>,
|
</OverlayTrigger>
|
||||||
|
</IntlProvider>
|
||||||
|
</ReduxProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dive into the react-bootstrap internals to find our overlay
|
// Dive into the react-bootstrap internals to find our overlay
|
||||||
|
@ -6,9 +6,7 @@ import {OverlayTrigger as OriginalOverlayTrigger} from 'react-bootstrap'; // esl
|
|||||||
import type {OverlayTriggerProps} from 'react-bootstrap';
|
import type {OverlayTriggerProps} from 'react-bootstrap';
|
||||||
import {IntlContext} from 'react-intl';
|
import {IntlContext} from 'react-intl';
|
||||||
import type {IntlShape} from 'react-intl';
|
import type {IntlShape} from 'react-intl';
|
||||||
import {Provider} from 'react-redux';
|
import {Provider, useStore} from 'react-redux';
|
||||||
|
|
||||||
import store from 'stores/redux_store';
|
|
||||||
|
|
||||||
export type BaseOverlayTrigger = OriginalOverlayTrigger & {
|
export type BaseOverlayTrigger = OriginalOverlayTrigger & {
|
||||||
hide: () => void;
|
hide: () => void;
|
||||||
@ -25,6 +23,8 @@ type Props = OverlayTriggerProps & {
|
|||||||
const OverlayTrigger = React.forwardRef((props: Props, ref?: React.Ref<OriginalOverlayTrigger>) => {
|
const OverlayTrigger = React.forwardRef((props: Props, ref?: React.Ref<OriginalOverlayTrigger>) => {
|
||||||
const {overlay, disabled, ...otherProps} = props;
|
const {overlay, disabled, ...otherProps} = props;
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
// The overlay is rendered outside of the regular React context, and our version react-bootstrap can't forward
|
// The overlay is rendered outside of the regular React context, and our version react-bootstrap can't forward
|
||||||
// that context itself, so we have to manually forward the react-intl context to this component's child.
|
// that context itself, so we have to manually forward the react-intl context to this component's child.
|
||||||
const OverlayWrapper = ({intl, ...overlayProps}: {intl: IntlShape}) => (
|
const OverlayWrapper = ({intl, ...overlayProps}: {intl: IntlShape}) => (
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {render, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
import PostEmoji from './post_emoji';
|
import PostEmoji from './post_emoji';
|
||||||
|
|
||||||
@ -14,14 +14,14 @@ describe('PostEmoji', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('should render image when imageUrl is provided', () => {
|
test('should render image when imageUrl is provided', () => {
|
||||||
render(<PostEmoji {...baseProps}/>);
|
renderWithContext(<PostEmoji {...baseProps}/>);
|
||||||
|
|
||||||
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toBeInTheDocument();
|
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toHaveStyle(`backgroundImage: url(${baseProps.imageUrl})}`);
|
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toHaveStyle(`backgroundImage: url(${baseProps.imageUrl})}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render shortcode text within span when imageUrl is provided', () => {
|
test('should render shortcode text within span when imageUrl is provided', () => {
|
||||||
render(<PostEmoji {...baseProps}/>);
|
renderWithContext(<PostEmoji {...baseProps}/>);
|
||||||
|
|
||||||
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toHaveTextContent(`:${baseProps.name}:`);
|
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).toHaveTextContent(`:${baseProps.name}:`);
|
||||||
});
|
});
|
||||||
@ -32,7 +32,7 @@ describe('PostEmoji', () => {
|
|||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
render(<PostEmoji {...props}/>);
|
renderWithContext(<PostEmoji {...props}/>);
|
||||||
|
|
||||||
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('postEmoji.:' + baseProps.name + ':')).not.toBeInTheDocument();
|
||||||
expect(screen.getByText(`:${props.name}:`)).toBeInTheDocument();
|
expect(screen.getByText(`:${props.name}:`)).toBeInTheDocument();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {ComponentProps} from 'react';
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
import {render, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
|
|
||||||
import PostProfilePicture from './post_profile_picture';
|
import PostProfilePicture from './post_profile_picture';
|
||||||
@ -31,7 +31,7 @@ describe('components/PostProfilePicture', () => {
|
|||||||
|
|
||||||
test('no status and post icon override specified, default props', () => {
|
test('no status and post icon override specified, default props', () => {
|
||||||
const props: Props = baseProps;
|
const props: Props = baseProps;
|
||||||
render(
|
renderWithContext(
|
||||||
<PostProfilePicture {...props}/>,
|
<PostProfilePicture {...props}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ describe('components/PostProfilePicture', () => {
|
|||||||
status: 'away',
|
status: 'away',
|
||||||
postIconOverrideURL: 'http://example.com/image.png',
|
postIconOverrideURL: 'http://example.com/image.png',
|
||||||
};
|
};
|
||||||
render(
|
renderWithContext(
|
||||||
<PostProfilePicture {...props}/>,
|
<PostProfilePicture {...props}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,194 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/QuickInput should render clear button with customized tooltip component 1`] = `
|
|
||||||
<div
|
|
||||||
className="input-clear visible"
|
|
||||||
onMouseDown={[Function]}
|
|
||||||
onTouchEnd={[Function]}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="InputClearTooltip"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Custom
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
id="InputClearTooltip"
|
|
||||||
intl={null}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Custom
|
|
||||||
</span>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className="input-clear-x"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={null}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close-circle"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/QuickInput should render clear button with customized tooltip text 1`] = `
|
|
||||||
<div
|
|
||||||
className="input-clear visible"
|
|
||||||
onMouseDown={[Function]}
|
|
||||||
onTouchEnd={[Function]}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="InputClearTooltip"
|
|
||||||
>
|
|
||||||
Custom
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
id="InputClearTooltip"
|
|
||||||
intl={null}
|
|
||||||
>
|
|
||||||
Custom
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className="input-clear-x"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={null}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close-circle"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/QuickInput should render clear button with default tooltip text 1`] = `
|
|
||||||
<div
|
|
||||||
className="input-clear visible"
|
|
||||||
onMouseDown={[Function]}
|
|
||||||
onTouchEnd={[Function]}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<Tooltip
|
|
||||||
id="InputClearTooltip"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Clear"
|
|
||||||
id="input.clear"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<OverlayTrigger
|
|
||||||
defaultOverlayShown={false}
|
|
||||||
delayShow={400}
|
|
||||||
overlay={
|
|
||||||
<OverlayWrapper
|
|
||||||
id="InputClearTooltip"
|
|
||||||
intl={null}
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Clear"
|
|
||||||
id="input.clear"
|
|
||||||
/>
|
|
||||||
</OverlayWrapper>
|
|
||||||
}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={
|
|
||||||
Array [
|
|
||||||
"hover",
|
|
||||||
"focus",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className="input-clear-x"
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={null}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon icon-close-circle"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
|
||||||
`;
|
|
@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {mount} from 'enzyme';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
import {QuickInput} from './quick_input';
|
import {QuickInput} from './quick_input';
|
||||||
|
|
||||||
describe('components/QuickInput', () => {
|
describe('components/QuickInput', () => {
|
||||||
@ -14,16 +15,16 @@ describe('components/QuickInput', () => {
|
|||||||
['when value undefined', {clearable: true, onClear: () => {}}],
|
['when value undefined', {clearable: true, onClear: () => {}}],
|
||||||
['when value empty', {value: '', clearable: true, onClear: () => {}}],
|
['when value empty', {value: '', clearable: true, onClear: () => {}}],
|
||||||
])('should not render clear button', (description, props) => {
|
])('should not render clear button', (description, props) => {
|
||||||
const wrapper = mount(
|
renderWithContext(
|
||||||
<QuickInput {...props}/>,
|
<QuickInput {...props}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.input-clear').exists()).toBe(false);
|
expect(screen.queryByTestId('input-clear')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should render clear button', () => {
|
describe('should render clear button', () => {
|
||||||
test('with default tooltip text', () => {
|
test('with default tooltip text', () => {
|
||||||
const wrapper = mount(
|
renderWithContext(
|
||||||
<QuickInput
|
<QuickInput
|
||||||
value='mock'
|
value='mock'
|
||||||
clearable={true}
|
clearable={true}
|
||||||
@ -31,11 +32,11 @@ describe('components/QuickInput', () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.input-clear')).toMatchSnapshot();
|
expect(screen.queryByTestId('input-clear')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('with customized tooltip text', () => {
|
test('with customized tooltip text', () => {
|
||||||
const wrapper = mount(
|
renderWithContext(
|
||||||
<QuickInput
|
<QuickInput
|
||||||
value='mock'
|
value='mock'
|
||||||
clearable={true}
|
clearable={true}
|
||||||
@ -44,11 +45,11 @@ describe('components/QuickInput', () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.input-clear')).toMatchSnapshot();
|
expect(screen.queryByTestId('input-clear')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('with customized tooltip component', () => {
|
test('with customized tooltip component', () => {
|
||||||
const wrapper = mount(
|
renderWithContext(
|
||||||
<QuickInput
|
<QuickInput
|
||||||
value='mock'
|
value='mock'
|
||||||
clearable={true}
|
clearable={true}
|
||||||
@ -59,7 +60,7 @@ describe('components/QuickInput', () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.input-clear')).toMatchSnapshot();
|
expect(screen.queryByTestId('input-clear')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ describe('components/QuickInput', () => {
|
|||||||
return <div/>;
|
return <div/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const wrapper = mount(
|
const {rerender} = renderWithContext(
|
||||||
<QuickInput
|
<QuickInput
|
||||||
value='mock'
|
value='mock'
|
||||||
clearable={true}
|
clearable={true}
|
||||||
@ -80,11 +81,20 @@ describe('components/QuickInput', () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.setProps({onClear: () => wrapper.setProps({value: ''})});
|
expect(screen.queryByTestId('input-clear')).toBeInTheDocument();
|
||||||
expect(wrapper.find('.input-clear').exists()).toBe(true);
|
|
||||||
|
|
||||||
wrapper.find('.input-clear').simulate('mousedown');
|
userEvent.click(screen.getByTestId('input-clear'));
|
||||||
expect(wrapper.find('.input-clear').exists()).toBe(false);
|
|
||||||
|
rerender(
|
||||||
|
<QuickInput
|
||||||
|
value=''
|
||||||
|
clearable={true}
|
||||||
|
onClear={() => {}}
|
||||||
|
inputComponent={MockComp}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId('input-clear')).not.toBeInTheDocument();
|
||||||
expect(focusFn).toBeCalled();
|
expect(focusFn).toBeCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -200,9 +200,11 @@ export class QuickInput extends React.PureComponent<Props> {
|
|||||||
{inputElement}
|
{inputElement}
|
||||||
{showClearButton &&
|
{showClearButton &&
|
||||||
<div
|
<div
|
||||||
|
data-testid='input-clear'
|
||||||
className={classNames(clearClassName, 'input-clear visible')}
|
className={classNames(clearClassName, 'input-clear visible')}
|
||||||
onMouseDown={this.onClear}
|
onMouseDown={this.onClear}
|
||||||
onTouchEnd={this.onClear}
|
onTouchEnd={this.onClear}
|
||||||
|
role='button'
|
||||||
>
|
>
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||||
|
@ -45,6 +45,8 @@ exports[`components/search_bar/SearchBar should match snapshot with search 1`] =
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="input-clear visible"
|
class="input-clear visible"
|
||||||
|
data-testid="input-clear"
|
||||||
|
role="button"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@ -115,6 +117,8 @@ exports[`components/search_bar/SearchBar should match snapshot with search, with
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="input-clear visible"
|
class="input-clear visible"
|
||||||
|
data-testid="input-clear"
|
||||||
|
role="button"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {screen} from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {BrowserRouter as Router} from 'react-router-dom';
|
|
||||||
|
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {renderWithContext} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
import TeamButton from './team_button';
|
import TeamButton from './team_button';
|
||||||
|
|
||||||
@ -35,14 +35,12 @@ describe('components/TeamSidebar/TeamButton', () => {
|
|||||||
unread: true,
|
unread: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
renderWithContext(
|
||||||
<Router>
|
<TeamButton {...props}/>,
|
||||||
<TeamButton {...props}/>
|
|
||||||
</Router>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.unread-badge').exists()).toBe(true);
|
expect(screen.queryByTestId('team-badge-')).toBeInTheDocument();
|
||||||
expect(wrapper.find('.team-container.unread').exists()).toBe(true);
|
expect(screen.getByTestId('team-container-')).toHaveClass('unread');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide unread badge and set no class when unread in a product', () => {
|
it('should hide unread badge and set no class when unread in a product', () => {
|
||||||
@ -53,14 +51,12 @@ describe('components/TeamSidebar/TeamButton', () => {
|
|||||||
isInProduct: true,
|
isInProduct: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
renderWithContext(
|
||||||
<Router>
|
<TeamButton {...props}/>,
|
||||||
<TeamButton {...props}/>
|
|
||||||
</Router>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.unread-badge').exists()).toBe(false);
|
expect(screen.queryByTestId('team-badge-')).not.toBeInTheDocument();
|
||||||
expect(wrapper.find('.team-container.unread').exists()).toBe(false);
|
expect(screen.getByTestId('team-container-')).not.toHaveClass('unread');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show mentions badge and set class when mentions in channels', () => {
|
it('should show mentions badge and set class when mentions in channels', () => {
|
||||||
@ -71,14 +67,12 @@ describe('components/TeamSidebar/TeamButton', () => {
|
|||||||
mentions: 1,
|
mentions: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
renderWithContext(
|
||||||
<Router>
|
<TeamButton {...props}/>,
|
||||||
<TeamButton {...props}/>
|
|
||||||
</Router>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.badge.badge-max-number').exists()).toBe(true);
|
expect(screen.queryByTestId('team-badge-')).toHaveClass('badge-max-number');
|
||||||
expect(wrapper.find('.team-container.unread').exists()).toBe(true);
|
expect(screen.getByTestId('team-container-')).toHaveClass('unread');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide mentions badge and set no class when mentions in product', () => {
|
it('should hide mentions badge and set no class when mentions in product', () => {
|
||||||
@ -90,13 +84,11 @@ describe('components/TeamSidebar/TeamButton', () => {
|
|||||||
isInProduct: true,
|
isInProduct: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mountWithIntl(
|
renderWithContext(
|
||||||
<Router>
|
<TeamButton {...props}/>,
|
||||||
<TeamButton {...props}/>
|
|
||||||
</Router>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find('.badge.badge-max-number').exists()).toBe(false);
|
expect(screen.queryByTestId('team-badge-')).not.toBeInTheDocument();
|
||||||
expect(wrapper.find('.team-container.unread').exists()).toBe(false);
|
expect(screen.getByTestId('team-container-')).not.toHaveClass('unread');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,10 @@ export default function TeamButton({
|
|||||||
teamClass = 'unread';
|
teamClass = 'unread';
|
||||||
|
|
||||||
badge = (
|
badge = (
|
||||||
<span className={'unread-badge'}/>
|
<span
|
||||||
|
data-testid={'team-badge-' + teamId}
|
||||||
|
className={'unread-badge'}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} else if (isNotCreateTeamButton) {
|
} else if (isNotCreateTeamButton) {
|
||||||
teamClass = '';
|
teamClass = '';
|
||||||
@ -115,7 +118,12 @@ export default function TeamButton({
|
|||||||
});
|
});
|
||||||
|
|
||||||
badge = (
|
badge = (
|
||||||
<span className={classNames('badge badge-max-number pull-right small', {urgent: otherProps.hasUrgent})}>{mentions > 99 ? '99+' : mentions}</span>
|
<span
|
||||||
|
data-testid={'team-badge-' + teamId}
|
||||||
|
className={classNames('badge badge-max-number pull-right small', {urgent: otherProps.hasUrgent})}
|
||||||
|
>
|
||||||
|
{mentions > 99 ? '99+' : mentions}
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,6 +214,7 @@ export default function TeamButton({
|
|||||||
</Draggable>
|
</Draggable>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
data-testid={'team-container-' + teamId}
|
||||||
className={`team-container ${teamClass}`}
|
className={`team-container ${teamClass}`}
|
||||||
>
|
>
|
||||||
{teamButton}
|
{teamButton}
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import type {ReactWrapper} from 'enzyme';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import * as Utils from 'utils/utils';
|
import * as Utils from 'utils/utils';
|
||||||
|
|
||||||
import Toast from './toast';
|
import Toast from './toast';
|
||||||
@ -45,12 +44,14 @@ describe('components/Toast', () => {
|
|||||||
test('should dismiss', () => {
|
test('should dismiss', () => {
|
||||||
defaultProps.onDismiss = jest.fn();
|
defaultProps.onDismiss = jest.fn();
|
||||||
|
|
||||||
const wrapper: ReactWrapper<any, any, React.Component> = mountWithIntl(<Toast {... defaultProps}><span>{'child'}</span></Toast>);
|
renderWithContext(
|
||||||
const toast = wrapper.find(Toast).instance();
|
<Toast {... defaultProps}>
|
||||||
|
<span>{'child'}</span>
|
||||||
|
</Toast>,
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByTestId('dismissToast').click();
|
||||||
|
|
||||||
if (toast instanceof Toast) {
|
|
||||||
toast.handleDismiss();
|
|
||||||
}
|
|
||||||
expect(defaultProps.onDismiss).toHaveBeenCalledTimes(1);
|
expect(defaultProps.onDismiss).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
|||||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
|
||||||
import ChannelHeaderPlug from 'plugins/channel_header_plug/channel_header_plug';
|
import ChannelHeaderPlug from 'plugins/channel_header_plug/channel_header_plug';
|
||||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
import {renderWithContext} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
import type {PluginComponent} from 'types/store/plugins';
|
import type {PluginComponent} from 'types/store/plugins';
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ describe('plugins/ChannelHeaderPlug', () => {
|
|||||||
tooltipText: 'some tooltip text',
|
tooltipText: 'some tooltip text',
|
||||||
} as PluginComponent;
|
} as PluginComponent;
|
||||||
|
|
||||||
test('should match snapshot with no extended component', () => {
|
test('should not render anything with no extended component', () => {
|
||||||
const wrapper = mountWithIntl(
|
const {asFragment} = renderWithContext(
|
||||||
<ChannelHeaderPlug
|
<ChannelHeaderPlug
|
||||||
components={[]}
|
components={[]}
|
||||||
channel={{} as Channel}
|
channel={{} as Channel}
|
||||||
@ -40,11 +40,11 @@ describe('plugins/ChannelHeaderPlug', () => {
|
|||||||
shouldShowAppBar={false}
|
shouldShowAppBar={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot with one extended component', () => {
|
test('should match snapshot with one extended component', () => {
|
||||||
const wrapper = mountWithIntl(
|
const {asFragment} = renderWithContext(
|
||||||
<ChannelHeaderPlug
|
<ChannelHeaderPlug
|
||||||
components={[testPlug]}
|
components={[testPlug]}
|
||||||
channel={{} as Channel}
|
channel={{} as Channel}
|
||||||
@ -61,11 +61,11 @@ describe('plugins/ChannelHeaderPlug', () => {
|
|||||||
shouldShowAppBar={false}
|
shouldShowAppBar={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot with six extended components', () => {
|
test('should match snapshot with six extended components', () => {
|
||||||
const wrapper = mountWithIntl(
|
const {asFragment} = renderWithContext(
|
||||||
<ChannelHeaderPlug
|
<ChannelHeaderPlug
|
||||||
components={[
|
components={[
|
||||||
testPlug,
|
testPlug,
|
||||||
@ -98,11 +98,11 @@ describe('plugins/ChannelHeaderPlug', () => {
|
|||||||
shouldShowAppBar={false}
|
shouldShowAppBar={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot when the App Bar is visible', () => {
|
test('should not render anything when the App Bar is visible', () => {
|
||||||
const wrapper = mountWithIntl(
|
const {asFragment} = renderWithContext(
|
||||||
<ChannelHeaderPlug
|
<ChannelHeaderPlug
|
||||||
components={[
|
components={[
|
||||||
testPlug,
|
testPlug,
|
||||||
@ -124,6 +124,6 @@ describe('plugins/ChannelHeaderPlug', () => {
|
|||||||
shouldShowAppBar={true}
|
shouldShowAppBar={true}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,6 +31,7 @@ import {getPost as getPostAction} from 'mattermost-redux/actions/posts';
|
|||||||
import {getTeamByName as getTeamByNameAction} from 'mattermost-redux/actions/teams';
|
import {getTeamByName as getTeamByNameAction} from 'mattermost-redux/actions/teams';
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
import {Preferences, General} from 'mattermost-redux/constants';
|
import {Preferences, General} from 'mattermost-redux/constants';
|
||||||
|
import {createSelector} from 'mattermost-redux/selectors/create_selector';
|
||||||
import {
|
import {
|
||||||
getChannel,
|
getChannel,
|
||||||
getChannelsNameMapInTeam,
|
getChannelsNameMapInTeam,
|
||||||
@ -1630,8 +1631,7 @@ const TrackFlowRoles: Record<string, string> = {
|
|||||||
su: General.SYSTEM_USER_ROLE,
|
su: General.SYSTEM_USER_ROLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getTrackFlowRole() {
|
export function getTrackFlowRole(state: GlobalState) {
|
||||||
const state = store.getState();
|
|
||||||
let trackFlowRole = 'su';
|
let trackFlowRole = 'su';
|
||||||
|
|
||||||
if (isFirstAdmin(state)) {
|
if (isFirstAdmin(state)) {
|
||||||
@ -1643,11 +1643,15 @@ export function getTrackFlowRole() {
|
|||||||
return trackFlowRole;
|
return trackFlowRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRoleForTrackFlow() {
|
export const getRoleForTrackFlow = createSelector(
|
||||||
const startedByRole = TrackFlowRoles[getTrackFlowRole()];
|
'getRoleForTrackFlow',
|
||||||
|
getTrackFlowRole,
|
||||||
|
(trackFlowRole) => {
|
||||||
|
const startedByRole = TrackFlowRoles[trackFlowRole];
|
||||||
|
|
||||||
return {started_by_role: startedByRole};
|
return {started_by_role: startedByRole};
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export function getSbr() {
|
export function getSbr() {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
20
webapp/package-lock.json
generated
20
webapp/package-lock.json
generated
@ -210,7 +210,6 @@
|
|||||||
"jest-cli": "29.7.0",
|
"jest-cli": "29.7.0",
|
||||||
"jest-environment-jsdom": "29.7.0",
|
"jest-environment-jsdom": "29.7.0",
|
||||||
"jest-junit": "16.0.0",
|
"jest-junit": "16.0.0",
|
||||||
"jest-styled-components": "7.2.0",
|
|
||||||
"jest-watch-typeahead": "2.2.2",
|
"jest-watch-typeahead": "2.2.2",
|
||||||
"mmjstool": "github:mattermost/mattermost-utilities#73e61d2ede0ebf802492df4cfbac481d35efed54",
|
"mmjstool": "github:mattermost/mattermost-utilities#73e61d2ede0ebf802492df4cfbac481d35efed54",
|
||||||
"nock": "13.2.8",
|
"nock": "13.2.8",
|
||||||
@ -250,11 +249,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
|
||||||
"version": "4.3.1",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -15904,20 +15898,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jest-styled-components": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@adobe/css-tools": "^4.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 12"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"styled-components": ">= 5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jest-util": {
|
"node_modules/jest-util": {
|
||||||
"version": "28.1.3",
|
"version": "28.1.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
Loading…
Reference in New Issue
Block a user