Improve Server Pro tests to use semantic selectors (#29790)

* Replace placeholders with labels

* Add 'Close dialog' label to modal close button to distinguish from footer Close button

* Add and translate heading on the not found page

* Update textarea to have id matching label's for attribute
Simplify test for template description textarea

* Label PDF zoom level dropdown button

* Improve test selectors to use semantic roles and accessible names

GitOrigin-RevId: d215ddca30ddf844cfffbcf0e528a601b134d772
This commit is contained in:
Rebeka Dekany
2025-11-25 09:05:48 +00:00
committed by Copybot
parent 7dce5f0e25
commit b4bfff1b67
24 changed files with 294 additions and 223 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ block content
.container
.error-container
.error-details
p.error-status Not found
h1.error-status #{translate("not_found")}
p.error-description #{translate("cant_find_page")}
p.error-actions
a.error-btn(href='/') Home
@@ -280,6 +280,7 @@
"clicking_delete_will_remove_sso_config_and_clear_saml_data": "",
"clone_with_git": "",
"close": "",
"close_dialog": "",
"clsi_maintenance": "",
"clsi_unavailable": "",
"code_check_failed": "",
@@ -1272,6 +1273,7 @@
"pdf_unavailable_for_download": "",
"pdf_viewer": "",
"pdf_viewer_error": "",
"pdf_zoom_level": "",
"pending_additional_licenses": "",
"pending_addon_cancellation": "",
"pending_invite": "",
@@ -75,6 +75,7 @@ function PdfZoomDropdown({
id="pdf-zoom-dropdown"
variant="link"
className="pdf-toolbar-btn pdfjs-zoom-dropdown-button small"
aria-label={t('pdf_zoom_level')}
>
{rawScaleToPercentage(rawScale)}
</DropdownToggle>
@@ -8,6 +8,7 @@ import {
} from 'react-bootstrap'
import { ModalBodyProps } from 'react-bootstrap/ModalBody'
import type { Options as FocusTrapOptions } from 'focus-trap'
import { useTranslation } from 'react-i18next'
type OLModalProps = ModalProps & {
size?: 'sm' | 'lg'
@@ -52,8 +53,13 @@ export function OLModalHeader({
closeButton = true,
...props
}: OLModalHeaderProps) {
const { t } = useTranslation()
return (
<Modal.Header closeButton={closeButton} {...props}>
<Modal.Header
closeButton={closeButton}
closeLabel={t('close_dialog')}
{...props}
>
{children}
</Modal.Header>
)
+3
View File
@@ -357,6 +357,7 @@
"clicking_delete_will_remove_sso_config_and_clear_saml_data": "Clicking <0>Delete</0> will remove your SSO configuration and unlink all users. You can only do this when SSO is disabled in your group settings.",
"clone_with_git": "Clone with Git",
"close": "Close",
"close_dialog": "Close dialog",
"clsi_maintenance": "The compile servers are down for maintenance, and will be back shortly.",
"clsi_unavailable": "Sorry, the compile server for your project was temporarily unavailable. Please try again in a few moments.",
"cn": "Chinese (Simplified)",
@@ -1506,6 +1507,7 @@
"normally_x_price_per_month": "Normally __price__ per month",
"normally_x_price_per_year": "Normally __price__ per year",
"not_a_student": "Not a student?",
"not_found": "Not found",
"not_found_error_from_the_supplied_url": "The link to open this content on Overleaf pointed to a file that could not be found. If this keeps happening for links on a particular site, please report this to them.",
"not_managed": "Not managed",
"not_now": "Not now",
@@ -1659,6 +1661,7 @@
"pdf_unavailable_for_download": "PDF unavailable for download",
"pdf_viewer": "PDF Viewer",
"pdf_viewer_error": "There was a problem displaying the PDF for this project.",
"pdf_zoom_level": "PDF zoom level",
"pending": "Pending",
"pending_additional_licenses": "Your subscription is changing to include <0>__pendingAdditionalLicenses__</0> additional license(s) for a total of <1>__pendingTotalLicenses__</1> licenses.",
"pending_addon_cancellation": "Your subscription will change to remove the <strong>__addOnName__</strong> add-on at the end of the current billing period.",
@@ -163,7 +163,7 @@ describe('<EditorLeftMenu />', function () {
cy.findByRole('heading', { name: 'Copy project' })
// try closing & re-opening the modal with different methods
cy.findByRole('button', { name: 'Close' }).click()
cy.findByRole('button', { name: 'Close dialog' }).click()
cy.findByRole('button', { name: 'Copy project' }).click()
cy.findByRole('button', { name: 'Cancel' }).click()
cy.findByRole('button', { name: 'Copy project' }).click()
@@ -43,7 +43,7 @@ describe('<OLModal />', function () {
cy.findByRole('button', { name: 'Open modal' }).click()
cy.findByRole('dialog').should('be.visible')
cy.findByLabelText(/enter text/i).should('be.visible')
cy.get('body').type('{esc}')
cy.findByRole('button', { name: 'Close dialog' }).click()
// Modal should hide with single escape (escapeDeactivates: false means FocusTrap doesn't handle it)
cy.findByRole('dialog').should('not.exist')
cy.findByRole('button', { name: 'Open modal' }).should('be.visible')
@@ -70,13 +70,13 @@ describe('<OLModal />', function () {
cy.findByRole('button', { name: 'Open modal' }).click()
cy.findByRole('dialog').should('be.visible')
cy.findByRole('button', { name: 'Close' }).should('be.focused')
cy.findByRole('button', { name: 'Close dialog' }).should('be.focused')
cy.focused().tab()
cy.findByLabelText(/enter text/i).should('be.focused')
cy.focused().tab()
cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
cy.focused().tab()
cy.findByRole('button', { name: 'Close' }).should('be.focused')
cy.findByRole('button', { name: 'Close dialog' }).should('be.focused')
cy.focused().tab({ shift: true })
cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
})
@@ -95,7 +95,7 @@ describe('<OLModal />', function () {
cy.mount(<Modal />)
cy.findByRole('button', { name: 'Open modal' }).click()
cy.findByRole('dialog').should('be.visible')
cy.findByRole('button', { name: 'Close' }).click()
cy.findByRole('button', { name: 'Close dialog' }).click()
cy.findByRole('dialog').should('not.exist')
})
@@ -103,7 +103,7 @@ describe('<OLModal />', function () {
cy.mount(<Modal backdrop="static" />)
cy.findByRole('button', { name: 'Open modal' }).click()
cy.findByRole('dialog').should('be.visible')
cy.get('body').type('{esc}')
cy.findByRole('button', { name: 'Close dialog' }).click()
cy.findByRole('dialog').should('not.exist')
})
})
@@ -24,8 +24,8 @@ describe('<SettingsDictionary />', function () {
within(modal).getByRole('heading', { name: 'Edit Dictionary' })
within(modal).getByText('Your custom dictionary is empty.')
const [, closeButton] = within(modal).getAllByRole('button', {
name: 'Close',
const closeButton = within(modal).getByRole('button', {
name: 'Close dialog',
})
fireEvent.click(closeButton)
expect(screen.getByTestId('dictionary-modal')).to.not.be.null
@@ -62,7 +62,7 @@ describe('<NewProjectButton />', function () {
it('close the new project modal when clicking at the top right "x" button', function () {
fireEvent.click(screen.getByRole('menuitem', { name: 'Blank project' }))
fireEvent.click(screen.getByRole('button', { name: 'Close' }))
fireEvent.click(screen.getByRole('button', { name: 'Close dialog' }))
expect(screen.queryByRole('dialog')).to.be.null
})
@@ -26,8 +26,8 @@ describe('<DictionarySetting />', function () {
within(modal).getByRole('heading', { name: 'Edit Dictionary' })
within(modal).getByText('Your custom dictionary is empty.')
const [, closeButton] = within(modal).getAllByRole('button', {
name: 'Close',
const closeButton = within(modal).getByRole('button', {
name: 'Close dialog',
})
fireEvent.click(closeButton)
expect(screen.getByTestId('dictionary-modal')).to.not.be.null
@@ -35,7 +35,7 @@ describe('<LeaveSection />', function () {
)
const cancelButton = screen.getByRole('button', {
name: 'Close',
name: 'Cancel',
})
fireEvent.click(cancelButton)
@@ -141,15 +141,10 @@ describe('<ShareProjectModal/>', function () {
createContextProps()
)
const [headerCloseButton, footerCloseButton] = await screen.findAllByRole(
'button',
{ name: 'Close' }
)
const closeButton = screen.getByRole('button', { name: 'Close dialog' })
await userEvent.click(closeButton)
await userEvent.click(headerCloseButton)
await userEvent.click(footerCloseButton)
expect(handleHide.callCount).to.equal(2)
expect(handleHide.callCount).to.equal(1)
})
it('handles access level "private"', async function () {
@@ -410,7 +405,7 @@ describe('<ShareProjectModal/>', function () {
createContextProps({ publicAccessLevel: 'tokenBased', invites })
)
const [, closeButton] = screen.getAllByRole('button', {
const closeButton = screen.getByRole('button', {
name: 'Close',
})
@@ -446,7 +441,7 @@ describe('<ShareProjectModal/>', function () {
createContextProps({ publicAccessLevel: 'tokenBased', invites })
)
const [, closeButton] = screen.getAllByRole('button', {
const closeButton = screen.getByRole('button', {
name: 'Close',
})
@@ -481,7 +476,7 @@ describe('<ShareProjectModal/>', function () {
createContextProps({ publicAccessLevel: 'tokenBased', members })
)
const [, closeButton] = screen.getAllByRole('button', {
const closeButton = screen.getByRole('button', {
name: 'Close',
})