unit test Notifications.tsx (#7468)
Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
parent
4ab3470184
commit
92ee02fe02
5 changed files with 428 additions and 10 deletions
|
@ -148,7 +148,6 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
private async refreshRules(): Promise<Partial<IState>> {
|
private async refreshRules(): Promise<Partial<IState>> {
|
||||||
const ruleSets = await MatrixClientPeg.get().getPushRules();
|
const ruleSets = await MatrixClientPeg.get().getPushRules();
|
||||||
|
|
||||||
const categories = {
|
const categories = {
|
||||||
[RuleId.Master]: RuleClass.Master,
|
[RuleId.Master]: RuleClass.Master,
|
||||||
|
|
||||||
|
@ -182,6 +181,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
for (const k in ruleSets.global) {
|
for (const k in ruleSets.global) {
|
||||||
// noinspection JSUnfilteredForInLoop
|
// noinspection JSUnfilteredForInLoop
|
||||||
const kind = k as PushRuleKind;
|
const kind = k as PushRuleKind;
|
||||||
|
|
||||||
for (const r of ruleSets.global[kind]) {
|
for (const r of ruleSets.global[kind]) {
|
||||||
const rule: IAnnotatedPushRule = Object.assign(r, { kind });
|
const rule: IAnnotatedPushRule = Object.assign(r, { kind });
|
||||||
const category = categories[rule.rule_id] ?? RuleClass.Other;
|
const category = categories[rule.rule_id] ?? RuleClass.Other;
|
||||||
|
@ -471,6 +471,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
private renderTopSection() {
|
private renderTopSection() {
|
||||||
const masterSwitch = <LabelledToggleSwitch
|
const masterSwitch = <LabelledToggleSwitch
|
||||||
|
data-test-id='notif-master-switch'
|
||||||
value={!this.isInhibited}
|
value={!this.isInhibited}
|
||||||
label={_t("Enable for this account")}
|
label={_t("Enable for this account")}
|
||||||
onChange={this.onMasterRuleChanged}
|
onChange={this.onMasterRuleChanged}
|
||||||
|
@ -484,6 +485,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
const emailSwitches = (this.state.threepids || []).filter(t => t.medium === ThreepidMedium.Email)
|
const emailSwitches = (this.state.threepids || []).filter(t => t.medium === ThreepidMedium.Email)
|
||||||
.map(e => <LabelledToggleSwitch
|
.map(e => <LabelledToggleSwitch
|
||||||
|
data-test-id='notif-email-switch'
|
||||||
key={e.address}
|
key={e.address}
|
||||||
value={this.state.pushers.some(p => p.kind === "email" && p.pushkey === e.address)}
|
value={this.state.pushers.some(p => p.kind === "email" && p.pushkey === e.address)}
|
||||||
label={_t("Enable email notifications for %(email)s", { email: e.address })}
|
label={_t("Enable email notifications for %(email)s", { email: e.address })}
|
||||||
|
@ -495,6 +497,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
{ masterSwitch }
|
{ masterSwitch }
|
||||||
|
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
|
data-test-id='notif-setting-notificationsEnabled'
|
||||||
value={SettingsStore.getValue("notificationsEnabled")}
|
value={SettingsStore.getValue("notificationsEnabled")}
|
||||||
onChange={this.onDesktopNotificationsChanged}
|
onChange={this.onDesktopNotificationsChanged}
|
||||||
label={_t('Enable desktop notifications for this session')}
|
label={_t('Enable desktop notifications for this session')}
|
||||||
|
@ -502,6 +505,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
|
data-test-id='notif-setting-notificationBodyEnabled'
|
||||||
value={SettingsStore.getValue("notificationBodyEnabled")}
|
value={SettingsStore.getValue("notificationBodyEnabled")}
|
||||||
onChange={this.onDesktopShowBodyChanged}
|
onChange={this.onDesktopShowBodyChanged}
|
||||||
label={_t('Show message in desktop notification')}
|
label={_t('Show message in desktop notification')}
|
||||||
|
@ -509,6 +513,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
|
data-test-id='notif-setting-audioNotificationsEnabled'
|
||||||
value={SettingsStore.getValue("audioNotificationsEnabled")}
|
value={SettingsStore.getValue("audioNotificationsEnabled")}
|
||||||
onChange={this.onAudioNotificationsChanged}
|
onChange={this.onAudioNotificationsChanged}
|
||||||
label={_t('Enable audible notifications for this session')}
|
label={_t('Enable audible notifications for this session')}
|
||||||
|
@ -559,6 +564,12 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VectorStateToLabel = {
|
||||||
|
[VectorState.On]: _t('On'),
|
||||||
|
[VectorState.Off]: _t('Off'),
|
||||||
|
[VectorState.Loud]: _t('Noisy'),
|
||||||
|
};
|
||||||
|
|
||||||
const makeRadio = (r: IVectorPushRule, s: VectorState) => (
|
const makeRadio = (r: IVectorPushRule, s: VectorState) => (
|
||||||
<StyledRadioButton
|
<StyledRadioButton
|
||||||
key={r.ruleId}
|
key={r.ruleId}
|
||||||
|
@ -566,10 +577,14 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
checked={r.vectorState === s}
|
checked={r.vectorState === s}
|
||||||
onChange={this.onRadioChecked.bind(this, r, s)}
|
onChange={this.onRadioChecked.bind(this, r, s)}
|
||||||
disabled={this.state.phase === Phase.Persisting}
|
disabled={this.state.phase === Phase.Persisting}
|
||||||
|
aria-label={VectorStateToLabel[s]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const rows = this.state.vectorPushRules[category].map(r => <tr key={category + r.ruleId}>
|
const rows = this.state.vectorPushRules[category].map(r => <tr
|
||||||
|
data-test-id={category + r.ruleId}
|
||||||
|
key={category + r.ruleId}
|
||||||
|
>
|
||||||
<td>{ r.description }</td>
|
<td>{ r.description }</td>
|
||||||
<td>{ makeRadio(r, VectorState.Off) }</td>
|
<td>{ makeRadio(r, VectorState.Off) }</td>
|
||||||
<td>{ makeRadio(r, VectorState.On) }</td>
|
<td>{ makeRadio(r, VectorState.On) }</td>
|
||||||
|
@ -592,13 +607,13 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<table className='mx_UserNotifSettings_pushRulesTable'>
|
<table data-test-id={`notif-section-${category}`} className='mx_UserNotifSettings_pushRulesTable'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{ sectionName }</th>
|
<th>{ sectionName }</th>
|
||||||
<th>{ _t("Off") }</th>
|
<th>{ VectorStateToLabel[VectorState.Off] }</th>
|
||||||
<th>{ _t("On") }</th>
|
<th>{ VectorStateToLabel[VectorState.On] }</th>
|
||||||
<th>{ _t("Noisy") }</th>
|
<th>{ VectorStateToLabel[VectorState.Loud] }</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -635,7 +650,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
// Ends up default centered
|
// Ends up default centered
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
} else if (this.state.phase === Phase.Error) {
|
} else if (this.state.phase === Phase.Error) {
|
||||||
return <p>{ _t("There was an error loading your notification settings.") }</p>;
|
return <p data-test-id='error-message'>{ _t("There was an error loading your notification settings.") }</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className='mx_UserNotifSettings'>
|
return <div className='mx_UserNotifSettings'>
|
||||||
|
|
|
@ -1276,11 +1276,11 @@
|
||||||
"Clear notifications": "Clear notifications",
|
"Clear notifications": "Clear notifications",
|
||||||
"Keyword": "Keyword",
|
"Keyword": "Keyword",
|
||||||
"New keyword": "New keyword",
|
"New keyword": "New keyword",
|
||||||
|
"On": "On",
|
||||||
|
"Off": "Off",
|
||||||
|
"Noisy": "Noisy",
|
||||||
"Global": "Global",
|
"Global": "Global",
|
||||||
"Mentions & keywords": "Mentions & keywords",
|
"Mentions & keywords": "Mentions & keywords",
|
||||||
"Off": "Off",
|
|
||||||
"On": "On",
|
|
||||||
"Noisy": "Noisy",
|
|
||||||
"Notification targets": "Notification targets",
|
"Notification targets": "Notification targets",
|
||||||
"There was an error loading your notification settings.": "There was an error loading your notification settings.",
|
"There was an error loading your notification settings.": "There was an error loading your notification settings.",
|
||||||
"Failed to save your profile": "Failed to save your profile",
|
"Failed to save your profile": "Failed to save your profile",
|
||||||
|
|
283
test/components/views/settings/Notifications-test.tsx
Normal file
283
test/components/views/settings/Notifications-test.tsx
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import '../../../skinned-sdk';
|
||||||
|
import { IPushRule, IPushRules, RuleId } from 'matrix-js-sdk';
|
||||||
|
import { ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
|
import { createTestClient } from '../../../test-utils';
|
||||||
|
import Notifications from '../../../../src/components/views/settings/Notifications';
|
||||||
|
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||||
|
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||||
|
import { StandardActions } from '../../../../src/notifications/StandardActions';
|
||||||
|
|
||||||
|
jest.mock('../../../../src/settings/SettingsStore', () => ({
|
||||||
|
monitorSetting: jest.fn(),
|
||||||
|
getValue: jest.fn(),
|
||||||
|
setValue: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// don't pollute test output with error logs from mock rejections
|
||||||
|
jest.mock("matrix-js-sdk/src/logger");
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
const masterRule = {
|
||||||
|
actions: ["dont_notify"],
|
||||||
|
conditions: [],
|
||||||
|
default: true,
|
||||||
|
enabled: false,
|
||||||
|
rule_id: RuleId.Master,
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const oneToOneRule = { "conditions": [{ "kind": "room_member_count", "is": "2" }, { "kind": "event_match", "key": "type", "pattern": "m.room.message" }], "actions": ["notify", { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.room_one_to_one", "default": true, "enabled": true } as IPushRule;
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const encryptedOneToOneRule = { "conditions": [{ "kind": "room_member_count", "is": "2" }, { "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.encrypted_room_one_to_one", "default": true, "enabled": true } as IPushRule;
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const encryptedGroupRule = { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" }], "actions": ["dont_notify"], "rule_id": ".m.rule.encrypted", "default": true, "enabled": true } as IPushRule;
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const pushRules: IPushRules = { "global": { "underride": [{ "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.call.invite" }], "actions": ["notify", { "set_tweak": "sound", "value": "ring" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.call", "default": true, "enabled": true }, oneToOneRule, encryptedOneToOneRule, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.message" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.message", "default": true, "enabled": true }, encryptedGroupRule, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "im.vector.modular.widgets" }, { "kind": "event_match", "key": "content.type", "pattern": "jitsi" }, { "kind": "event_match", "key": "state_key", "pattern": "*" }], "actions": ["notify", { "set_tweak": "highlight", "value": false }], "rule_id": ".im.vector.jitsi", "default": true, "enabled": true }], "sender": [], "room": [{ "actions": ["dont_notify"], "rule_id": "!zJPyWqpMorfCcWObge:matrix.org", "default": false, "enabled": true }], "content": [{ "actions": ["notify", { "set_tweak": "highlight", "value": false }], "pattern": "banana", "rule_id": "banana", "default": false, "enabled": true }, { "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight" }], "pattern": "kadev1", "rule_id": ".m.rule.contains_user_name", "default": true, "enabled": true }], "override": [{ "conditions": [], "actions": ["dont_notify"], "rule_id": ".m.rule.master", "default": true, "enabled": false }, { "conditions": [{ "kind": "event_match", "key": "content.msgtype", "pattern": "m.notice" }], "actions": ["dont_notify"], "rule_id": ".m.rule.suppress_notices", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.member" }, { "kind": "event_match", "key": "content.membership", "pattern": "invite" }, { "kind": "event_match", "key": "state_key", "pattern": "@kadev1:matrix.org" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.invite_for_me", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.member" }], "actions": ["dont_notify"], "rule_id": ".m.rule.member_event", "default": true, "enabled": true }, { "conditions": [{ "kind": "contains_display_name" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight" }], "rule_id": ".m.rule.contains_display_name", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "content.body", "pattern": "@room" }, { "kind": "sender_notification_permission", "key": "room" }], "actions": ["notify", { "set_tweak": "highlight", "value": true }], "rule_id": ".m.rule.roomnotif", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.tombstone" }, { "kind": "event_match", "key": "state_key", "pattern": "" }], "actions": ["notify", { "set_tweak": "highlight", "value": true }], "rule_id": ".m.rule.tombstone", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.reaction" }], "actions": ["dont_notify"], "rule_id": ".m.rule.reaction", "default": true, "enabled": true }] }, "device": {} } as IPushRules;
|
||||||
|
|
||||||
|
const flushPromises = async () => await new Promise(process.nextTick);
|
||||||
|
|
||||||
|
describe('<Notifications />', () => {
|
||||||
|
const getComponent = () => mount(<Notifications />);
|
||||||
|
|
||||||
|
// get component, wait for async data and force a render
|
||||||
|
const getComponentAndWait = async () => {
|
||||||
|
const component = getComponent();
|
||||||
|
await flushPromises();
|
||||||
|
component.setProps({});
|
||||||
|
return component;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockClient = createTestClient();
|
||||||
|
mockClient.getPushRules.mockResolvedValue(pushRules);
|
||||||
|
|
||||||
|
const findByTestId = (component, id) => component.find(`[data-test-id="${id}"]`);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
MatrixClientPeg.get = () => mockClient;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockClient.getPushRules.mockClear().mockResolvedValue(pushRules);
|
||||||
|
mockClient.getPushers.mockClear().mockResolvedValue({ pushers: [] });
|
||||||
|
mockClient.getThreePids.mockClear().mockResolvedValue({ threepids: [] });
|
||||||
|
mockClient.setPusher.mockClear().mockResolvedValue({});
|
||||||
|
|
||||||
|
(SettingsStore.getValue as jest.Mock).mockClear().mockReturnValue(true);
|
||||||
|
(SettingsStore.setValue as jest.Mock).mockClear().mockResolvedValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders spinner while loading', () => {
|
||||||
|
const component = getComponent();
|
||||||
|
expect(component.find('.mx_Spinner').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders error message when fetching push rules fails', async () => {
|
||||||
|
mockClient.getPushRules.mockRejectedValue();
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
expect(findByTestId(component, 'error-message').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('renders error message when fetching push rules fails', async () => {
|
||||||
|
mockClient.getPushRules.mockRejectedValue();
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
expect(findByTestId(component, 'error-message').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('renders error message when fetching pushers fails', async () => {
|
||||||
|
mockClient.getPushers.mockRejectedValue();
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
expect(findByTestId(component, 'error-message').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('renders error message when fetching threepids fails', async () => {
|
||||||
|
mockClient.getThreePids.mockRejectedValue();
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
expect(findByTestId(component, 'error-message').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('main notification switches', () => {
|
||||||
|
it('renders only enable notifications switch when notifications are disabled', async () => {
|
||||||
|
const disableNotificationsPushRules = {
|
||||||
|
global: {
|
||||||
|
...pushRules.global,
|
||||||
|
override: [{ ...masterRule, enabled: true }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mockClient.getPushRules.mockClear().mockResolvedValue(disableNotificationsPushRules);
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
it('renders switches correctly', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
expect(findByTestId(component, 'notif-master-switch').length).toBeTruthy();
|
||||||
|
expect(findByTestId(component, 'notif-setting-notificationsEnabled').length).toBeTruthy();
|
||||||
|
expect(findByTestId(component, 'notif-setting-notificationBodyEnabled').length).toBeTruthy();
|
||||||
|
expect(findByTestId(component, 'notif-setting-audioNotificationsEnabled').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('email switches', () => {
|
||||||
|
const testEmail = 'tester@test.com';
|
||||||
|
beforeEach(() => {
|
||||||
|
mockClient.getThreePids.mockResolvedValue({
|
||||||
|
threepids: [
|
||||||
|
// should render switch bc pushKey and address match
|
||||||
|
{
|
||||||
|
medium: ThreepidMedium.Email,
|
||||||
|
address: testEmail,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders email switches correctly when email 3pids exist', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
expect(findByTestId(component, 'notif-email-switch')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders email switches correctly when notifications are on for email', async () => {
|
||||||
|
mockClient.getPushers.mockResolvedValue({ pushers: [{ kind: 'email', pushkey: testEmail }] });
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
expect(findByTestId(component, 'notif-email-switch').props().value).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enables email notification when toggling on', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
const emailToggle = findByTestId(component, 'notif-email-switch')
|
||||||
|
.find('div[role="switch"]');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
emailToggle.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClient.setPusher).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
kind: "email",
|
||||||
|
app_id: "m.email",
|
||||||
|
pushkey: testEmail,
|
||||||
|
app_display_name: "Email Notifications",
|
||||||
|
device_display_name: testEmail,
|
||||||
|
append: true,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays error when pusher update fails', async () => {
|
||||||
|
mockClient.setPusher.mockRejectedValue();
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
const emailToggle = findByTestId(component, 'notif-email-switch')
|
||||||
|
.find('div[role="switch"]');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
emailToggle.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
// force render
|
||||||
|
await flushPromises();
|
||||||
|
await component.setProps({});
|
||||||
|
|
||||||
|
expect(findByTestId(component, 'error-message').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enables email notification when toggling off', async () => {
|
||||||
|
const testPusher = { kind: 'email', pushkey: 'tester@test.com' };
|
||||||
|
mockClient.getPushers.mockResolvedValue({ pushers: [testPusher] });
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
const emailToggle = findByTestId(component, 'notif-email-switch')
|
||||||
|
.find('div[role="switch"]');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
emailToggle.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClient.setPusher).toHaveBeenCalledWith({
|
||||||
|
...testPusher, kind: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets settings value on toggle click', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
const audioNotifsToggle = findByTestId(component, 'notif-setting-audioNotificationsEnabled')
|
||||||
|
.find('div[role="switch"]');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
audioNotifsToggle.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(SettingsStore.setValue).toHaveBeenCalledWith('audioNotificationsEnabled', null, "device", false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('individual notification level settings', () => {
|
||||||
|
const getCheckedRadioForRule = (ruleEl) =>
|
||||||
|
ruleEl.find('input[type="radio"][checked=true]').props()['aria-label'];
|
||||||
|
it('renders categories correctly', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
|
||||||
|
expect(findByTestId(component, 'notif-section-vector_global').length).toBeTruthy();
|
||||||
|
expect(findByTestId(component, 'notif-section-vector_mentions').length).toBeTruthy();
|
||||||
|
expect(findByTestId(component, 'notif-section-vector_other').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders radios correctly', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
const section = 'vector_global';
|
||||||
|
|
||||||
|
const globalSection = findByTestId(component, `notif-section-${section}`);
|
||||||
|
// 16 notification rules with class 'global'
|
||||||
|
expect(globalSection.find('td').length).toEqual(16);
|
||||||
|
// oneToOneRule is set to 'on'
|
||||||
|
const oneToOneRuleElement = findByTestId(component, section + oneToOneRule.rule_id);
|
||||||
|
expect(getCheckedRadioForRule(oneToOneRuleElement)).toEqual('On');
|
||||||
|
// encryptedOneToOneRule is set to 'loud'
|
||||||
|
const encryptedOneToOneElement = findByTestId(component, section + encryptedOneToOneRule.rule_id);
|
||||||
|
expect(getCheckedRadioForRule(encryptedOneToOneElement)).toEqual('Noisy');
|
||||||
|
// encryptedGroupRule is set to 'off'
|
||||||
|
const encryptedGroupElement = findByTestId(component, section + encryptedGroupRule.rule_id);
|
||||||
|
expect(getCheckedRadioForRule(encryptedGroupElement)).toEqual('Off');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates notification level when changed', async () => {
|
||||||
|
const component = await getComponentAndWait();
|
||||||
|
const section = 'vector_global';
|
||||||
|
|
||||||
|
// oneToOneRule is set to 'on'
|
||||||
|
// and is kind: 'underride'
|
||||||
|
const oneToOneRuleElement = findByTestId(component, section + oneToOneRule.rule_id);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
// toggle at 0 is 'off'
|
||||||
|
const offToggle = oneToOneRuleElement.find('input[type="radio"]').at(0);
|
||||||
|
offToggle.simulate('change');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClient.setPushRuleEnabled).toHaveBeenCalledWith(
|
||||||
|
'global', 'underride', oneToOneRule.rule_id, true);
|
||||||
|
|
||||||
|
// actions for '.m.rule.room_one_to_one' state is ACTION_DONT_NOTIFY
|
||||||
|
expect(mockClient.setPushRuleActions).toHaveBeenCalledWith(
|
||||||
|
'global', 'underride', oneToOneRule.rule_id, StandardActions.ACTION_DONT_NOTIFY);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,114 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<Notifications /> main notification switches email switches renders email switches correctly when email 3pids exist 1`] = `
|
||||||
|
<LabelledToggleSwitch
|
||||||
|
data-test-id="notif-email-switch"
|
||||||
|
disabled={false}
|
||||||
|
key="tester@test.com"
|
||||||
|
label="Enable email notifications for tester@test.com"
|
||||||
|
onChange={[Function]}
|
||||||
|
value={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_SettingsFlag "
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="mx_SettingsFlag_label"
|
||||||
|
>
|
||||||
|
Enable email notifications for tester@test.com
|
||||||
|
</span>
|
||||||
|
<_default
|
||||||
|
aria-label="Enable email notifications for tester@test.com"
|
||||||
|
checked={false}
|
||||||
|
disabled={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
>
|
||||||
|
<AccessibleButton
|
||||||
|
aria-checked={false}
|
||||||
|
aria-disabled={false}
|
||||||
|
aria-label="Enable email notifications for tester@test.com"
|
||||||
|
className="mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||||
|
element="div"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="switch"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-checked={false}
|
||||||
|
aria-disabled={false}
|
||||||
|
aria-label="Enable email notifications for tester@test.com"
|
||||||
|
className="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="switch"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_ToggleSwitch_ball"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
</_default>
|
||||||
|
</div>
|
||||||
|
</LabelledToggleSwitch>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<Notifications /> main notification switches renders only enable notifications switch when notifications are disabled 1`] = `
|
||||||
|
<Notifications>
|
||||||
|
<div
|
||||||
|
className="mx_UserNotifSettings"
|
||||||
|
>
|
||||||
|
<LabelledToggleSwitch
|
||||||
|
data-test-id="notif-master-switch"
|
||||||
|
disabled={false}
|
||||||
|
label="Enable for this account"
|
||||||
|
onChange={[Function]}
|
||||||
|
value={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_SettingsFlag "
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="mx_SettingsFlag_label"
|
||||||
|
>
|
||||||
|
Enable for this account
|
||||||
|
</span>
|
||||||
|
<_default
|
||||||
|
aria-label="Enable for this account"
|
||||||
|
checked={false}
|
||||||
|
disabled={false}
|
||||||
|
onChange={[Function]}
|
||||||
|
>
|
||||||
|
<AccessibleButton
|
||||||
|
aria-checked={false}
|
||||||
|
aria-disabled={false}
|
||||||
|
aria-label="Enable for this account"
|
||||||
|
className="mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||||
|
element="div"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="switch"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-checked={false}
|
||||||
|
aria-disabled={false}
|
||||||
|
aria-label="Enable for this account"
|
||||||
|
className="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="switch"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_ToggleSwitch_ball"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
</_default>
|
||||||
|
</div>
|
||||||
|
</LabelledToggleSwitch>
|
||||||
|
</div>
|
||||||
|
</Notifications>
|
||||||
|
`;
|
|
@ -113,6 +113,12 @@ export function createTestClient() {
|
||||||
registerWithIdentityServer: jest.fn().mockResolvedValue({}),
|
registerWithIdentityServer: jest.fn().mockResolvedValue({}),
|
||||||
getIdentityAccount: jest.fn().mockResolvedValue({}),
|
getIdentityAccount: jest.fn().mockResolvedValue({}),
|
||||||
getTerms: jest.fn().mockResolvedValueOnce(),
|
getTerms: jest.fn().mockResolvedValueOnce(),
|
||||||
|
getPushRules: jest.fn().mockResolvedValue(),
|
||||||
|
getPushers: jest.fn().mockResolvedValue({ pushers: [] }),
|
||||||
|
getThreePids: jest.fn().mockResolvedValue({ threepids: [] }),
|
||||||
|
setPusher: jest.fn().mockResolvedValue(),
|
||||||
|
setPushRuleEnabled: jest.fn().mockResolvedValue(),
|
||||||
|
setPushRuleActions: jest.fn().mockResolvedValue(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue