/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/
import {act, render, waitFor} from '@testing-library/react';
import {FeatureStatus, FEATURE_KEY, FeatureList} from '@wireapp/api-client/lib/team/feature';
import {Runtime} from '@wireapp/commons/lib/util/Runtime';
import {PrimaryModal} from 'Components/Modals/PrimaryModal';
import en from 'I18n/en-US.json';
import {setStrings} from 'Util/LocalizerUtil';
import {FeatureConfigChangeNotifier} from './FeatureConfigChangeNotifier';
import {TeamState} from '../../../../team/TeamState';
setStrings({en});
describe('FeatureConfigChangeNotifier', () => {
const showModalSpy = jest.spyOn(PrimaryModal, 'show');
beforeEach(() => {
showModalSpy.mockClear();
localStorage.clear();
jest.spyOn(Runtime, 'isDesktopApp').mockReturnValue(true);
jest.spyOn(Runtime, 'isWindows').mockReturnValue(true);
});
const baseConfig: FeatureList = {
[FEATURE_KEY.FILE_SHARING]: {
status: FeatureStatus.DISABLED,
},
[FEATURE_KEY.VIDEO_CALLING]: {
status: FeatureStatus.DISABLED,
},
[FEATURE_KEY.SELF_DELETING_MESSAGES]: {
status: FeatureStatus.DISABLED,
config: {enforcedTimeoutSeconds: 0},
},
[FEATURE_KEY.CONFERENCE_CALLING]: {
config: {useSFTForOneToOneCalls: false},
status: FeatureStatus.DISABLED,
},
[FEATURE_KEY.CONVERSATION_GUEST_LINKS]: {
status: FeatureStatus.DISABLED,
},
[FEATURE_KEY.ENFORCE_DOWNLOAD_PATH]: {
status: FeatureStatus.DISABLED,
config: {enforcedDownloadLocation: ''},
},
};
it.each([
[
FEATURE_KEY.FILE_SHARING,
'Sharing and receiving files of any type is now enabled',
'Sharing and receiving files of any type is now disabled',
],
[FEATURE_KEY.VIDEO_CALLING, 'Camera in calls is enabled', 'Camera in calls is disabled'],
[
FEATURE_KEY.CONFERENCE_CALLING,
'Your team was upgraded to Enterprise, which gives you access to features such as conference calls and more. Learn more about Enterprise',
undefined,
],
[
FEATURE_KEY.CONVERSATION_GUEST_LINKS,
'Generating guest links is now enabled for all group admins.',
'Generating guest links is now disabled for all group admins.',
],
[
FEATURE_KEY.ENFORCE_DOWNLOAD_PATH,
'You’ll find your downloaded files now in a specific standard location on your Windows computer. The app needs a restart for the new setting to take effect.',
'Standard file location on Windows computers is disabled. Restart the app to save downloaded files in a new location.',
],
] as const)('shows a modal when feature %s is turned on and off', async (feature, enabledString, disabledString) => {
const teamState = new TeamState();
render();
act(() => {
teamState.teamFeatures(baseConfig);
});
act(() => {
teamState.teamFeatures({
...baseConfig,
[feature]: {
status: FeatureStatus.ENABLED,
...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: 'dlpath'}}),
},
});
});
await waitFor(() => {
expect(showModalSpy).toHaveBeenCalledTimes(1);
expect(showModalSpy).toHaveBeenCalledWith(
PrimaryModal.type.ACKNOWLEDGE,
expect.objectContaining({
text: expect.objectContaining({
htmlMessage: enabledString,
}),
}),
);
});
act(() => {
teamState.teamFeatures({
...baseConfig,
[feature]: {
status: FeatureStatus.DISABLED,
...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: ''}}),
},
});
});
if (!disabledString) {
expect(showModalSpy).toHaveBeenCalledTimes(1);
} else {
await waitFor(() => {
expect(showModalSpy).toHaveBeenCalledTimes(2);
expect(showModalSpy).toHaveBeenCalledWith(
PrimaryModal.type.ACKNOWLEDGE,
expect.objectContaining({
text: expect.objectContaining({
htmlMessage: disabledString,
}),
}),
);
});
}
});
it('saves previous state of feature and warn of feature change at mount time', async () => {
const teamState = new TeamState();
teamState.teamFeatures(baseConfig);
const {unmount} = render();
unmount();
expect(showModalSpy).not.toHaveBeenCalled();
teamState.teamFeatures({...baseConfig, [FEATURE_KEY.FILE_SHARING]: {status: FeatureStatus.ENABLED}});
render();
expect(showModalSpy).toHaveBeenCalledTimes(1);
});
it.each([
[
{status: FeatureStatus.DISABLED, config: {enforcedTimeoutSeconds: 0}},
{status: FeatureStatus.ENABLED, config: {enforcedTimeoutSeconds: 10}},
'Self-deleting messages are now mandatory. New messages will self-delete after 10 seconds.',
],
[
{status: FeatureStatus.DISABLED, config: {enforcedTimeoutSeconds: 0}},
{status: FeatureStatus.ENABLED, config: {enforcedTimeoutSeconds: 0}},
'Self-deleting messages are enabled. You can set a timer before writing a message.',
],
[
{status: FeatureStatus.ENABLED, config: {enforcedTimeoutSeconds: 0}},
{status: FeatureStatus.ENABLED, config: {enforcedTimeoutSeconds: 10}},
'Self-deleting messages are now mandatory. New messages will self-delete after 10 seconds.',
],
[
{status: FeatureStatus.ENABLED, config: {enforcedTimeoutSeconds: 10}},
{status: FeatureStatus.DISABLED, config: {enforcedTimeoutSeconds: 0}},
'Self-deleting messages are disabled',
],
])(
'indicates the config change when self deleting messages have changed (%s) to (%s)',
async (fromStatus, toStatus, expectedText) => {
const teamState = new TeamState();
render();
act(() => {
teamState.teamFeatures({
...baseConfig,
[FEATURE_KEY.SELF_DELETING_MESSAGES]: fromStatus,
});
});
act(() => {
teamState.teamFeatures({
...baseConfig,
[FEATURE_KEY.SELF_DELETING_MESSAGES]: toStatus,
});
});
await waitFor(() => {
expect(showModalSpy).toHaveBeenCalledTimes(1);
expect(showModalSpy).toHaveBeenCalledWith(PrimaryModal.type.ACKNOWLEDGE, {
hideCloseBtn: false,
primaryAction: undefined,
preventClose: false,
text: expect.objectContaining({
htmlMessage: expectedText,
}),
});
});
},
);
});