mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-28 14:10:57 -05:00
tests(permissions): add cypress tests for permission alert icon
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
6d69b294cb
commit
5ccc9059f0
7 changed files with 182 additions and 28 deletions
115
frontend/cypress/e2e/permissions.spec.ts
Normal file
115
frontend/cypress/e2e/permissions.spec.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import type { NotePermissions } from '@hedgedoc/commons'
|
||||||
|
|
||||||
|
const mockPermissionChangeApiRoutes = (permission: NotePermissions) => {
|
||||||
|
cy.intercept('PUT', 'api/private/notes/mock-note/metadata/permissions/groups/_EVERYONE', {
|
||||||
|
statusCode: 200,
|
||||||
|
body: permission
|
||||||
|
})
|
||||||
|
cy.intercept('DELETE', 'api/private/notes/mock-note/metadata/permissions/groups/_EVERYONE', {
|
||||||
|
statusCode: 200,
|
||||||
|
body: permission
|
||||||
|
})
|
||||||
|
cy.intercept('PUT', 'api/private/notes/mock-note/metadata/permissions/groups/_LOGGED_IN', {
|
||||||
|
statusCode: 200,
|
||||||
|
body: permission
|
||||||
|
})
|
||||||
|
cy.intercept('DELETE', 'api/private/notes/mock-note/metadata/permissions/groups/_LOGGED_IN', {
|
||||||
|
statusCode: 200,
|
||||||
|
body: permission
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('The permission settings modal', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visitTestNote()
|
||||||
|
cy.getByCypressId('sidebar-permission-btn').click()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can be displayed', () => {
|
||||||
|
cy.getByCypressId('permission-modal').should('be.visible')
|
||||||
|
cy.getByCypressId('permission-owner-name').contains('Mock User')
|
||||||
|
cy.getByCypressId('permission-setting-deny_LOGGED_IN').should('have.class', 'btn-secondary')
|
||||||
|
cy.getByCypressId('permission-setting-deny_EVERYONE').should('have.class', 'btn-secondary')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows alert icon on invalid settings in special groups', () => {
|
||||||
|
cy.getByCypressId('permission-setting-deny_LOGGED_IN').should('have.class', 'btn-secondary')
|
||||||
|
mockPermissionChangeApiRoutes({
|
||||||
|
owner: 'mock',
|
||||||
|
sharedToUsers: [],
|
||||||
|
sharedToGroups: [
|
||||||
|
{
|
||||||
|
groupName: '_EVERYONE',
|
||||||
|
canEdit: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
cy.getByCypressId('permission-setting-read_EVERYONE').click()
|
||||||
|
cy.get('svg.text-warning.me-2').should('be.visible')
|
||||||
|
mockPermissionChangeApiRoutes({
|
||||||
|
owner: 'mock',
|
||||||
|
sharedToUsers: [],
|
||||||
|
sharedToGroups: [
|
||||||
|
{
|
||||||
|
groupName: '_EVERYONE',
|
||||||
|
canEdit: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
cy.getByCypressId('permission-setting-write_EVERYONE').click()
|
||||||
|
cy.get('svg.text-warning.me-2').should('be.visible')
|
||||||
|
mockPermissionChangeApiRoutes({
|
||||||
|
owner: 'mock',
|
||||||
|
sharedToUsers: [],
|
||||||
|
sharedToGroups: [
|
||||||
|
{
|
||||||
|
groupName: '_EVERYONE',
|
||||||
|
canEdit: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupName: '_LOGGED_IN',
|
||||||
|
canEdit: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
cy.getByCypressId('permission-setting-read_LOGGED_IN').click()
|
||||||
|
cy.get('svg.text-warning.me-2').should('not.exist')
|
||||||
|
mockPermissionChangeApiRoutes({
|
||||||
|
owner: 'mock',
|
||||||
|
sharedToUsers: [],
|
||||||
|
sharedToGroups: [
|
||||||
|
{
|
||||||
|
groupName: '_EVERYONE',
|
||||||
|
canEdit: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupName: '_LOGGED_IN',
|
||||||
|
canEdit: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
cy.getByCypressId('permission-setting-write_EVERYONE').click()
|
||||||
|
cy.get('svg.text-warning.me-2').should('be.visible')
|
||||||
|
mockPermissionChangeApiRoutes({
|
||||||
|
owner: 'mock',
|
||||||
|
sharedToUsers: [],
|
||||||
|
sharedToGroups: [
|
||||||
|
{
|
||||||
|
groupName: '_EVERYONE',
|
||||||
|
canEdit: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupName: '_LOGGED_IN',
|
||||||
|
canEdit: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
cy.getByCypressId('permission-setting-write_LOGGED_IN').click()
|
||||||
|
cy.get('svg.text-warning.me-2').should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
|
@ -6,11 +6,7 @@
|
||||||
import type { Note } from '../../src/api/notes/types'
|
import type { Note } from '../../src/api/notes/types'
|
||||||
|
|
||||||
export const testNoteId = 'test'
|
export const testNoteId = 'test'
|
||||||
|
const mockMetadata = {
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept(`api/private/notes/${testNoteId}`, {
|
|
||||||
content: '',
|
|
||||||
metadata: {
|
|
||||||
id: testNoteId,
|
id: testNoteId,
|
||||||
aliases: [
|
aliases: [
|
||||||
{
|
{
|
||||||
|
@ -34,7 +30,13 @@ beforeEach(() => {
|
||||||
sharedToUsers: [],
|
sharedToUsers: [],
|
||||||
sharedToGroups: []
|
sharedToGroups: []
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.intercept(`api/private/notes/${testNoteId}`, {
|
||||||
|
content: '',
|
||||||
|
metadata: mockMetadata,
|
||||||
editedByAtPosition: []
|
editedByAtPosition: []
|
||||||
} as Note)
|
} as Note)
|
||||||
|
cy.intercept(`api/private/notes/${testNoteId}/metadata`, mockMetadata)
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { ToggleButtonGroup } from 'react-bootstrap'
|
||||||
import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
|
import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
|
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
|
||||||
|
import { cypressId } from '../../../../../../utils/cypress-attribute'
|
||||||
|
|
||||||
export interface PermissionEntrySpecialGroupProps {
|
export interface PermissionEntrySpecialGroupProps {
|
||||||
level: AccessLevel
|
level: AccessLevel
|
||||||
|
@ -101,6 +102,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
||||||
onClick={onSetEntryDenied}
|
onClick={onSetEntryDenied}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={'p-1'}
|
className={'p-1'}
|
||||||
|
{...cypressId(`permission-setting-deny${type}`)}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={IconEye}
|
icon={IconEye}
|
||||||
|
@ -109,6 +111,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
||||||
onClick={onSetEntryReadOnly}
|
onClick={onSetEntryReadOnly}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={'p-1'}
|
className={'p-1'}
|
||||||
|
{...cypressId(`permission-setting-read${type}`)}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={IconPencil}
|
icon={IconPencil}
|
||||||
|
@ -117,6 +120,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
|
||||||
onClick={onSetEntryWriteable}
|
onClick={onSetEntryWriteable}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={'p-1'}
|
className={'p-1'}
|
||||||
|
{...cypressId(`permission-setting-write${type}`)}
|
||||||
/>
|
/>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { PermissionSectionSpecialGroups } from './permission-section-special-gro
|
||||||
import { PermissionSectionUsers } from './permission-section-users'
|
import { PermissionSectionUsers } from './permission-section-users'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Modal } from 'react-bootstrap'
|
import { Modal } from 'react-bootstrap'
|
||||||
|
import { cypressId } from '../../../../../../utils/cypress-attribute'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modal for viewing and managing the permissions of the note.
|
* Modal for viewing and managing the permissions of the note.
|
||||||
|
@ -21,7 +22,12 @@ import { Modal } from 'react-bootstrap'
|
||||||
export const PermissionModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
|
export const PermissionModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
|
||||||
const isOwner = useIsOwner()
|
const isOwner = useIsOwner()
|
||||||
return (
|
return (
|
||||||
<CommonModal show={show} onHide={onHide} showCloseButton={true} titleI18nKey={'editor.modal.permissions.title'}>
|
<CommonModal
|
||||||
|
show={show}
|
||||||
|
onHide={onHide}
|
||||||
|
showCloseButton={true}
|
||||||
|
titleI18nKey={'editor.modal.permissions.title'}
|
||||||
|
{...cypressId('permission-modal')}>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<PermissionSectionOwner disabled={!isOwner} />
|
<PermissionSectionOwner disabled={!isOwner} />
|
||||||
<PermissionSectionUsers disabled={!isOwner} />
|
<PermissionSectionUsers disabled={!isOwner} />
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { PermissionOwnerChange } from './permission-owner-change'
|
||||||
import { PermissionOwnerInfo } from './permission-owner-info'
|
import { PermissionOwnerInfo } from './permission-owner-info'
|
||||||
import React, { Fragment, useCallback, useState } from 'react'
|
import React, { Fragment, useCallback, useState } from 'react'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
|
import { cypressId } from '../../../../../../utils/cypress-attribute'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Section in the permissions modal for managing the owner of a note.
|
* Section in the permissions modal for managing the owner of a note.
|
||||||
|
@ -50,7 +51,9 @@ export const PermissionSectionOwner: React.FC<PermissionDisabledProps> = ({ disa
|
||||||
<Trans i18nKey={'editor.modal.permissions.owner'} />
|
<Trans i18nKey={'editor.modal.permissions.owner'} />
|
||||||
</h5>
|
</h5>
|
||||||
<ul className={'list-group'}>
|
<ul className={'list-group'}>
|
||||||
<li className={'list-group-item d-flex flex-row align-items-center justify-content-between'}>
|
<li
|
||||||
|
className={'list-group-item d-flex flex-row align-items-center justify-content-between'}
|
||||||
|
{...cypressId('permission-owner-name')}>
|
||||||
{changeOwner ? (
|
{changeOwner ? (
|
||||||
<PermissionOwnerChange onConfirmOwnerChange={onOwnerChange} />
|
<PermissionOwnerChange onConfirmOwnerChange={onOwnerChange} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { PermissionModal } from './permissions-modal/permission-modal'
|
||||||
import React, { Fragment } from 'react'
|
import React, { Fragment } from 'react'
|
||||||
import { Lock as IconLock } from 'react-bootstrap-icons'
|
import { Lock as IconLock } from 'react-bootstrap-icons'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import { cypressId } from '../../../../../utils/cypress-attribute'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a button to open the permission modal for the sidebar.
|
* Renders a button to open the permission modal for the sidebar.
|
||||||
|
@ -23,7 +24,12 @@ export const PermissionsSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ c
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<SidebarButton hide={hide} className={className} icon={IconLock} onClick={showModal}>
|
<SidebarButton
|
||||||
|
hide={hide}
|
||||||
|
className={className}
|
||||||
|
icon={IconLock}
|
||||||
|
onClick={showModal}
|
||||||
|
{...cypressId('sidebar-permission-btn')}>
|
||||||
<Trans i18nKey={'editor.modal.permissions.title'} />
|
<Trans i18nKey={'editor.modal.permissions.title'} />
|
||||||
</SidebarButton>
|
</SidebarButton>
|
||||||
<PermissionModal show={modalVisibility} onHide={closeModal} />
|
<PermissionModal show={modalVisibility} onHide={closeModal} />
|
||||||
|
|
18
frontend/src/pages/api/private/users/mock.ts
Normal file
18
frontend/src/pages/api/private/users/mock.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import type { UserInfo } from '../../../../api/users/types'
|
||||||
|
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
|
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
|
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
||||||
|
respondToMatchingRequest<UserInfo>(HttpMethod.GET, req, res, {
|
||||||
|
username: 'mock',
|
||||||
|
displayName: 'Mock User',
|
||||||
|
photoUrl: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default handler
|
Loading…
Reference in a new issue