[MM-63838] Fix potential TypeError when Calls is disabled (#30867)
* Fix potential TypeError when Calls is disabled * Use createSelector
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
bc561620cb
Коммит
d69925a415
@@ -1,7 +1,14 @@
|
|||||||
// 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 {isUserInCall} from './index';
|
import {screen, render} from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import {Provider} from 'react-redux';
|
||||||
|
import configureStore from 'redux-mock-store';
|
||||||
|
|
||||||
|
import {TestHelper} from '../../../utils/test_helper';
|
||||||
|
|
||||||
|
import CallButton, {isUserInCall} from './index';
|
||||||
|
|
||||||
describe('isUserInCall', () => {
|
describe('isUserInCall', () => {
|
||||||
test('missing state', () => {
|
test('missing state', () => {
|
||||||
@@ -51,3 +58,385 @@ describe('isUserInCall', () => {
|
|||||||
} as any, 'userA', 'channelID')).toBe(true);
|
} as any, 'userA', 'channelID')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('CallButton', () => {
|
||||||
|
const mockStore = configureStore();
|
||||||
|
const userId = 'user1';
|
||||||
|
const currentUserId = 'current_user';
|
||||||
|
const dmChannelId = 'dm_channel_id';
|
||||||
|
const baseProps = {
|
||||||
|
userId,
|
||||||
|
currentUserId,
|
||||||
|
fullname: 'Test User',
|
||||||
|
username: 'testuser',
|
||||||
|
};
|
||||||
|
const PluginComponent = () => {
|
||||||
|
return <button>{'Start Call'}</button>;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should not render when calls are disabled', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_user',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId('startCallButton')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render for admin even in test mode', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
DefaultEnabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
[dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
currentUserId,
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_admin',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CallButton: [{
|
||||||
|
id: 'CallButton',
|
||||||
|
plugin_id: 'com.mattermost.calls',
|
||||||
|
button: PluginComponent,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
rhs: {
|
||||||
|
isSidebarOpen: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render when calls are enabled and not in test mode', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
DefaultEnabled: true,
|
||||||
|
},
|
||||||
|
channels: {},
|
||||||
|
sessions: {},
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
[dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
currentUserId,
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_admin',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CallButton: [{
|
||||||
|
id: 'CallButton',
|
||||||
|
plugin_id: 'com.mattermost.calls',
|
||||||
|
button: PluginComponent,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
rhs: {
|
||||||
|
isSidebarOpen: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render when channel is explicitly enabled regardless of test mode', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
DefaultEnabled: false,
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
[dmChannelId]: {enabled: true},
|
||||||
|
},
|
||||||
|
sessions: {},
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
[dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
currentUserId,
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_admin',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CallButton: [{
|
||||||
|
id: 'CallButton',
|
||||||
|
plugin_id: 'com.mattermost.calls',
|
||||||
|
button: PluginComponent,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
rhs: {
|
||||||
|
isSidebarOpen: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not render when channel is explicitly disabled', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
DefaultEnabled: true,
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
[dmChannelId]: {enabled: false},
|
||||||
|
},
|
||||||
|
sessions: {},
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
[dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_admin',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText('Start Call')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should disable button when there is an ongoing call', () => {
|
||||||
|
const store = mockStore({
|
||||||
|
'plugins-com.mattermost.calls': {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
DefaultEnabled: true,
|
||||||
|
},
|
||||||
|
sessions: {
|
||||||
|
[dmChannelId]: {
|
||||||
|
session1: {
|
||||||
|
user_id: currentUserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
channels: {
|
||||||
|
currentChannelId: dmChannelId,
|
||||||
|
channels: {
|
||||||
|
current_channel_id: TestHelper.getChannelMock({
|
||||||
|
id: dmChannelId,
|
||||||
|
name: `${currentUserId}__${userId}`,
|
||||||
|
display_name: `${currentUserId}__${userId}`,
|
||||||
|
delete_at: 0,
|
||||||
|
type: 'D',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
myMembers: {
|
||||||
|
[dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
profiles: {
|
||||||
|
[currentUserId]: TestHelper.getUserMock({
|
||||||
|
id: currentUserId,
|
||||||
|
roles: 'system_admin',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
plugins: {
|
||||||
|
'com.mattermost.calls': {
|
||||||
|
id: 'com.mattermost.calls',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<CallButton {...baseProps}/>
|
||||||
|
</Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByLabelText('Call with Test User is ongoing');
|
||||||
|
expect(button).toBeInTheDocument();
|
||||||
|
expect(button).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,12 +5,18 @@ import semver from 'semver';
|
|||||||
|
|
||||||
import type {CallsConfig, UserSessionState} from '@mattermost/calls-common/lib/types';
|
import type {CallsConfig, UserSessionState} from '@mattermost/calls-common/lib/types';
|
||||||
|
|
||||||
|
import {createSelector} from 'mattermost-redux/selectors/create_selector';
|
||||||
|
|
||||||
import {suitePluginIds} from 'utils/constants';
|
import {suitePluginIds} from 'utils/constants';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
const CALLS_PLUGIN = `plugins-${suitePluginIds.calls}`;
|
const CALLS_PLUGIN = `plugins-${suitePluginIds.calls}`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const pluginState = (state: GlobalState) => state[CALLS_PLUGIN];
|
||||||
|
|
||||||
export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
||||||
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
|
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
|
||||||
semver.gte(String(semver.clean(state.plugins.plugins[suitePluginIds.calls].version || '0.0.0')), minVersion));
|
semver.gte(String(semver.clean(state.plugins.plugins[suitePluginIds.calls].version || '0.0.0')), minVersion));
|
||||||
@@ -18,32 +24,33 @@ export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
|||||||
|
|
||||||
// isCallsRingingEnabledOnServer is the flag for the ringing/notification feature in calls
|
// isCallsRingingEnabledOnServer is the flag for the ringing/notification feature in calls
|
||||||
export function isCallsRingingEnabledOnServer(state: GlobalState) {
|
export function isCallsRingingEnabledOnServer(state: GlobalState) {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
return Boolean(pluginState(state)?.callsConfig?.EnableRinging);
|
||||||
// @ts-ignore
|
|
||||||
return Boolean(state[CALLS_PLUGIN]?.callsConfig?.EnableRinging);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSessionsInCalls(state: GlobalState): Record<string, Record<string, UserSessionState>> {
|
export const getSessionsInCalls = createSelector(
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
'getSessionsInCalls',
|
||||||
// @ts-ignore
|
pluginState,
|
||||||
return state[CALLS_PLUGIN]?.sessions || {};
|
(state): Record<string, Record<string, UserSessionState>> => {
|
||||||
}
|
return state?.sessions || {};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export function getCallsConfig(state: GlobalState): CallsConfig {
|
export const getCallsConfig = createSelector(
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
'getCallsConfig',
|
||||||
// @ts-ignore
|
pluginState,
|
||||||
return state[CALLS_PLUGIN]?.callsConfig;
|
(state): CallsConfig => {
|
||||||
}
|
return state?.callsConfig || {};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export function getCallsChannelState(state: GlobalState, channelId: string): {enabled?: boolean} {
|
export function getCallsChannelState(state: GlobalState, channelId: string): {enabled?: boolean} {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
const callsState = pluginState(state);
|
||||||
// @ts-ignore
|
|
||||||
if (!state[CALLS_PLUGIN] || !state[CALLS_PLUGIN].channels) {
|
if (!callsState || !callsState.channels || !callsState.channels[channelId]) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
return callsState.channels[channelId];
|
||||||
return state[CALLS_PLUGIN].channels[channelId] || {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function callsChannelExplicitlyEnabled(state: GlobalState, channelId: string) {
|
export function callsChannelExplicitlyEnabled(state: GlobalState, channelId: string) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user