test: fixes flaky localization e2e once and for all (#10406)
The localization e2e test is notorious for flaking, consuming a lot of time and resources continually retrying. This was because the test was attempting to click DOM elements using selectors that never resolve, or attempting to click inaccessible DOM nodes such as those behind a modal. The fix is to ensure that the dot nav, for example, is disabled while form state loads, and that modals are properly closed prior to executing subsequent tests, etc. Tests also needed to explicitly check for _enabled_ states before performing click actions, rather than simply awaiting their visibility.
This commit is contained in:
@@ -13,6 +13,7 @@ import React, { Fragment, useEffect } from 'react'
|
||||
|
||||
import type { DocumentDrawerContextType } from '../DocumentDrawer/Provider.js'
|
||||
|
||||
import { useFormInitializing, useFormProcessing } from '../../forms/Form/context.js'
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { useEditDepth } from '../../providers/EditDepth/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
@@ -111,6 +112,9 @@ export const DocumentControls: React.FC<{
|
||||
const [updatedAt, setUpdatedAt] = React.useState<string>('')
|
||||
const [createdAt, setCreatedAt] = React.useState<string>('')
|
||||
|
||||
const processing = useFormProcessing()
|
||||
const initializing = useFormInitializing()
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.updatedAt) {
|
||||
setUpdatedAt(formatDate({ date: data.updatedAt, i18n, pattern: dateFormat }))
|
||||
@@ -255,6 +259,7 @@ export const DocumentControls: React.FC<{
|
||||
</div>
|
||||
}
|
||||
className={`${baseClass}__popup`}
|
||||
disabled={initializing || processing}
|
||||
horizontalAlign="right"
|
||||
size="large"
|
||||
verticalAlign="bottom"
|
||||
|
||||
@@ -63,16 +63,18 @@ export const PublishButton: React.FC<{ label?: string }> = ({ label: labelProp }
|
||||
entityConfig?.versions?.drafts.schedulePublish
|
||||
|
||||
const hasNewerVersions = unpublishedVersionCount > 0
|
||||
|
||||
const canPublish =
|
||||
hasPublishPermission &&
|
||||
(modified || hasNewerVersions || !hasPublishedDoc) &&
|
||||
uploadStatus !== 'uploading'
|
||||
|
||||
const operation = useOperation()
|
||||
|
||||
const forceDisable = operation === 'update' && !modified
|
||||
const disabled = operation === 'update' && !modified
|
||||
|
||||
const saveDraft = useCallback(async () => {
|
||||
if (forceDisable) {
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ export const PublishButton: React.FC<{ label?: string }> = ({ label: labelProp }
|
||||
},
|
||||
skipValidation: true,
|
||||
})
|
||||
}, [submit, collectionSlug, globalSlug, serverURL, api, localeCode, id, forceDisable])
|
||||
}, [submit, collectionSlug, globalSlug, serverURL, api, localeCode, id, disabled])
|
||||
|
||||
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -20,10 +20,10 @@ export const SaveButton: React.FC<{ label?: string }> = ({ label: labelProp }) =
|
||||
const editDepth = useEditDepth()
|
||||
const operation = useOperation()
|
||||
|
||||
const forceDisable = (operation === 'update' && !modified) || uploadStatus === 'uploading'
|
||||
const disabled = (operation === 'update' && !modified) || uploadStatus === 'uploading'
|
||||
|
||||
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
|
||||
if (forceDisable) {
|
||||
if (disabled) {
|
||||
// absorb the event
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export const SaveButton: React.FC<{ label?: string }> = ({ label: labelProp }) =
|
||||
return (
|
||||
<FormSubmit
|
||||
buttonId="action-save"
|
||||
disabled={forceDisable}
|
||||
disabled={disabled}
|
||||
onClick={handleSubmit}
|
||||
ref={ref}
|
||||
size="medium"
|
||||
|
||||
@@ -23,6 +23,7 @@ export const SaveDraftButton: React.FC = () => {
|
||||
} = useConfig()
|
||||
const { id, collectionSlug, globalSlug, setUnpublishedVersionCount, uploadStatus } =
|
||||
useDocumentInfo()
|
||||
|
||||
const modified = useFormModified()
|
||||
const { code: locale } = useLocale()
|
||||
const ref = useRef<HTMLButtonElement>(null)
|
||||
@@ -31,10 +32,10 @@ export const SaveDraftButton: React.FC = () => {
|
||||
const { submit } = useForm()
|
||||
const operation = useOperation()
|
||||
|
||||
const forceDisable = (operation === 'update' && !modified) || uploadStatus === 'uploading'
|
||||
const disabled = (operation === 'update' && !modified) || uploadStatus === 'uploading'
|
||||
|
||||
const saveDraft = useCallback(async () => {
|
||||
if (forceDisable) {
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,12 +72,12 @@ export const SaveDraftButton: React.FC = () => {
|
||||
api,
|
||||
locale,
|
||||
id,
|
||||
forceDisable,
|
||||
disabled,
|
||||
setUnpublishedVersionCount,
|
||||
])
|
||||
|
||||
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
|
||||
if (forceDisable) {
|
||||
if (disabled) {
|
||||
// absorb the event
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ export const SaveDraftButton: React.FC = () => {
|
||||
buttonId="action-save-draft"
|
||||
buttonStyle="secondary"
|
||||
className={baseClass}
|
||||
disabled={forceDisable}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
return void saveDraft()
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { BrowserContext, Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { openDocControls } from 'helpers/e2e/openDocControls.js'
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
initPageConsoleErrorCatch,
|
||||
openDocDrawer,
|
||||
saveDocAndAssert,
|
||||
throttleTest,
|
||||
} from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
@@ -28,6 +29,7 @@ import {
|
||||
spanishLocale,
|
||||
withRequiredLocalizedFields,
|
||||
} from './shared.js'
|
||||
import { navigateToDoc } from 'helpers/e2e/navigateToDoc.js'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
@@ -40,7 +42,7 @@ const dirname = path.dirname(filename)
|
||||
* Repeat above for Globals
|
||||
*/
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
const { beforeAll, beforeEach, describe } = test
|
||||
let url: AdminUrlUtil
|
||||
let urlWithRequiredLocalizedFields: AdminUrlUtil
|
||||
let urlRelationshipLocalized: AdminUrlUtil
|
||||
@@ -54,6 +56,7 @@ let page: Page
|
||||
let payload: PayloadTestSDK<Config>
|
||||
let serverURL: string
|
||||
let richTextURL: AdminUrlUtil
|
||||
let context: BrowserContext
|
||||
|
||||
describe('Localization', () => {
|
||||
beforeAll(async ({ browser }, testInfo) => {
|
||||
@@ -65,7 +68,7 @@ describe('Localization', () => {
|
||||
richTextURL = new AdminUrlUtil(serverURL, richTextSlug)
|
||||
urlWithRequiredLocalizedFields = new AdminUrlUtil(serverURL, withRequiredLocalizedFields)
|
||||
|
||||
const context = await browser.newContext()
|
||||
context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
|
||||
initPageConsoleErrorCatch(page)
|
||||
@@ -73,6 +76,14 @@ describe('Localization', () => {
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
// await throttleTest({
|
||||
// page,
|
||||
// context,
|
||||
// delay: 'Fast 4G',
|
||||
// })
|
||||
})
|
||||
|
||||
describe('localized text', () => {
|
||||
test('create english post, switch to spanish', async () => {
|
||||
await page.goto(url.create)
|
||||
@@ -243,43 +254,30 @@ describe('Localization', () => {
|
||||
})
|
||||
|
||||
describe('localized relationships', () => {
|
||||
test('ensure relationship field fetches are localised as well', async () => {
|
||||
await page.goto(url.list)
|
||||
test('ensure relationship field fetches are localized as well', async () => {
|
||||
await changeLocale(page, spanishLocale)
|
||||
|
||||
const localisedPost = page.locator('.cell-title a').first()
|
||||
const localisedPostUrl = await localisedPost.getAttribute('href')
|
||||
await page.goto(serverURL + localisedPostUrl)
|
||||
await page.waitForURL(serverURL + localisedPostUrl)
|
||||
|
||||
await navigateToDoc(page, url)
|
||||
const selectField = page.locator('#field-children .rs__control')
|
||||
await selectField.click()
|
||||
|
||||
await expect(page.locator('#field-children .rs__menu')).toContainText('spanish-relation2')
|
||||
})
|
||||
|
||||
test('ensure relationship edit drawers are opened in currently selected locale', async () => {
|
||||
await page.goto(urlRelationshipLocalized.list)
|
||||
await changeLocale(page, spanishLocale)
|
||||
|
||||
const post = page.locator('.cell-id a').first()
|
||||
const postUrl = await post.getAttribute('href')
|
||||
await page.goto(serverURL + postUrl)
|
||||
await page.waitForURL(serverURL + postUrl)
|
||||
|
||||
await openDocDrawer(
|
||||
page,
|
||||
'#field-relationMultiRelationTo .relationship--single-value__drawer-toggler',
|
||||
)
|
||||
|
||||
await navigateToDoc(page, urlRelationshipLocalized)
|
||||
const drawerToggler =
|
||||
'#field-relationMultiRelationTo .relationship--single-value__drawer-toggler'
|
||||
expect(page.locator(drawerToggler)).toBeEnabled()
|
||||
await openDocDrawer(page, drawerToggler)
|
||||
await expect(page.locator('.doc-drawer__header-text')).toContainText('spanish-relation2')
|
||||
await page.locator('.doc-drawer__header-close').click()
|
||||
})
|
||||
})
|
||||
|
||||
describe('copy localized data', () => {
|
||||
test('should show Copy To Locale button and drawer', async () => {
|
||||
await changeLocale(page, defaultLocale)
|
||||
await createAndSaveDoc(page, url, { description, title })
|
||||
await navigateToDoc(page, url)
|
||||
await openCopyToLocaleDrawer(page)
|
||||
await expect(page.locator('.copy-locale-data__content')).toBeVisible()
|
||||
await page.locator('.drawer-close-button').click()
|
||||
@@ -287,11 +285,9 @@ describe('Localization', () => {
|
||||
|
||||
test('should copy data to correct locale', async () => {
|
||||
await createAndSaveDoc(page, url, { title })
|
||||
|
||||
await openCopyToLocaleDrawer(page)
|
||||
await setToLocale(page, 'Spanish')
|
||||
await runCopy(page)
|
||||
|
||||
await expect(page.locator('#field-title')).toHaveValue(title)
|
||||
})
|
||||
|
||||
@@ -400,6 +396,9 @@ describe('Localization', () => {
|
||||
await runCopy(page)
|
||||
await expect(page.locator('#field-title')).toHaveValue(title)
|
||||
|
||||
const regexPattern = new RegExp(`locale=es`)
|
||||
await expect(page).toHaveURL(regexPattern)
|
||||
|
||||
await openCopyToLocaleDrawer(page)
|
||||
await setToLocale(page, 'Hungarian')
|
||||
await runCopy(page)
|
||||
@@ -445,11 +444,13 @@ async function createAndSaveDoc(page, url, values) {
|
||||
}
|
||||
|
||||
async function openCopyToLocaleDrawer(page) {
|
||||
const docControls = page.locator('.doc-controls__popup')
|
||||
const docControls = page.locator('.doc-controls__popup button.popup-button')
|
||||
expect(docControls).toBeEnabled()
|
||||
await docControls.click()
|
||||
const copyButton = page.locator('#copy-locale-data__button')
|
||||
await expect(copyButton).toBeVisible()
|
||||
await copyButton.click()
|
||||
await expect(page.locator('#copy-locale')).toBeVisible()
|
||||
await expect(page.locator('.copy-locale-data__content')).toBeVisible()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user