feat(frontend): deactivate alias buttons if user is not owner

These buttons and their functionality only work if the user is the owner, so it doesn't make sense to make it possible to press them otherwise…

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2023-03-24 16:10:34 +01:00 committed by Tilman Vatteroth
parent 107ec7a522
commit 09e56a418e
5 changed files with 122 additions and 5 deletions

View file

@ -1,5 +1,65 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AliasesListEntry disables button in AliasesListEntry if it's not primary 1`] = `
<div>
<li
class="list-group-item d-flex flex-row justify-content-between align-items-center"
>
test-primary
<div>
<button
class="me-2 btn btn-light"
data-testid="aliasButtonMakePrimary"
disabled=""
title="editor.modal.aliases.makePrimary"
type="button"
>
BootstrapIconMock_StarFill
</button>
<button
class="text-danger btn btn-light"
data-testid="aliasButtonRemove"
disabled=""
title="editor.modal.aliases.removeAlias"
type="button"
>
BootstrapIconMock_X
</button>
</div>
</li>
</div>
`;
exports[`AliasesListEntry disables button in AliasesListEntry if it's primary 1`] = `
<div>
<li
class="list-group-item d-flex flex-row justify-content-between align-items-center"
>
test-primary
<div>
<button
class="me-2 text-warning btn btn-light"
data-testid="aliasIsPrimary"
disabled=""
title="editor.modal.aliases.isPrimary"
type="button"
>
BootstrapIconMock_Star
</button>
<button
class="text-danger btn btn-light"
data-testid="aliasButtonRemove"
disabled=""
title="editor.modal.aliases.removeAlias"
type="button"
>
BootstrapIconMock_X
</button>
</div>
</li>
</div>
`;
exports[`AliasesListEntry renders an AliasesListEntry that is not primary 1`] = `
<div>
<li

View file

@ -1,10 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { addAlias } from '../../../../api/alias'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
import { useIsOwner } from '../../../../hooks/common/use-is-owner'
import { useOnInputChange } from '../../../../hooks/common/use-on-input-change'
import { updateMetadata } from '../../../../redux/note-details/methods'
import { testId } from '../../../../utils/test-id'
@ -25,6 +26,7 @@ export const AliasesAddForm: React.FC = () => {
const { t } = useTranslation()
const { showErrorNotification } = useUiNotifications()
const noteId = useApplicationState((state) => state.noteDetails.id)
const isOwner = useIsOwner()
const [newAlias, setNewAlias] = useState('')
const onAddAlias = useCallback(
@ -54,6 +56,7 @@ export const AliasesAddForm: React.FC = () => {
placeholder={t('editor.modal.aliases.addAlias') ?? undefined}
onChange={onNewAliasInputChange}
isInvalid={!newAliasValid}
disabled={!isOwner}
required={true}
{...testId('addAliasInput')}
/>
@ -61,7 +64,7 @@ export const AliasesAddForm: React.FC = () => {
type={'submit'}
variant='light'
className={'text-secondary ms-2'}
disabled={!newAliasValid || newAlias === ''}
disabled={!isOwner || !newAliasValid || newAlias === ''}
title={t('editor.modal.aliases.addAlias') ?? undefined}
{...testId('addAliasButton')}>
<UiIcon icon={IconPlus} />

View file

@ -1,11 +1,12 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as AliasModule from '../../../../api/alias'
import type { Alias } from '../../../../api/alias/types'
import * as NoteDetailsReduxModule from '../../../../redux/note-details/methods'
import { mockNoteOwnership } from '../../../../test-utils/note-ownership'
import { mockI18n } from '../../../markdown-renderer/test-utils/mock-i18n'
import * as useUiNotificationsModule from '../../../notifications/ui-notification-boundary'
import { AliasesListEntry } from './aliases-list-entry'
@ -15,6 +16,8 @@ import React from 'react'
jest.mock('../../../../api/alias')
jest.mock('../../../../redux/note-details/methods')
jest.mock('../../../notifications/ui-notification-boundary')
// This needs to be mocked here in addition to note-ownership.ts, because jest doesn't work otherwise
jest.mock('../../../../hooks/common/use-application-state')
const deletePromise = Promise.resolve()
const markAsPrimaryPromise = Promise.resolve({ name: 'mock', primaryAlias: true, noteId: 'mock' })
@ -32,12 +35,13 @@ describe('AliasesListEntry', () => {
})
})
afterAll(() => {
afterEach(() => {
jest.resetAllMocks()
jest.resetModules()
})
it('renders an AliasesListEntry that is primary', async () => {
mockNoteOwnership('test', 'test')
const testAlias: Alias = {
name: 'test-primary',
primaryAlias: true,
@ -54,7 +58,19 @@ describe('AliasesListEntry', () => {
expect(NoteDetailsReduxModule.updateMetadata).toBeCalled()
})
it("disables button in AliasesListEntry if it's primary", () => {
mockNoteOwnership('test2', 'test')
const testAlias: Alias = {
name: 'test-primary',
primaryAlias: true,
noteId: 'test-note-id'
}
const view = render(<AliasesListEntry alias={testAlias} />)
expect(view.container).toMatchSnapshot()
})
it('renders an AliasesListEntry that is not primary', async () => {
mockNoteOwnership('test', 'test')
const testAlias: Alias = {
name: 'test-non-primary',
primaryAlias: false,
@ -77,4 +93,15 @@ describe('AliasesListEntry', () => {
await markAsPrimaryPromise
expect(NoteDetailsReduxModule.updateMetadata).toBeCalled()
})
it("disables button in AliasesListEntry if it's not primary", () => {
mockNoteOwnership('test2', 'test')
const testAlias: Alias = {
name: 'test-primary',
primaryAlias: false,
noteId: 'test-note-id'
}
const view = render(<AliasesListEntry alias={testAlias} />)
expect(view.container).toMatchSnapshot()
})
})

View file

@ -1,10 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { deleteAlias, markAliasAsPrimary } from '../../../../api/alias'
import type { Alias } from '../../../../api/alias/types'
import { useIsOwner } from '../../../../hooks/common/use-is-owner'
import { updateMetadata } from '../../../../redux/note-details/methods'
import { testId } from '../../../../utils/test-id'
import { UiIcon } from '../../../common/icons/ui-icon'
@ -29,6 +30,7 @@ export interface AliasesListEntryProps {
export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) => {
const { t } = useTranslation()
const { showErrorNotification } = useUiNotifications()
const isOwner = useIsOwner()
const onRemoveClick = useCallback(() => {
deleteAlias(alias.name)
@ -60,6 +62,7 @@ export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) =>
<Button
className={'me-2'}
variant='light'
disabled={!isOwner}
title={t('editor.modal.aliases.makePrimary') ?? undefined}
onClick={onMakePrimaryClick}
{...testId('aliasButtonMakePrimary')}>
@ -69,6 +72,7 @@ export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) =>
<Button
variant='light'
className={'text-danger'}
disabled={!isOwner}
title={t('editor.modal.aliases.removeAlias') ?? undefined}
onClick={onRemoveClick}
{...testId('aliasButtonRemove')}>

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as useApplicationStateModule from '../hooks/common/use-application-state'
import type { ApplicationState } from '../redux/application-state'
jest.mock('../hooks/common/use-application-state')
export const mockNoteOwnership = (ownUsername: string, noteOwner: string) => {
jest.spyOn(useApplicationStateModule, 'useApplicationState').mockImplementation((fn) => {
return fn({
noteDetails: {
permissions: {
owner: noteOwner
}
},
user: {
username: ownUsername
}
} as ApplicationState)
})
}