diff --git a/frontend/cypress/e2e/motd.spec.ts b/frontend/cypress/e2e/motd.spec.ts index 7507af047..db1a1f03b 100644 --- a/frontend/cypress/e2e/motd.spec.ts +++ b/frontend/cypress/e2e/motd.spec.ts @@ -4,19 +4,20 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -const MOTD_LOCAL_STORAGE_KEY = 'motd.lastModified' +import { MOTD_LOCAL_STORAGE_KEY } from '../../src/components/global-dialogs/motd-modal/local-storage-keys' + const motdMockHtml = 'This is the test motd text' describe('Motd', () => { it("shows, dismisses and won't show again a motd modal", () => { - localStorage.removeItem(MOTD_LOCAL_STORAGE_KEY) + window.localStorage.removeItem(MOTD_LOCAL_STORAGE_KEY) cy.visitHistory() cy.getSimpleRendererBody().should('contain.text', motdMockHtml) cy.getByCypressId('motd-dismiss') .click() .then(() => { - expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).not.to.be.eq(null) + expect(window.localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).not.to.be.eq(null) }) cy.getByCypressId('motd-modal').should('not.exist') cy.reload() diff --git a/frontend/cypress/support/config.ts b/frontend/cypress/support/config.ts index 1f52d6e07..7522103f4 100644 --- a/frontend/cypress/support/config.ts +++ b/frontend/cypress/support/config.ts @@ -5,6 +5,7 @@ */ import { AuthProviderType } from '../../src/api/config/types' import { HttpMethod } from '../../src/handler-utils/respond-to-matching-request' +import { IGNORE_MOTD, MOTD_LOCAL_STORAGE_KEY } from '../../src/components/global-dialogs/motd-modal/local-storage-keys' declare namespace Cypress { interface Chainable { @@ -87,6 +88,7 @@ Cypress.Commands.add('logOut', () => { beforeEach(() => { cy.loadConfig() + window.localStorage.setItem(MOTD_LOCAL_STORAGE_KEY, IGNORE_MOTD) cy.logIn() cy.intercept('GET', '/public/motd.md', { diff --git a/frontend/src/components/global-dialogs/motd-modal/cached-motd-modal.tsx b/frontend/src/components/global-dialogs/motd-modal/cached-motd-modal.tsx index 96a21eeac..9039943e8 100644 --- a/frontend/src/components/global-dialogs/motd-modal/cached-motd-modal.tsx +++ b/frontend/src/components/global-dialogs/motd-modal/cached-motd-modal.tsx @@ -8,9 +8,10 @@ import React, { useCallback, useMemo, useState } from 'react' import { useMotdContextValue } from '../../motd/motd-context' import { useLocalStorage } from 'react-use' -import { MOTD_LOCAL_STORAGE_KEY } from './fetch-motd' import { MotdModal } from './motd-modal' import { testId } from '../../../utils/test-id' +import { isTestMode } from '../../../utils/test-modes' +import { IGNORE_MOTD, MOTD_LOCAL_STORAGE_KEY } from './local-storage-keys' /** * Reads the motd from the context and shows it in a modal. @@ -19,13 +20,22 @@ import { testId } from '../../../utils/test-id' */ export const CachedMotdModal: React.FC = () => { const contextValue = useMotdContextValue() - const [cachedLastModified, saveLocalStorage] = useLocalStorage(MOTD_LOCAL_STORAGE_KEY, undefined) + const [cachedLastModified, saveLocalStorage] = useLocalStorage(MOTD_LOCAL_STORAGE_KEY, undefined, { + raw: true + }) const [dismissed, setDismissed] = useState(false) const show = useMemo(() => { const lastModified = contextValue?.lastModified - return cachedLastModified !== lastModified && lastModified !== undefined && !dismissed + + if (cachedLastModified === IGNORE_MOTD && isTestMode) { + return false + } + if (cachedLastModified === lastModified || lastModified === undefined) { + return false + } + return !dismissed }, [cachedLastModified, contextValue?.lastModified, dismissed]) const doDismiss = useCallback(() => { diff --git a/frontend/src/components/global-dialogs/motd-modal/fetch-motd.ts b/frontend/src/components/global-dialogs/motd-modal/fetch-motd.ts index a3474be5a..bf0255d15 100644 --- a/frontend/src/components/global-dialogs/motd-modal/fetch-motd.ts +++ b/frontend/src/components/global-dialogs/motd-modal/fetch-motd.ts @@ -5,8 +5,6 @@ */ import { defaultConfig } from '../../../api/common/default-config' -export const MOTD_LOCAL_STORAGE_KEY = 'motd.lastModified' - export interface MotdApiResponse { motdText: string lastModified: string diff --git a/frontend/src/components/global-dialogs/motd-modal/local-storage-keys.ts b/frontend/src/components/global-dialogs/motd-modal/local-storage-keys.ts new file mode 100644 index 000000000..35aba04c2 --- /dev/null +++ b/frontend/src/components/global-dialogs/motd-modal/local-storage-keys.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const MOTD_LOCAL_STORAGE_KEY: string = 'motd.lastModified' +export const IGNORE_MOTD: string = 'IGNORE_MOTD' diff --git a/frontend/src/handler-utils/respond-to-matching-request.ts b/frontend/src/handler-utils/respond-to-matching-request.ts index b28230ce9..6e3eb344b 100644 --- a/frontend/src/handler-utils/respond-to-matching-request.ts +++ b/frontend/src/handler-utils/respond-to-matching-request.ts @@ -62,7 +62,10 @@ export const respondToTestRequest = (req: NextApiRequest, res: NextApiRespons res.status(405).send('Method not allowed') } else if (!isTestMode) { res.status(404).send('Route only available in test mode') - } else if (!['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(req.socket.remoteAddress)) { + } else if ( + req.socket.remoteAddress === undefined || + !['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(req.socket.remoteAddress) + ) { res.status(403).send(`Request must come from localhost but was ${req.socket.remoteAddress}`) } else { res.status(200).json(response())