[MM-59296] Can't open web client on iOS Safari (#27607)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
db138fd23a
Коммит
1ff54a31bc
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {screen, waitFor} from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
|
||||||
|
import {requestNotificationPermission, isNotificationAPISupported} from 'utils/notifications';
|
||||||
|
|
||||||
|
import NotificationPermissionBar from './index';
|
||||||
|
|
||||||
|
jest.mock('utils/notifications', () => ({
|
||||||
|
requestNotificationPermission: jest.fn(),
|
||||||
|
isNotificationAPISupported: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('NotificationPermissionBar', () => {
|
||||||
|
const initialState = {
|
||||||
|
entities: {
|
||||||
|
users: {
|
||||||
|
currentUserId: 'user-id',
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
config: {},
|
||||||
|
license: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(isNotificationAPISupported as jest.Mock).mockReturnValue(true);
|
||||||
|
(window as any).Notification = {permission: 'default'};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
delete (window as any).Notification;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render the notification bar when conditions are met', () => {
|
||||||
|
renderWithContext(<NotificationPermissionBar/>, initialState);
|
||||||
|
|
||||||
|
expect(screen.getByText('We need your permission to show desktop notifications.')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Enable notifications')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not render the notification bar if user is not logged in', () => {
|
||||||
|
renderWithContext(<NotificationPermissionBar/>);
|
||||||
|
|
||||||
|
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Enable notifications')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not render the notification bar if Notifications are not supported', () => {
|
||||||
|
delete (window as any).Notification;
|
||||||
|
(isNotificationAPISupported as jest.Mock).mockReturnValue(false);
|
||||||
|
|
||||||
|
renderWithContext(<NotificationPermissionBar/>, initialState);
|
||||||
|
|
||||||
|
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Enable notifications')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should call requestNotificationPermission and hide the bar when the button is clicked', async () => {
|
||||||
|
(requestNotificationPermission as jest.Mock).mockResolvedValue('granted');
|
||||||
|
|
||||||
|
renderWithContext(<NotificationPermissionBar/>, initialState);
|
||||||
|
|
||||||
|
expect(screen.getByText('We need your permission to show desktop notifications.')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await waitFor(async () => {
|
||||||
|
userEvent.click(screen.getByText('Enable notifications'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(requestNotificationPermission).toHaveBeenCalled();
|
||||||
|
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,25 +10,25 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
|||||||
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
|
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
|
||||||
|
|
||||||
import {AnnouncementBarTypes} from 'utils/constants';
|
import {AnnouncementBarTypes} from 'utils/constants';
|
||||||
|
import {requestNotificationPermission, isNotificationAPISupported} from 'utils/notifications';
|
||||||
|
|
||||||
export default function NotificationPermissionBar() {
|
export default function NotificationPermissionBar() {
|
||||||
const isLoggedIn = Boolean(useSelector(getCurrentUserId));
|
const isLoggedIn = Boolean(useSelector(getCurrentUserId));
|
||||||
|
|
||||||
const [show, setShow] = useState(Notification.permission === 'default');
|
const [show, setShow] = useState(isNotificationAPISupported() ? Notification.permission === 'default' : false);
|
||||||
|
|
||||||
|
const handleClick = useCallback(async () => {
|
||||||
|
await requestNotificationPermission();
|
||||||
|
setShow(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
// If the user closes the bar, don't show the notification bar any more for the rest of the session, but
|
// If the user closes the bar, don't show the notification bar any more for the rest of the session, but
|
||||||
// show it again after refresh.
|
// show it again on app refresh.
|
||||||
setShow(false);
|
setShow(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
if (!show || !isLoggedIn || !isNotificationAPISupported()) {
|
||||||
Notification.requestPermission().then(() => {
|
|
||||||
setShow(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!show || !isLoggedIn) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ export default function NotificationPermissionBar() {
|
|||||||
type={AnnouncementBarTypes.ANNOUNCEMENT}
|
type={AnnouncementBarTypes.ANNOUNCEMENT}
|
||||||
message={
|
message={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='announcement_bar.notification.needs_permisson'
|
id='announcement_bar.notification.needs_permission'
|
||||||
defaultMessage='We need your permission to show desktop notifications.'
|
defaultMessage='We need your permission to show desktop notifications.'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2814,7 +2814,7 @@
|
|||||||
"announcement_bar.error.trial_license_expiring_last_day.short": "This is the last day of your free trial.",
|
"announcement_bar.error.trial_license_expiring_last_day.short": "This is the last day of your free trial.",
|
||||||
"announcement_bar.notification.email_verified": "Email verified",
|
"announcement_bar.notification.email_verified": "Email verified",
|
||||||
"announcement_bar.notification.enable_notifications": "Enable notifications",
|
"announcement_bar.notification.enable_notifications": "Enable notifications",
|
||||||
"announcement_bar.notification.needs_permisson": "We need your permission to show desktop notifications.",
|
"announcement_bar.notification.needs_permission": "We need your permission to show desktop notifications.",
|
||||||
"announcement_bar.warn.contact_support_text": "To renew your license, contact support at support@mattermost.com.",
|
"announcement_bar.warn.contact_support_text": "To renew your license, contact support at support@mattermost.com.",
|
||||||
"announcement_bar.warn.email_support": "[Contact support](!{email}).",
|
"announcement_bar.warn.email_support": "[Contact support](!{email}).",
|
||||||
"announcement_bar.warn.no_internet_connection": "Looks like you do not have access to the internet.",
|
"announcement_bar.warn.no_internet_connection": "Looks like you do not have access to the internet.",
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
|
|
||||||
// to enable being a typescript file
|
|
||||||
export const a = '';
|
|
||||||
import configureStore from 'tests/test_store';
|
import configureStore from 'tests/test_store';
|
||||||
|
|
||||||
import type {showNotification} from './notifications';
|
import type {showNotification} from './notifications';
|
||||||
|
import {isNotificationAPISupported, requestNotificationPermission} from './notifications';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -39,20 +38,20 @@ describe('Notifications.showNotification', () => {
|
|||||||
it('should throw an exception if Notification is not defined on window', async () => {
|
it('should throw an exception if Notification is not defined on window', async () => {
|
||||||
delete window.Notification;
|
delete window.Notification;
|
||||||
|
|
||||||
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification not supported');
|
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception if Notification.requestPermission is not defined', async () => {
|
it('should throw an exception if Notification.requestPermission is not defined', async () => {
|
||||||
window.Notification = jest.fn();
|
window.Notification = jest.fn();
|
||||||
|
|
||||||
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification.requestPermission not supported');
|
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception if Notification.requestPermission is not a function', async () => {
|
it('should throw an exception if Notification.requestPermission is not a function', async () => {
|
||||||
window.Notification = jest.fn();
|
window.Notification = jest.fn();
|
||||||
window.Notification.requestPermission = true;
|
window.Notification.requestPermission = true;
|
||||||
|
|
||||||
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification.requestPermission not supported');
|
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
|
||||||
expect(window.Notification).not.toHaveBeenCalled();
|
expect(window.Notification).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -241,3 +240,64 @@ describe('Notifications.showNotification', () => {
|
|||||||
expect(window.Notification.requestPermission).toHaveBeenCalledTimes(0);
|
expect(window.Notification.requestPermission).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Notifications.isNotificationAPISupported', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
window.Notification = {
|
||||||
|
requestPermission: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete (window as any).Notification;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if Notification is supported', () => {
|
||||||
|
expect(isNotificationAPISupported()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if Notification is not supported', () => {
|
||||||
|
delete (window as any).Notification;
|
||||||
|
|
||||||
|
expect(isNotificationAPISupported()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if requestPermission is not a function', () => {
|
||||||
|
(window as any).Notification = {};
|
||||||
|
|
||||||
|
expect(isNotificationAPISupported()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Notifications.requestNotificationPermission', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(window as any).Notification = {
|
||||||
|
requestPermission: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete (window as any).Notification;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the permission if Notification.requestPermission resolves', async () => {
|
||||||
|
(window as any).Notification.requestPermission = jest.fn().mockResolvedValue('granted');
|
||||||
|
|
||||||
|
const permission = await requestNotificationPermission();
|
||||||
|
expect(permission).toBe('granted');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null if Notification is not supported', async () => {
|
||||||
|
delete (window as any).Notification;
|
||||||
|
|
||||||
|
const permission = await requestNotificationPermission();
|
||||||
|
expect(permission).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null if requestPermission throws an error', async () => {
|
||||||
|
(window as any).Notification.requestPermission = jest.fn().mockRejectedValue('some error');
|
||||||
|
|
||||||
|
const permission = await requestNotificationPermission();
|
||||||
|
expect(permission).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -50,12 +50,8 @@ export function showNotification(
|
|||||||
icon = iconWS;
|
icon = iconWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!('Notification' in window)) {
|
if (!isNotificationAPISupported()) {
|
||||||
throw new Error('Notification not supported');
|
throw new Error('Notification API is not supported');
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof Notification.requestPermission !== 'function') {
|
|
||||||
throw new Error('Notification.requestPermission not supported');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Notification.permission !== 'granted') {
|
if (Notification.permission !== 'granted') {
|
||||||
@@ -104,3 +100,20 @@ export function showNotification(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNotificationAPISupported() {
|
||||||
|
return ('Notification' in window) && (typeof Notification.requestPermission === 'function');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestNotificationPermission() {
|
||||||
|
if (!isNotificationAPISupported()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const notificationPermission = await Notification.requestPermission();
|
||||||
|
return notificationPermission;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user