mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-06 07:39:02 +02:00
[web] open project notification settings from URL (#32689)
GitOrigin-RevId: 8d6e1c2d7cb63c1597d952d546121f030b79d126
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
useSettingsModalContext,
|
||||
} from '../context/settings-modal-context'
|
||||
import useFocusOnSetting from '../hooks/use-focus-on-setting'
|
||||
import useOpenSettingsViaQueryParam from '../hooks/use-open-settings-via-query-param'
|
||||
|
||||
const SettingsModalWrapper = () => {
|
||||
return (
|
||||
@@ -26,6 +27,7 @@ const SettingsModal = () => {
|
||||
useSettingsModalContext()
|
||||
|
||||
useFocusOnSetting()
|
||||
useOpenSettingsViaQueryParam()
|
||||
|
||||
return (
|
||||
<OLModal
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useSettingsModalContext } from '../context/settings-modal-context'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
|
||||
export default function useOpenSettingsViaQueryParam() {
|
||||
const { setShow, setActiveTab } = useSettingsModalContext()
|
||||
|
||||
useEffect(() => {
|
||||
const inNotificationsSplitTest = isSplitTestEnabled('email-notifications')
|
||||
if (!inNotificationsSplitTest) {
|
||||
return
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
if (params.get('open') !== 'project-notifications') {
|
||||
return
|
||||
}
|
||||
|
||||
setShow(true)
|
||||
setActiveTab('project_notifications')
|
||||
|
||||
const url = new URL(window.location.href)
|
||||
url.searchParams.delete('open')
|
||||
window.history.replaceState(window.history.state, '', url.toString())
|
||||
}, [setShow, setActiveTab])
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { screen, render } from '@testing-library/react'
|
||||
import { screen, render, waitFor } from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import { EditorProviders } from '../../helpers/editor-providers'
|
||||
@@ -131,4 +131,60 @@ describe('<SettingsModal />', function () {
|
||||
settings.forEach(setting => assertSettingIsVisible(setting))
|
||||
})
|
||||
})
|
||||
|
||||
describe('when open=project-notifications query param is present', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-splitTestVariants', {
|
||||
'email-notifications': 'enabled',
|
||||
})
|
||||
fetchMock.get(/\/notifications\/preferences\/project\//, {
|
||||
trackedChangesOnOwnProject: false,
|
||||
trackedChangesOnInvitedProject: false,
|
||||
commentOnOwnProject: false,
|
||||
commentOnInvitedProject: false,
|
||||
repliesOnOwnProject: false,
|
||||
repliesOnInvitedProject: false,
|
||||
repliesOnAuthoredThread: false,
|
||||
repliesOnParticipatingThread: false,
|
||||
})
|
||||
window.history.pushState({}, '', '?open=project-notifications')
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
window.history.pushState({}, '', window.location.pathname)
|
||||
window.metaAttributesCache.delete('ol-splitTestVariants')
|
||||
})
|
||||
|
||||
it('opens the modal and selects the Project notifications tab', async function () {
|
||||
render(
|
||||
<EditorProviders rootFolder={[rootFolder as any]}>
|
||||
<SettingsModal />
|
||||
</EditorProviders>
|
||||
)
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
screen.getByRole('tab', {
|
||||
name: /Project notifications/,
|
||||
selected: true,
|
||||
})
|
||||
).to.exist
|
||||
)
|
||||
})
|
||||
|
||||
it('removes the open param from the URL', async function () {
|
||||
render(
|
||||
<EditorProviders rootFolder={[rootFolder as any]}>
|
||||
<SettingsModal />
|
||||
</EditorProviders>
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.location.search).to.not.include(
|
||||
'open=project-notifications'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
import { screen, render, waitFor } from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { EditorProviders, PROJECT_ID } from '../../../helpers/editor-providers'
|
||||
import { SettingsModalProvider } from '@/features/settings/context/settings-modal-context'
|
||||
import ProjectNotificationsSetting from '@/features/settings/components/editor-settings/project-notifications-setting'
|
||||
|
||||
const preferencesUrl = `/notifications/preferences/project/${PROJECT_ID}`
|
||||
|
||||
const allNotificationsOn = {
|
||||
trackedChangesOnOwnProject: true,
|
||||
trackedChangesOnInvitedProject: true,
|
||||
commentOnOwnProject: true,
|
||||
commentOnInvitedProject: true,
|
||||
repliesOnOwnProject: true,
|
||||
repliesOnInvitedProject: true,
|
||||
repliesOnAuthoredThread: true,
|
||||
repliesOnParticipatingThread: true,
|
||||
}
|
||||
|
||||
const repliesOnlyPreferences = {
|
||||
trackedChangesOnOwnProject: false,
|
||||
trackedChangesOnInvitedProject: false,
|
||||
commentOnOwnProject: false,
|
||||
commentOnInvitedProject: false,
|
||||
repliesOnOwnProject: false,
|
||||
repliesOnInvitedProject: false,
|
||||
repliesOnAuthoredThread: true,
|
||||
repliesOnParticipatingThread: true,
|
||||
}
|
||||
|
||||
const allNotificationsOff = {
|
||||
trackedChangesOnOwnProject: false,
|
||||
trackedChangesOnInvitedProject: false,
|
||||
commentOnOwnProject: false,
|
||||
commentOnInvitedProject: false,
|
||||
repliesOnOwnProject: false,
|
||||
repliesOnInvitedProject: false,
|
||||
repliesOnAuthoredThread: false,
|
||||
repliesOnParticipatingThread: false,
|
||||
}
|
||||
|
||||
function renderComponent() {
|
||||
return render(
|
||||
<EditorProviders>
|
||||
<SettingsModalProvider>
|
||||
<ProjectNotificationsSetting />
|
||||
</SettingsModalProvider>
|
||||
</EditorProviders>
|
||||
)
|
||||
}
|
||||
|
||||
describe('<ProjectNotificationsSetting />', function () {
|
||||
afterEach(function () {
|
||||
fetchMock.removeRoutes().clearHistory()
|
||||
})
|
||||
|
||||
it('selects "All project activity" when all notifications are on', async function () {
|
||||
fetchMock.get(preferencesUrl, allNotificationsOn)
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('All project activity', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.true
|
||||
)
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('Replies to your activity only', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.false
|
||||
expect(
|
||||
(screen.getByLabelText('Off', { exact: false }) as HTMLInputElement)
|
||||
.checked
|
||||
).to.be.false
|
||||
})
|
||||
|
||||
it('selects "Replies to your activity only" when only reply notifications are on', async function () {
|
||||
fetchMock.get(preferencesUrl, repliesOnlyPreferences)
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('Replies to your activity only', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.true
|
||||
)
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('All project activity', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.false
|
||||
expect(
|
||||
(screen.getByLabelText('Off', { exact: false }) as HTMLInputElement)
|
||||
.checked
|
||||
).to.be.false
|
||||
})
|
||||
|
||||
it('selects "Off" when all notifications are off', async function () {
|
||||
fetchMock.get(preferencesUrl, allNotificationsOff)
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(screen.getByLabelText('Off', { exact: false }) as HTMLInputElement)
|
||||
.checked
|
||||
).to.be.true
|
||||
)
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('All project activity', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.false
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('Replies to your activity only', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.false
|
||||
})
|
||||
|
||||
it('POSTs "replies" preferences when "Replies to your activity only" is selected', async function () {
|
||||
fetchMock.get(preferencesUrl, allNotificationsOn)
|
||||
const saveMock = fetchMock.post(preferencesUrl, { status: 200 })
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('All project activity', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.true
|
||||
)
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByLabelText('Replies to your activity only', { exact: false })
|
||||
)
|
||||
|
||||
expect(
|
||||
saveMock.callHistory.called(preferencesUrl, {
|
||||
body: repliesOnlyPreferences,
|
||||
})
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('POSTs "off" preferences when "Off" is selected', async function () {
|
||||
fetchMock.get(preferencesUrl, allNotificationsOn)
|
||||
const saveMock = fetchMock.post(preferencesUrl, { status: 200 })
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(
|
||||
screen.getByLabelText('All project activity', {
|
||||
exact: false,
|
||||
}) as HTMLInputElement
|
||||
).checked
|
||||
).to.be.true
|
||||
)
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Off', { exact: false }))
|
||||
|
||||
expect(
|
||||
saveMock.callHistory.called(preferencesUrl, {
|
||||
body: allNotificationsOff,
|
||||
})
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('POSTs "all" preferences when "All project activity" is selected', async function () {
|
||||
fetchMock.get(preferencesUrl, allNotificationsOff)
|
||||
const saveMock = fetchMock.post(preferencesUrl, { status: 200 })
|
||||
|
||||
renderComponent()
|
||||
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
(screen.getByLabelText('Off', { exact: false }) as HTMLInputElement)
|
||||
.checked
|
||||
).to.be.true
|
||||
)
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByLabelText('All project activity', { exact: false })
|
||||
)
|
||||
|
||||
expect(
|
||||
saveMock.callHistory.called(preferencesUrl, {
|
||||
body: allNotificationsOn,
|
||||
})
|
||||
).to.be.true
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user