Merge pull request #24313 from overleaf/td-ac-bs5-frontend-tests

Update front-end tests to use Bootstrap 5

GitOrigin-RevId: abaa09f8c0639d64d6ade97468ab16204e5de97b
This commit is contained in:
Tim Down
2025-03-19 11:48:41 +00:00
committed by Copybot
parent b8d74c6ae0
commit c2da12939e
56 changed files with 358 additions and 264 deletions

View File

@@ -836,6 +836,7 @@
"join_project": "",
"join_team_explanation": "",
"joined_team": "",
"joining": "",
"justify": "",
"kb_suggestions_enquiry": "",
"keep_current_plan": "",

View File

@@ -15,6 +15,7 @@ function DictionaryModal({ show, handleHide }: DictionaryModalProps) {
show={show}
onHide={handleHide}
id="dictionary-modal"
data-testid="dictionary-modal"
size="sm"
>
<DictionaryModalContent handleHide={handleHide} />

View File

@@ -96,6 +96,7 @@ function CommonNotification({ notification }: CommonNotificationProps) {
<OLButton
variant="secondary"
isLoading={isLoading}
loadingLabel={t('joining')}
disabled={isLoading}
onClick={() => handleAcceptInvite(notification)}
>

View File

@@ -145,6 +145,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
type="submit"
disabled={!role || !department}
isLoading={isLoading}
loadingLabel={t('saving')}
bs3Props={{
loading: isLoading
? `${t('saving')}`

View File

@@ -203,6 +203,7 @@ function PasswordForm() {
variant="primary"
disabled={!isFormValid}
isLoading={isLoading}
loadingLabel={`${t('saving')}`}
bs3Props={{
loading: isLoading ? `${t('saving')}` : t('change'),
}}

View File

@@ -70,13 +70,14 @@ export default function LeaveGroupModal() {
onClick={handleConfirmLeaveGroup}
disabled={inflight}
isLoading={inflight}
loadingLabel={t('processing_uppercase') + '…'}
bs3Props={{
loading: inflight
? t('processing_uppercase') + '…'
: t('leave_now'),
}}
>
{t('processing_uppercase')}
{t('leave_now')}
</OLButton>
</OLModalFooter>
</OLModal>

View File

@@ -50,6 +50,7 @@ function PersonalSubscriptionRecurlySyncEmail() {
type="submit"
disabled={isLoading}
isLoading={isLoading}
loadingLabel={t('updating')}
bs3Props={{
loading: isLoading ? t('updating') + '…' : t('update'),
}}

View File

@@ -34,6 +34,7 @@ function ConfirmCancelSubscriptionButton({
return (
<OLButton
isLoading={isLoading}
loadingLabel={t('processing_uppercase') + '…'}
disabled={disabled}
onClick={onClick}
variant={showNoThanks ? 'link' : undefined}

View File

@@ -40,6 +40,7 @@ export default function DowngradePlanButton({
onClick={handleDowngradePlan}
disabled={isButtonDisabled}
isLoading={isLoading}
loadingLabel={t('processing_uppercase') + '…'}
bs3Props={{
loading: isLoading ? t('processing_uppercase') + '…' : buttonText,
}}

View File

@@ -33,6 +33,7 @@ export default function ExtendTrialButton({
onClick={handleExtendTrial}
disabled={isButtonDisabled}
isLoading={isLoading}
loadingLabel={t('processing_uppercase') + '…'}
bs3Props={{
loading: isLoading ? t('processing_uppercase') + '…' : buttonText,
}}

View File

@@ -105,6 +105,7 @@ export function CancelAiAddOnModal() {
variant="danger"
disabled={inflight}
isLoading={inflight}
loadingLabel={t('processing_uppercase') + '…'}
onClick={handleConfirmChange}
bs3Props={{
loading: inflight

View File

@@ -332,6 +332,7 @@ export function ChangeToGroupModal() {
}
onClick={upgrade}
isLoading={inflight}
loadingLabel={t('processing_uppercase') + '…'}
bs3Props={{
loading: inflight
? t('processing_uppercase') + '…'

View File

@@ -110,6 +110,7 @@ export function ConfirmChangePlanModal() {
variant="primary"
disabled={inflight}
isLoading={inflight}
loadingLabel={t('processing_uppercase') + '…'}
onClick={handleConfirmChange}
bs3Props={{
loading: inflight

View File

@@ -91,6 +91,7 @@ export function KeepCurrentPlanModal() {
variant="primary"
disabled={inflight}
isLoading={inflight}
loadingLabel={t('processing_uppercase') + '…'}
onClick={confirmCancelPendingPlanChange}
bs3Props={{
loading: inflight

View File

@@ -25,6 +25,12 @@ function LoadingSpinner({
const [show, setShow] = useState(false)
useEffect(() => {
// Ensure that spinner is displayed immediately if delay is 0
if (delay === 0) {
setShow(true)
return
}
const timer = setTimeout(() => {
setShow(true)
}, delay)

View File

@@ -19,7 +19,7 @@ describe('<SettingsDictionary />', function () {
const button = screen.getByText('Edit')
fireEvent.click(button)
const modal = screen.getAllByRole('dialog')[0]
const modal = screen.getByTestId('dictionary-modal')
within(modal).getByRole('heading', { name: 'Edit Dictionary' })
within(modal).getByText('Your custom dictionary is empty.')
@@ -28,6 +28,6 @@ describe('<SettingsDictionary />', function () {
name: 'Close',
})
fireEvent.click(closeButton)
expect(screen.queryByRole('dialog')).to.be.null
expect(screen.getByTestId('dictionary-modal')).to.not.be.null
})
})

View File

@@ -26,11 +26,11 @@ describe('<OnlineUsersWidget />', function () {
screen.getByText('a')
})
it('displays user name in a tooltip', function () {
it('displays user name in a tooltip', async function () {
render(<OnlineUsersWidget {...defaultProps} />)
const icon = screen.getByText('t')
fireEvent.mouseOver(icon)
screen.getByRole('tooltip', { name: 'test_user' })
await screen.findByRole('tooltip', { name: 'test_user' })
})
it('calls "goToUser" when the user initial is clicked', function () {

View File

@@ -41,13 +41,13 @@ describe('<CurrentPlanWidget />', function () {
render(<CurrentPlanWidget />)
})
it('shows text and tooltip on mouseover', function () {
it('shows text and tooltip on mouseover', async function () {
const link = screen.getByRole('link', {
name: /plan is paused/i,
})
fireEvent.mouseOver(link)
screen.getByRole('tooltip', { name: pausedTooltipMessage })
await screen.findByRole('tooltip', { name: pausedTooltipMessage })
})
})
@@ -60,17 +60,17 @@ describe('<CurrentPlanWidget />', function () {
render(<CurrentPlanWidget />)
})
it('shows text and tooltip on mouseover', function () {
it('shows text and tooltip on mouseover', async function () {
const link = screen.getByRole('link', {
name: /youre on the free plan/i,
})
fireEvent.mouseOver(link)
screen.getByRole('tooltip', { name: freePlanTooltipMessage })
await screen.findByRole('tooltip', { name: freePlanTooltipMessage })
})
it('clicks on upgrade button', function () {
const upgradeLink = screen.getByRole('link', { name: /upgrade/i })
const upgradeLink = screen.getByRole('button', { name: /upgrade/i })
fireEvent.click(upgradeLink)
expect(sendMBSpy).to.be.calledOnce
expect(sendMBSpy).calledWith('upgrade-button-click', {
@@ -156,7 +156,7 @@ describe('<CurrentPlanWidget />', function () {
})
})
it('shows text and tooltip on mouseover', function () {
it('shows text and tooltip on mouseover', async function () {
render(<CurrentPlanWidget />)
const link = screen.getByRole('link', {
@@ -164,10 +164,10 @@ describe('<CurrentPlanWidget />', function () {
})
fireEvent.mouseOver(link)
screen.getByRole('tooltip', {
await screen.findByRole('tooltip', {
name: new RegExp(`on the ${subscription.plan.name}`, 'i'),
})
screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
await screen.findByRole('tooltip', { name: paidPlanTooltipMessage })
})
})
@@ -189,7 +189,7 @@ describe('<CurrentPlanWidget />', function () {
})
})
it('shows text and tooltip on mouseover (without subscription team name)', function () {
it('shows text and tooltip on mouseover (without subscription team name)', async function () {
render(<CurrentPlanWidget />)
const link = screen.getByRole('link', {
@@ -198,16 +198,16 @@ describe('<CurrentPlanWidget />', function () {
fireEvent.mouseOver(link)
expect(subscription.subscription.teamName).to.be.undefined
screen.getByRole('tooltip', {
await screen.findByRole('tooltip', {
name: new RegExp(
`on the ${subscription.plan.name} plan as a member of a group subscription`,
'i'
),
})
screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
await screen.findByRole('tooltip', { name: paidPlanTooltipMessage })
})
it('shows text and tooltip on mouseover (with subscription team name)', function () {
it('shows text and tooltip on mouseover (with subscription team name)', async function () {
const newSubscription = {
...subscription,
subscription: {
@@ -227,18 +227,18 @@ describe('<CurrentPlanWidget />', function () {
})
fireEvent.mouseOver(link)
screen.getByRole('tooltip', {
await screen.findByRole('tooltip', {
name: new RegExp(
`on the ${newSubscription.plan.name} plan as a member of a group subscription, ${newSubscription.subscription.teamName}`,
'i'
),
})
screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
await screen.findByRole('tooltip', { name: paidPlanTooltipMessage })
})
})
describe('commons', function () {
it('shows text and tooltip on mouseover', function () {
it('shows text and tooltip on mouseover', async function () {
const subscription = {
type: 'commons',
plan: {
@@ -260,13 +260,13 @@ describe('<CurrentPlanWidget />', function () {
})
fireEvent.mouseOver(link)
screen.getByRole('tooltip', {
await screen.findByRole('tooltip', {
name: new RegExp(
`on the ${subscription.plan.name} plan because of your affiliation with ${subscription.subscription.name}`,
'i'
),
})
screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
await screen.findByRole('tooltip', { name: paidPlanTooltipMessage })
})
})
})

View File

@@ -4,20 +4,8 @@ import fetchMock from 'fetch-mock'
import NewProjectButton from '../../../../../frontend/js/features/project-list/components/new-project-button'
import { renderWithProjectListContext } from '../helpers/render-with-context'
import getMeta from '@/utils/meta'
import * as bootstrapUtils from '@/features/utils/bootstrap-5'
import sinon, { type SinonStub } from 'sinon'
describe('<NewProjectButton />', function () {
let isBootstrap5Stub: SinonStub
before(function () {
isBootstrap5Stub = sinon.stub(bootstrapUtils, 'isBootstrap5').returns(true)
})
after(function () {
isBootstrap5Stub.restore()
})
beforeEach(function () {
fetchMock.reset()
})

View File

@@ -141,7 +141,9 @@ describe('<UserNotifications />', function () {
expect(joinBtn.disabled).to.be.true
await waitForElementToBeRemoved(() => screen.getByText('Loading'))
await waitForElementToBeRemoved(() =>
screen.getByRole('button', { name: /joining/i })
)
expect(acceptMock.called()).to.be.true
screen.getByText(/joined/i)
@@ -185,7 +187,7 @@ describe('<UserNotifications />', function () {
fireEvent.click(joinBtn)
await waitForElementToBeRemoved(() =>
screen.getByRole('button', { name: /loading/i })
screen.getByRole('button', { name: /joining/i })
)
expect(fetchMock.called()).to.be.true

View File

@@ -1,4 +1,10 @@
import { fireEvent, screen, waitFor, within } from '@testing-library/react'
import {
fireEvent,
screen,
waitFor,
waitForElementToBeRemoved,
within,
} from '@testing-library/react'
import { expect } from 'chai'
import fetchMock from 'fetch-mock'
import sinon from 'sinon'
@@ -157,7 +163,9 @@ describe('<ProjectListRoot />', function () {
const archiveButton = within(actionsToolbar).getByLabelText('Archive')
fireEvent.click(archiveButton)
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true
@@ -187,7 +195,9 @@ describe('<ProjectListRoot />', function () {
const archiveButton = within(actionsToolbar).getByLabelText('Trash')
fireEvent.click(archiveButton)
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true
@@ -266,14 +276,15 @@ describe('<ProjectListRoot />', function () {
status: 200,
})
const unarchiveButton =
within(actionsToolbar).getByText<HTMLButtonElement>('Restore')
const unarchiveButton = within(actionsToolbar).getByRole('button', {
name: 'Restore',
})
fireEvent.click(unarchiveButton)
await fetchMock.flush(true)
expect(fetchMock.done()).to.be.true
screen.getByText('No projects')
await screen.findByText('No projects')
})
it('only unarchive the selected projects', async function () {
@@ -340,7 +351,7 @@ describe('<ProjectListRoot />', function () {
await fetchMock.flush(true)
expect(fetchMock.done()).to.be.true
screen.getByText('No projects')
await screen.findByText('No projects')
})
it('only untrashes the selected projects', async function () {
@@ -369,7 +380,9 @@ describe('<ProjectListRoot />', function () {
within(actionsToolbar).getByLabelText<HTMLButtonElement>('Archive')
fireEvent.click(archiveButton)
const confirmButton = screen.getByText<HTMLButtonElement>('Confirm')
const confirmButton = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmButton)
expect(confirmButton.disabled).to.be.true
@@ -427,7 +440,9 @@ describe('<ProjectListRoot />', function () {
})
fireEvent.click(leaveButton)
const confirmButton = screen.getByText<HTMLButtonElement>('Confirm')
const confirmButton = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmButton)
expect(confirmButton.disabled).to.be.true
@@ -484,7 +499,9 @@ describe('<ProjectListRoot />', function () {
})
fireEvent.click(deleteButton)
const confirmButton = screen.getByText<HTMLButtonElement>('Confirm')
const confirmButton = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmButton)
expect(confirmButton.disabled).to.be.true
@@ -550,7 +567,9 @@ describe('<ProjectListRoot />', function () {
})
fireEvent.click(deleteLeaveButton)
const confirmButton = screen.getByText<HTMLButtonElement>('Confirm')
const confirmButton = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmButton)
expect(confirmButton.disabled).to.be.true
@@ -616,7 +635,11 @@ describe('<ProjectListRoot />', function () {
const trashBtns = screen.getAllByRole('button', { name: 'Trash' })
for (const [index, trashBtn] of trashBtns.entries()) {
fireEvent.click(trashBtn)
fireEvent.click(screen.getByText<HTMLButtonElement>('Confirm'))
fireEvent.click(
screen.getByRole('button', {
name: 'Confirm',
})
)
await waitFor(() => {
expect(
trashProjectMock.called(
@@ -843,8 +866,9 @@ describe('<ProjectListRoot />', function () {
)
// same name
let confirmButton =
within(modal).getByText<HTMLButtonElement>('Rename')
let confirmButton = within(modal).getByRole('button', {
name: 'Rename',
}) as HTMLButtonElement
expect(confirmButton.disabled).to.be.true
// no name
@@ -852,7 +876,9 @@ describe('<ProjectListRoot />', function () {
fireEvent.change(input, {
target: { value: '' },
})
confirmButton = within(modal).getByText<HTMLButtonElement>('Rename')
confirmButton = within(modal).getByRole('button', {
name: 'Rename',
}) as HTMLButtonElement
expect(confirmButton.disabled).to.be.true
})
@@ -887,8 +913,9 @@ describe('<ProjectListRoot />', function () {
target: { value: newProjectName },
})
const confirmButton =
within(modal).getByText<HTMLButtonElement>('Rename')
const confirmButton = within(modal).getByRole('button', {
name: 'Rename',
}) as HTMLButtonElement
expect(confirmButton.disabled).to.be.false
fireEvent.click(confirmButton)
@@ -1033,7 +1060,7 @@ describe('<ProjectListRoot />', function () {
selectedMatchesDisplayed(2)
})
it('shows correct list after closing modal, changing selecting, and reopening modal', async function () {
it('shows correct list after closing modal, changing selection, and reopening modal', async function () {
selectedMatchesDisplayed(2)
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
@@ -1041,7 +1068,10 @@ describe('<ProjectListRoot />', function () {
name: 'Cancel',
})
fireEvent.click(cancelButton)
expect(screen.queryByRole('dialog', { hidden: false })).to.be.null
await waitForElementToBeRemoved(
screen.getByRole('dialog', { hidden: false })
)
await screen.findAllByRole('checkbox')
fireEvent.click(allCheckboxes[3])

View File

@@ -4,6 +4,9 @@ import { Tag } from '../../../../../app/src/Features/Tags/types'
import ProjectListTitle from '../../../../../frontend/js/features/project-list/components/title/project-list-title'
describe('<ProjectListTitle />', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-bootstrapVersion', 5)
})
type TestCase = {
filter: Filter
selectedTag: Tag | undefined

View File

@@ -31,7 +31,7 @@ describe('Add affiliation widget', function () {
await waitFor(() => expect(fetchMock.called('/api/project')))
screen.getByText(/are you affiliated with an institution/i)
const addAffiliationLink = screen.getByRole('link', {
const addAffiliationLink = screen.getByRole('button', {
name: /add affiliation/i,
})
expect(addAffiliationLink.getAttribute('href')).to.equal('/user/settings')

View File

@@ -36,7 +36,7 @@ describe('<SurveyWidgetDsNav />', function () {
screen.getByText(this.preText)
screen.getByText(this.linkText)
const link = screen.getByRole('link', {
const link = screen.getByRole('button', {
name: 'Take survey',
}) as HTMLAnchorElement
expect(link.href).to.equal(this.url)
@@ -51,7 +51,7 @@ describe('<SurveyWidgetDsNav />', function () {
const text = screen.queryByText(this.preText)
expect(text).to.be.null
const link = screen.queryByRole('link')
const link = screen.queryByRole('button')
expect(link).to.be.null
const dismissed = localStorage.getItem('dismissed-my-survey')
@@ -80,7 +80,7 @@ describe('<SurveyWidgetDsNav />', function () {
const text = screen.queryByText(this.preText)
expect(text).to.be.null
const link = screen.queryByRole('link')
const link = screen.queryByRole('button')
expect(link).to.be.null
})
})
@@ -98,7 +98,7 @@ describe('<SurveyWidgetDsNav />', function () {
const text = screen.queryByText(this.preText)
expect(text).to.be.null
const link = screen.queryByRole('link')
const link = screen.queryByRole('button')
expect(link).to.be.null
})
})

View File

@@ -16,13 +16,13 @@ describe('<ArchiveProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<ArchiveProjectButtonTooltip project={archiveableProject} />
)
const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Archive' })
await screen.findByRole('tooltip', { name: 'Archive' })
})
it('opens the modal when clicked', function () {
@@ -59,7 +59,9 @@ describe('<ArchiveProjectButton />', function () {
screen.getByText('Archive Projects')
screen.getByText('You are about to archive the following projects:')
screen.getByText('Archiving projects wont affect your collaborators.')
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true

View File

@@ -32,10 +32,10 @@ describe('<CompileAndDownloadProjectPDFButton />', function () {
sendMBSpy.restore()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
const btn = screen.getByRole('button', { name: 'Download PDF' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download PDF' })
await screen.findByRole('tooltip', { name: 'Download PDF' })
})
it('downloads the project PDF when clicked', async function () {

View File

@@ -17,13 +17,13 @@ describe('<CopyProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<CopyProjectButtonTooltip project={copyableProject} />
)
const btn = screen.getByRole('button', { name: 'Copy' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Copy' })
await screen.findByRole('tooltip', { name: 'Copy' })
})
it('does not render the button when project is archived', function () {
@@ -51,14 +51,16 @@ describe('<CopyProjectButton />', function () {
renderWithProjectListContext(
<CopyProjectButtonTooltip project={copyableProject} />
)
const btn = screen.getByRole('button', { name: 'Copy' })
fireEvent.click(btn)
screen.getByText('Copy Project')
screen.getByLabelText('New Name')
screen.getByDisplayValue(`${copyableProject.name} (Copy)`)
const copyBtn = screen.getByRole<HTMLButtonElement>('button', {
const copyBtn = screen.getAllByRole<HTMLButtonElement>('button', {
name: 'Copy',
})
})[1]
fireEvent.click(copyBtn)
expect(copyBtn.disabled).to.be.true

View File

@@ -17,14 +17,14 @@ describe('<DeleteProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
window.metaAttributesCache.set('ol-user_id', trashedProject.owner?.id)
renderWithProjectListContext(
<DeleteProjectButtonTooltip project={trashedProject} />
)
const btn = screen.getByRole('button', { name: 'Delete' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Delete' })
await screen.findByRole('tooltip', { name: 'Delete' })
})
it('does not render button when trashed and not owner', function () {
@@ -61,7 +61,9 @@ describe('<DeleteProjectButton />', function () {
screen.getByText('Delete Projects')
screen.getByText('You are about to delete the following projects:')
screen.getByText('This action cannot be undone.')
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true

View File

@@ -23,10 +23,10 @@ describe('<DownloadProjectButton />', function () {
this.locationStub.restore()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
const btn = screen.getByRole('button', { name: 'Download .zip file' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download .zip file' })
await screen.findByRole('tooltip', { name: 'Download .zip file' })
})
it('downloads the project when clicked', async function () {

View File

@@ -18,13 +18,13 @@ describe('<LeaveProjectButtton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<LeaveProjectButtonTooltip project={trashedAndNotOwnedProject} />
)
const btn = screen.getByRole('button', { name: 'Leave' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Leave' })
await screen.findByRole('tooltip', { name: 'Leave' })
})
it('does not render button when owner', function () {
@@ -67,7 +67,9 @@ describe('<LeaveProjectButtton />', function () {
screen.getByText('Leave Projects')
screen.getByText('You are about to leave the following projects:')
screen.getByText('This action cannot be undone.')
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true

View File

@@ -51,7 +51,9 @@ describe('<RenameProjectButton />', function () {
const btn = screen.getByRole('button')
fireEvent.click(btn)
screen.getByText('Rename Project')
const confirmBtn = screen.getByText('Rename') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Rename',
}) as HTMLButtonElement
expect(confirmBtn.disabled).to.be.true
const nameInput = screen.getByDisplayValue(ownedProject.name)
fireEvent.change(nameInput, { target: { value: 'new name' } })

View File

@@ -16,13 +16,13 @@ describe('<TrashProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<TrashProjectButtonTooltip project={archivedProject} />
)
const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Trash' })
await screen.findByRole('tooltip', { name: 'Trash' })
})
it('does not render the button when project is trashed', function () {
@@ -49,7 +49,9 @@ describe('<TrashProjectButton />', function () {
screen.getByText('Trash Projects')
screen.getByText('You are about to trash the following projects:')
screen.getByText('Trashing projects wont affect your collaborators.')
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true

View File

@@ -17,13 +17,13 @@ describe('<UnarchiveProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<UnarchiveProjectButtonTooltip project={archivedProject} />
)
const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Restore' })
await screen.findByRole('tooltip', { name: 'Restore' })
})
it('does not render the button when project is trashed', function () {

View File

@@ -16,13 +16,13 @@ describe('<UntrashProjectButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(
<UntrashProjectButtonTooltip project={trashedProject} />
)
const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Restore' })
await screen.findByRole('tooltip', { name: 'Restore' })
})
it('does not render the button when project is current', function () {

View File

@@ -10,11 +10,11 @@ describe('<ArchiveProjectsButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(<ArchiveProjectsButton />)
const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Archive' })
await screen.findByRole('tooltip', { name: 'Archive' })
})
it('opens the modal when clicked', function () {

View File

@@ -10,10 +10,10 @@ describe('<DownloadProjectsButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(<DownloadProjectsButton />)
const btn = screen.getByRole('button', { name: 'Download' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download' })
await screen.findByRole('tooltip', { name: 'Download' })
})
})

View File

@@ -10,11 +10,11 @@ describe('<TrashProjectsButton />', function () {
resetProjectListContextFetch()
})
it('renders tooltip for button', function () {
it('renders tooltip for button', async function () {
renderWithProjectListContext(<TrashProjectsButton />)
const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Trash' })
await screen.findByRole('tooltip', { name: 'Trash' })
})
it('opens the modal when clicked', function () {

View File

@@ -32,10 +32,14 @@ describe('<RenameProjectModal />', function () {
/>
)
screen.getByText('Rename Project')
const input = screen.getByLabelText('New Name') as HTMLButtonElement
const input = screen.getByRole('textbox', {
name: 'New Name',
}) as HTMLInputElement
expect(input.value).to.equal(currentProjects[0].name)
const submitButton = screen.getByText('Rename') as HTMLButtonElement
const submitButton = screen.getByRole('button', {
name: 'Rename',
}) as HTMLButtonElement
expect(submitButton.disabled).to.be.true
fireEvent.change(input, {

View File

@@ -32,7 +32,9 @@ describe('<ProjectsActionModal />', function () {
showModal
/>
)
const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
const confirmBtn = screen.getByRole('button', {
name: 'Confirm',
}) as HTMLButtonElement
fireEvent.click(confirmBtn)
expect(confirmBtn.disabled).to.be.true
// verify action handled

View File

@@ -90,7 +90,7 @@ describe('<AccountInfoSection />', function () {
name: 'Update',
})
)
await screen.findByText('Saving…')
await screen.findByRole('button', { name: 'Loading' })
finishUpdateCall(200)
await screen.findByRole('button', {

View File

@@ -172,11 +172,7 @@ describe('email actions - make primary', function () {
fireEvent.click(
withinModal.getByRole('button', { name: 'Change primary email' })
)
expect(screen.queryByRole('dialog')).to.be.null
await waitForElementToBeRemoved(() =>
screen.getByRole('button', { name: /Processing/i, hidden: true })
)
await waitForElementToBeRemoved(() => screen.getByRole('dialog'))
}
it('shows confirmation modal and closes it', async function () {
@@ -201,13 +197,14 @@ describe('email actions - make primary', function () {
withinModal.getByRole('button', { name: 'Change primary email' })
fireEvent.click(withinModal.getByRole('button', { name: /cancel/i }))
expect(screen.queryByRole('dialog')).to.be.null
await waitForElementToBeRemoved(screen.getByRole('dialog'))
})
it('shows loader and removes button', async function () {
fetchMock
.get('/user/emails?ensureAffiliation=true', [userEmailData])
.post('/user/emails/default?delete-primary-unconfirmed', 200)
.post('/user/emails/default?delete-unconfirmed-primary', 200)
render(<EmailsSection />)
await confirmPrimaryEmail()
@@ -223,7 +220,7 @@ describe('email actions - make primary', function () {
it('shows error', async function () {
fetchMock
.get('/user/emails?ensureAffiliation=true', [userEmailData])
.post('/user/emails/default?delete-primary-unconfirmed', 503)
.post('/user/emails/default?delete-unconfirmed-primary', 503)
render(<EmailsSection />)
await confirmPrimaryEmail()

View File

@@ -69,7 +69,7 @@ async function confirmCodeForEmail(email: string) {
})
fireEvent.click(submitCodeBtn)
await waitForElementToBeRemoved(() =>
screen.getByRole('button', { name: /confirming/i })
screen.getByRole('button', { name: /Loading/i })
)
}
@@ -216,7 +216,7 @@ describe('<EmailsSection />', function () {
await waitForElementToBeRemoved(() =>
screen.getByRole('button', {
name: /add new email/i,
name: /Loading/i,
})
)

View File

@@ -134,7 +134,7 @@ describe('<EmailsSection />', function () {
screen.queryByText(/an error has occurred while performing your request/i)
).to.be.null
await screen.findByRole('button', {
await screen.findAllByRole('button', {
name: /resend confirmation code/i,
})
})

View File

@@ -3,7 +3,7 @@ import { expect } from 'chai'
import { SSOAlert } from '../../../../../../frontend/js/features/settings/components/emails/sso-alert'
describe('<SSOAlert/>', function () {
describe('when thereis no institutional linking information', function () {
describe('when there is no institutional linking information', function () {
it('should be empty', function () {
render(<SSOAlert />)
expect(screen.queryByRole('alert')).to.be.null
@@ -49,11 +49,11 @@ describe('<SSOAlert/>', function () {
)
render(<SSOAlert />)
const closeButtons = screen.getAllByRole('button', {
name: 'Close alert',
name: 'Close',
})
fireEvent.click(closeButtons[0])
fireEvent.click(closeButtons[1])
expect(screen.queryByRole('button', { name: 'Close alert' })).to.be.null
expect(screen.queryByRole('button', { name: 'Close' })).to.be.null
})
})
@@ -87,10 +87,10 @@ describe('<SSOAlert/>', function () {
it('the alert should be closeable', function () {
render(<SSOAlert />)
const closeButton = screen.getByRole('button', {
name: 'Close alert',
name: 'Close',
})
fireEvent.click(closeButton)
expect(screen.queryByRole('button', { name: 'Close alert' })).to.be.null
expect(screen.queryByRole('button', { name: 'Close' })).to.be.null
})
})
})

View File

@@ -78,7 +78,7 @@ describe('<LinkingSection />', function () {
)
const helpLink = screen.getByRole('link', { name: 'Learn more' })
expect(helpLink.getAttribute('href')).to.equal('/blog/434')
const linkButton = screen.getByRole('link', { name: 'Link' })
const linkButton = screen.getByRole('button', { name: 'Link' })
expect(linkButton.getAttribute('href')).to.equal('/auth/orcid?intent=link')
})

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { screen, fireEvent, render } from '@testing-library/react'
import { screen, fireEvent, render, within } from '@testing-library/react'
import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/integration-widget'
import * as eventTracking from '@/infrastructure/event-tracking'
@@ -32,7 +32,7 @@ describe('<IntegrationLinkingWidgetTest/>', function () {
})
it('should render an upgrade link and track clicks', function () {
const upgradeLink = screen.getByRole('link', { name: 'Upgrade' })
const upgradeLink = screen.getByRole('button', { name: 'Upgrade' })
expect(upgradeLink.getAttribute('href')).to.equal(
'/user/subscription/plans'
)
@@ -51,7 +51,7 @@ describe('<IntegrationLinkingWidgetTest/>', function () {
it('should render a link to initiate integration linking', function () {
expect(
screen.getByRole('link', { name: 'Link' }).getAttribute('href')
screen.getByRole('button', { name: 'Link' }).getAttribute('href')
).to.equal('/link')
})
@@ -90,10 +90,11 @@ describe('<IntegrationLinkingWidgetTest/>', function () {
it('should open a modal with a link to confirm integration unlinking', function () {
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
screen.getByText('confirm unlink')
screen.getByText('you will be unlinked')
screen.getByRole('button', { name: 'Cancel' })
screen.getByRole('button', { name: 'Unlink' })
const withinModal = within(screen.getByRole('dialog'))
withinModal.getByText('confirm unlink')
withinModal.getByText('you will be unlinked')
withinModal.getByRole('button', { name: 'Cancel' })
withinModal.getByRole('button', { name: 'Unlink' })
})
it('should cancel unlinking when clicking "cancel" in the confirmation modal', async function () {

View File

@@ -1,6 +1,12 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
import {
screen,
fireEvent,
render,
waitFor,
within,
} from '@testing-library/react'
import { FetchError } from '../../../../../../frontend/js/infrastructure/fetch-json'
import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/sso-widget'
@@ -27,7 +33,7 @@ describe('<SSOLinkingWidget />', function () {
it('should render a link to `linkPath`', function () {
render(<SSOLinkingWidget {...defaultProps} linked={false} />)
expect(
screen.getByRole('link', { name: 'Link' }).getAttribute('href')
screen.getByRole('button', { name: 'Link' }).getAttribute('href')
).to.equal('/integration/link?intent=link')
})
})
@@ -75,7 +81,7 @@ describe('<SSOLinkingWidget />', function () {
<SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
)
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
confirmBtn = screen.getByRole('button', {
confirmBtn = within(screen.getByRole('dialog')).getByRole('button', {
name: 'Unlink',
hidden: false,
})
@@ -109,10 +115,13 @@ describe('<SSOLinkingWidget />', function () {
<SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
)
fireEvent.click(screen.getByRole('button', { name: 'Unlink' }))
const confirmBtn = screen.getByRole('button', {
name: 'Unlink',
hidden: false,
})
const confirmBtn = within(screen.getByRole('dialog')).getByRole(
'button',
{
name: 'Unlink',
hidden: false,
}
)
fireEvent.click(confirmBtn)
})

View File

@@ -145,7 +145,7 @@ describe('<PasswordSection />', function () {
render(<PasswordSection />)
submitValidForm()
await screen.findByText('Saving…')
await screen.findByRole('button', { name: 'Saving…' })
finishUpdateCall({
status: 200,

View File

@@ -22,7 +22,7 @@ describe('<SecuritySection />', function () {
render(<SecuritySection />)
expect(screen.getAllByText('Single Sign-On (SSO)').length).to.equal(2)
const link = screen.getByRole('link', {
const link = screen.getByRole('button', {
name: /Set up SSO/i,
})
expect(link).to.exist

View File

@@ -92,7 +92,9 @@ describe('<GroupSubscriptionMemberships />', function () {
</SplitTestProvider>
)
const leaveGroupButton = screen.getByText('Leave group')
const leaveGroupButton = screen.getByRole('button', {
name: 'Leave group',
})
fireEvent.click(leaveGroupButton)
this.confirmModal = screen.getByRole('dialog')
@@ -100,8 +102,12 @@ describe('<GroupSubscriptionMemberships />', function () {
'Are you sure you want to leave this group?'
)
this.cancelButton = within(this.confirmModal).getByText('Cancel')
this.leaveNowButton = within(this.confirmModal).getByText('Leave now')
this.cancelButton = within(this.confirmModal).getByRole('button', {
name: 'Cancel',
})
this.leaveNowButton = within(this.confirmModal).getByRole('button', {
name: 'Leave now',
})
})
afterEach(function () {

View File

@@ -89,7 +89,7 @@ describe('<PersonalSubscription />', function () {
screen.getByText('No further payments will be taken.', { exact: false })
screen.getByRole('link', { name: 'View your invoices' })
screen.getByRole('button', { name: 'View your invoices' })
screen.getByRole('button', { name: 'Reactivate your subscription' })
})

View File

@@ -323,7 +323,7 @@ describe('<ActiveSubscription />', function () {
fireEvent.click(button)
const cancelButton = screen.getByRole('button', {
name: 'Loading',
name: 'Processing',
}) as HTMLButtonElement
expect(cancelButton.disabled).to.be.true
@@ -368,7 +368,7 @@ describe('<ActiveSubscription />', function () {
name: cancelButtonText,
})
screen.getByRole('button', {
name: 'Loading',
name: 'Processing',
})
})
@@ -385,7 +385,7 @@ describe('<ActiveSubscription />', function () {
expect(buttons[0].getAttribute('disabled')).to.equal('')
expect(buttons[1].getAttribute('disabled')).to.equal('')
screen.getByRole('button', {
name: 'Loading',
name: 'Processing',
})
screen.getByRole('button', {
name: extendTrialButtonText,
@@ -456,7 +456,7 @@ describe('<ActiveSubscription />', function () {
name: cancelButtonText,
})
screen.getByRole('button', {
name: 'Loading',
name: 'Processing',
})
})
@@ -473,7 +473,7 @@ describe('<ActiveSubscription />', function () {
expect(buttons[0].getAttribute('disabled')).to.equal('')
expect(buttons[1].getAttribute('disabled')).to.equal('')
screen.getByRole('button', {
name: 'Loading',
name: 'Processing',
})
screen.getByRole('button', {
name: downgradeButtonText,

View File

@@ -118,10 +118,14 @@ describe('<ChangePlanModal />', function () {
})
fireEvent.click(buttons[0])
await screen.findByText('Are you sure you want to change plan to', {
exact: false,
})
screen.getByRole('button', { name: 'Change plan' })
const confirmModal = screen.getByRole('dialog')
await within(confirmModal).findByText(
'Are you sure you want to change plan to',
{
exact: false,
}
)
within(confirmModal).getByRole('button', { name: 'Change plan' })
expect(
screen.queryByText(
@@ -197,10 +201,13 @@ describe('<ChangePlanModal />', function () {
await screen.findByText('Are you sure you want to change plan to', {
exact: false,
})
const buttonConfirm = screen.getByRole('button', { name: 'Change plan' })
const buttonConfirm = within(screen.getByRole('dialog')).getByRole(
'button',
{ name: 'Change plan' }
)
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
// page is reloaded on success
await waitFor(() => {
@@ -230,10 +237,13 @@ describe('<ChangePlanModal />', function () {
await screen.findByText('Are you sure you want to change plan to', {
exact: false,
})
const buttonConfirm = screen.getByRole('button', { name: 'Change plan' })
const buttonConfirm = within(screen.getByRole('dialog')).getByRole(
'button',
{ name: 'Change plan' }
)
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
await screen.findByText('Sorry, something went wrong. ', { exact: false })
await screen.findByText('Please try again. ', { exact: false })
@@ -286,7 +296,7 @@ describe('<ChangePlanModal />', function () {
})
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
// page is reloaded on success
await waitFor(() => {
@@ -304,7 +314,7 @@ describe('<ChangePlanModal />', function () {
})
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
await screen.findByText('Sorry, something went wrong. ', { exact: false })
await screen.findByText('Please try again. ', { exact: false })
await screen.findByText('If the problem continues please contact us.', {
@@ -518,7 +528,7 @@ describe('<ChangePlanModal />', function () {
const buttonConfirm = screen.getByRole('button', { name: 'Upgrade now' })
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
// // page is reloaded on success
await waitFor(() => {
@@ -539,7 +549,7 @@ describe('<ChangePlanModal />', function () {
const buttonConfirm = screen.getByRole('button', { name: 'Upgrade now' })
fireEvent.click(buttonConfirm)
screen.getByText('processing', { exact: false })
screen.getByRole('button', { name: 'Processing…' })
await screen.findByText('Sorry, something went wrong. ', { exact: false })
await screen.findByText('Please try again. ', { exact: false })

View File

@@ -2,34 +2,38 @@ import { render, screen } from '@testing-library/react'
import AcceptedInvite from '../../../../../../frontend/js/features/subscription/components/group-invite/accepted-invite'
import { expect } from 'chai'
describe('accepted group invite', function () {
it('renders', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
render(<AcceptedInvite />)
await screen.findByText(
'You have joined the group subscription managed by example@overleaf.com'
)
})
for (const bsVersion of [3, 5])
describe('accepted group invite' + ` bs${bsVersion}`, function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-bootstrapVersion', bsVersion)
})
it('renders', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
render(<AcceptedInvite />)
await screen.findByText(
'You have joined the group subscription managed by example@overleaf.com'
)
})
it('links to SSO enrollment page for SSO groups', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
window.metaAttributesCache.set('ol-groupSSOActive', true)
window.metaAttributesCache.set('ol-subscriptionId', 'group123')
render(<AcceptedInvite />)
const linkBtn = (await screen.findByRole('link', {
name: 'Done',
})) as HTMLLinkElement
expect(linkBtn.href).to.equal(
'https://www.test-overleaf.com/subscription/group123/sso_enrollment'
)
})
it('links to SSO enrollment page for SSO groups', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
window.metaAttributesCache.set('ol-groupSSOActive', true)
window.metaAttributesCache.set('ol-subscriptionId', 'group123')
render(<AcceptedInvite />)
const linkBtn = (await screen.findByRole('link', {
name: 'Done',
})) as HTMLLinkElement
expect(linkBtn.href).to.equal(
'https://www.test-overleaf.com/subscription/group123/sso_enrollment'
)
})
it('links to dash for non-SSO groups', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
render(<AcceptedInvite />)
const linkBtn = (await screen.findByRole('link', {
name: 'Done',
})) as HTMLLinkElement
expect(linkBtn.href).to.equal('https://www.test-overleaf.com/project')
it('links to dash for non-SSO groups', async function () {
window.metaAttributesCache.set('ol-inviterName', 'example@overleaf.com')
render(<AcceptedInvite />)
const linkBtn = (await screen.findByRole('link', {
name: 'Done',
})) as HTMLLinkElement
expect(linkBtn.href).to.equal('https://www.test-overleaf.com/project')
})
})
})

View File

@@ -2,37 +2,80 @@ import { render, screen } from '@testing-library/react'
import { expect } from 'chai'
import GroupInvite from '../../../../../../frontend/js/features/subscription/components/group-invite/group-invite'
describe('group invite', function () {
const inviterName = 'example@overleaf.com'
beforeEach(function () {
window.metaAttributesCache.set('ol-inviterName', inviterName)
})
it('renders header', async function () {
render(<GroupInvite />)
await screen.findByText(inviterName)
screen.getByText(`has invited you to join a group subscription on Overleaf`)
expect(screen.queryByText('Email link expired, please request a new one.'))
.to.be.null
})
describe('when user has personal subscription', function () {
for (const bsVersion of [3, 5])
describe('group invite' + ` bs${bsVersion}`, function () {
window.metaAttributesCache.set('ol-bootstrapVersion', bsVersion)
const inviterName = 'example@overleaf.com'
beforeEach(function () {
window.metaAttributesCache.set(
'ol-hasIndividualRecurlySubscription',
true
)
window.metaAttributesCache.set('ol-inviterName', inviterName)
})
it('renders cancel personal subscription view', async function () {
it('renders header', async function () {
render(<GroupInvite />)
await screen.findByText(
'You already have an individual subscription, would you like us to cancel this first before joining the group licence?'
await screen.findByText(inviterName)
screen.getByText(
`has invited you to join a group subscription on Overleaf`
)
expect(
screen.queryByText('Email link expired, please request a new one.')
).to.be.null
})
describe('and in a managed group', function () {
// note: this should not be possible but managed user view takes priority over all
describe('when user has personal subscription', function () {
beforeEach(function () {
window.metaAttributesCache.set(
'ol-hasIndividualRecurlySubscription',
true
)
})
it('renders cancel personal subscription view', async function () {
render(<GroupInvite />)
await screen.findByText(
'You already have an individual subscription, would you like us to cancel this first before joining the group licence?'
)
})
describe('and in a managed group', function () {
// note: this should not be possible but managed user view takes priority over all
beforeEach(function () {
window.metaAttributesCache.set(
'ol-currentManagedUserAdminEmail',
'example@overleaf.com'
)
window.metaAttributesCache.set('ol-cannot-join-subscription', true)
})
it('renders managed user cannot join view', async function () {
render(<GroupInvite />)
await screen.findByText('You cant join this group subscription')
screen.getByText(
'Your Overleaf account is managed by your current group admin (example@overleaf.com). This means you cant join additional group subscriptions',
{ exact: false }
)
screen.getByRole('link', { name: 'Read more about Managed Users.' })
})
})
})
describe('when user does not have a personal subscription', function () {
beforeEach(function () {
window.metaAttributesCache.set(
'ol-hasIndividualRecurlySubscription',
false
)
window.metaAttributesCache.set('ol-inviteToken', 'token123')
})
it('does not render cancel personal subscription view', async function () {
render(<GroupInvite />)
await screen.findByText(
'Please click the button below to join the group subscription and enjoy the benefits of an upgraded Overleaf account'
)
})
})
describe('when the user is already a managed user in another group', function () {
beforeEach(function () {
window.metaAttributesCache.set(
'ol-currentManagedUserAdminEmail',
@@ -43,7 +86,11 @@ describe('group invite', function () {
it('renders managed user cannot join view', async function () {
render(<GroupInvite />)
await screen.findByText('You cant join this group subscription')
await screen.findByText(inviterName)
screen.getByText(
`has invited you to join a group subscription on Overleaf`
)
screen.getByText('You cant join this group subscription')
screen.getByText(
'Your Overleaf account is managed by your current group admin (example@overleaf.com). This means you cant join additional group subscriptions',
{ exact: false }
@@ -51,70 +98,28 @@ describe('group invite', function () {
screen.getByRole('link', { name: 'Read more about Managed Users.' })
})
})
})
describe('when user does not have a personal subscription', function () {
beforeEach(function () {
window.metaAttributesCache.set(
'ol-hasIndividualRecurlySubscription',
false
)
window.metaAttributesCache.set('ol-inviteToken', 'token123')
describe('expired', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-expired', true)
})
it('shows error notification when expired', async function () {
render(<GroupInvite />)
await screen.findByText('Email link expired, please request a new one.')
})
})
it('does not render cancel personal subscription view', async function () {
render(<GroupInvite />)
await screen.findByText(
'Please click the button below to join the group subscription and enjoy the benefits of an upgraded Overleaf account'
)
describe('join view', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-inviteToken', 'token123')
})
it('shows view to join group', async function () {
render(<GroupInvite />)
await screen.findByText(
'Please click the button below to join the group subscription and enjoy the benefits of an upgraded Overleaf account'
)
})
})
})
describe('when the user is already a managed user in another group', function () {
beforeEach(function () {
window.metaAttributesCache.set(
'ol-currentManagedUserAdminEmail',
'example@overleaf.com'
)
window.metaAttributesCache.set('ol-cannot-join-subscription', true)
})
it('renders managed user cannot join view', async function () {
render(<GroupInvite />)
await screen.findByText(inviterName)
screen.getByText(
`has invited you to join a group subscription on Overleaf`
)
screen.getByText('You cant join this group subscription')
screen.getByText(
'Your Overleaf account is managed by your current group admin (example@overleaf.com). This means you cant join additional group subscriptions',
{ exact: false }
)
screen.getByRole('link', { name: 'Read more about Managed Users.' })
})
})
describe('expired', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-expired', true)
})
it('shows error notification when expired', async function () {
render(<GroupInvite />)
await screen.findByText('Email link expired, please request a new one.')
})
})
describe('join view', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-inviteToken', 'token123')
})
it('shows view to join group', async function () {
render(<GroupInvite />)
await screen.findByText(
'Please click the button below to join the group subscription and enjoy the benefits of an upgraded Overleaf account'
)
})
})
})

View File

@@ -2,6 +2,7 @@ export function resetMeta() {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-projectHistoryBlobsEnabled', true)
window.metaAttributesCache.set('ol-i18n', { currentLangCode: 'en' })
window.metaAttributesCache.set('ol-bootstrapVersion', 5)
window.metaAttributesCache.set('ol-ExposedSettings', {
appName: 'Overleaf',
maxEntitiesPerProject: 10,