fix: passing versions e2e (#5521)

This commit is contained in:
Jarrod Flesch
2024-03-29 01:20:02 -04:00
committed by GitHub
parent 6a0c6284d0
commit cb3723242c
11 changed files with 131 additions and 91 deletions

View File

@@ -230,7 +230,7 @@ jobs:
# - plugin-seo
# - refresh-permissions
# - uploads
# - versions
- versions
steps:
- name: Use Node.js 18

View File

@@ -5,6 +5,7 @@ import type { Where } from 'payload/types'
import { ReactSelect } from '@payloadcms/ui/elements/ReactSelect'
import { fieldBaseClass } from '@payloadcms/ui/fields/shared'
import { useConfig } from '@payloadcms/ui/providers/Config'
import { useDocumentInfo } from '@payloadcms/ui/providers/DocumentInfo'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { formatDate } from '@payloadcms/ui/utilities/formatDate'
import qs from 'qs'
@@ -28,6 +29,7 @@ export const SelectComparison: React.FC<Props> = (props) => {
admin: { dateFormat },
} = useConfig()
const { docConfig } = useDocumentInfo()
const [options, setOptions] = useState(baseOptions)
const [lastLoadedPage, setLastLoadedPage] = useState(1)
const [errorLoading, setErrorLoading] = useState('')
@@ -51,15 +53,18 @@ export const SelectComparison: React.FC<Props> = (props) => {
not_equals: versionID,
},
},
{
latest: {
not_equals: true,
},
},
],
},
}
if (docConfig.versions?.drafts) {
query.where.and.push({
latest: {
not_equals: true,
},
})
}
if (parentID) {
query.where.and.push({
parent: {
@@ -79,7 +84,6 @@ export const SelectComparison: React.FC<Props> = (props) => {
if (response.ok) {
const data: PaginatedDocs = await response.json()
if (data.docs.length > 0) {
setOptions((existingOptions) => [
...existingOptions,
@@ -98,7 +102,7 @@ export const SelectComparison: React.FC<Props> = (props) => {
setErrorLoading(t('error:unspecific'))
}
},
[dateFormat, baseURL, parentID, versionID, t, i18n],
[dateFormat, baseURL, parentID, versionID, t, i18n, docConfig.versions?.drafts],
)
useEffect(() => {

View File

@@ -15,52 +15,62 @@ import { IDCell } from './cells/ID/index.js'
export const buildVersionColumns = ({
collectionConfig,
config,
docID,
globalConfig,
i18n: { t },
i18n,
}: {
collectionConfig?: SanitizedCollectionConfig
config: SanitizedConfig
docID?: number | string
globalConfig?: SanitizedGlobalConfig
i18n: I18n
}): Column[] => [
{
name: '',
Label: '',
accessor: 'updatedAt',
active: true,
components: {
Cell: (
<CreatedAtCell
collectionSlug={collectionConfig?.slug}
docID={docID}
globalSlug={globalConfig?.slug}
/>
),
Heading: <SortColumn Label={t('general:updatedAt')} name="updatedAt" />,
}): Column[] => {
const entityConfig = collectionConfig || globalConfig
const columns = [
{
name: '',
Label: '',
accessor: 'updatedAt',
active: true,
components: {
Cell: (
<CreatedAtCell
collectionSlug={collectionConfig?.slug}
docID={docID}
globalSlug={globalConfig?.slug}
/>
),
Heading: <SortColumn Label={t('general:updatedAt')} name="updatedAt" />,
},
},
},
{
name: '',
Label: '',
accessor: 'id',
active: true,
components: {
Cell: <IDCell />,
Heading: <SortColumn Label={t('version:versionID')} disable name="id" />,
{
name: '',
Label: '',
accessor: 'id',
active: true,
components: {
Cell: <IDCell />,
Heading: <SortColumn Label={t('version:versionID')} disable name="id" />,
},
},
},
{
name: '',
Label: '',
accessor: 'autosave',
active: true,
components: {
Cell: <AutosaveCell />,
Heading: <SortColumn Label={t('version:type')} disable name="autosave" />,
},
},
]
]
if (
entityConfig?.versions?.drafts ||
(entityConfig?.versions?.drafts && entityConfig.versions.drafts?.autosave)
) {
columns.push({
name: '',
Label: '',
accessor: '_status',
active: true,
components: {
Cell: <AutosaveCell />,
Heading: <SortColumn Label={t('version:type')} disable name="autosave" />,
},
})
}
return columns
}

View File

@@ -8,15 +8,11 @@ export const AutosaveCell: React.FC = () => {
const { t } = useTranslation()
const { rowData } = useTableCell()
return (
<Fragment>
{rowData?.autosave && (
<React.Fragment>
<Pill>
Autosave
{t('version:autosave')}
</Pill>
<Pill>{t('version:autosave')}</Pill>
&nbsp;&nbsp;
</React.Fragment>
)}

View File

@@ -11,6 +11,7 @@ const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.
export const DocumentTabLink: React.FC<{
adminRoute: SanitizedConfig['routes']['admin']
ariaLabel?: string
baseClass: string
children?: React.ReactNode
href: string
@@ -19,6 +20,7 @@ export const DocumentTabLink: React.FC<{
newTab?: boolean
}> = ({
adminRoute,
ariaLabel,
baseClass,
children,
href: hrefFromProps,
@@ -53,7 +55,10 @@ export const DocumentTabLink: React.FC<{
isActiveFromProps
return (
<li className={[baseClass, isActive && `${baseClass}--active`].filter(Boolean).join(' ')}>
<li
aria-label={ariaLabel}
className={[baseClass, isActive && `${baseClass}--active`].filter(Boolean).join(' ')}
>
<Link
className={`${baseClass}__link`}
href={!isActive || href !== pathname ? hrefWithLocale : ''}

View File

@@ -56,6 +56,7 @@ export const DocumentTab: React.FC<DocumentTabProps & DocumentTabConfig> = (prop
return (
<DocumentTabLink
adminRoute={routes.admin}
ariaLabel={labelToRender}
baseClass={baseClass}
href={href}
isActive={isActive}

View File

@@ -10,7 +10,7 @@ import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useLocale } from '../../providers/Locale/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
const DefaultPublishButton: React.FC = () => {
export const DefaultPublishButton: React.FC<{ label?: string }> = ({ label: labelProp }) => {
const { code } = useLocale()
const { id, collectionSlug, globalSlug, publishedDoc, unpublishedVersions } = useDocumentInfo()
const [hasPublishPermission, setHasPublishPermission] = React.useState(false)
@@ -22,7 +22,7 @@ const DefaultPublishButton: React.FC = () => {
serverURL,
} = useConfig()
const { t } = useTranslation()
const label = t('version:publishChanges')
const label = labelProp || t('version:publishChanges')
const hasNewerVersions = unpublishedVersions?.totalDocs > 0
const canPublish = hasPublishPermission && (modified || hasNewerVersions || !publishedDoc)

View File

@@ -8,10 +8,10 @@ import { useHotkey } from '../../hooks/useHotkey.js'
import { useEditDepth } from '../../providers/EditDepth/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
const DefaultSaveButton: React.FC = () => {
export const DefaultSaveButton: React.FC<{ label?: string }> = ({ label: labelProp }) => {
const { t } = useTranslation()
const { submit } = useForm()
const label = t('general:save')
const label = labelProp || t('general:save')
const ref = useRef<HTMLButtonElement>(null)
const editDepth = useEditDepth()

View File

@@ -28,7 +28,6 @@ import type { Payload } from 'payload/types'
import { expect, test } from '@playwright/test'
import path from 'path'
import { wait } from 'payload/utilities'
import { fileURLToPath } from 'url'
import { globalSlug } from '../admin/slugs.js'
@@ -42,6 +41,7 @@ import {
} from '../helpers.js'
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
import { POLL_TOPASS_TIMEOUT } from '../playwright.config.js'
import config from './config.js'
import { clearAndSeedEverything } from './seed.js'
import { titleToDelete } from './shared.js'
@@ -62,6 +62,22 @@ const { beforeAll, beforeEach, describe } = test
let payload: Payload
const waitForAutoSaveToComplete = async (page: Page) => {
await expect(async () => {
await expect(page.locator('.autosave:has-text("Saving...")')).toBeVisible()
}).toPass({
timeout: 45000,
})
await expect(async () => {
await expect(
page.locator('.autosave:has-text("Last saved less than a minute ago")'),
).toBeVisible()
}).toPass({
timeout: 45000,
})
}
describe('versions', () => {
let page: Page
let url: AdminUrlUtil
@@ -294,16 +310,11 @@ describe('versions', () => {
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
// fill out global title and wait for autosave
await page.goto(url.global(autoSaveGlobalSlug))
await wait(500)
await page.waitForURL(`**/${autoSaveGlobalSlug}`)
const titleField = page.locator('#field-title')
await expect(titleField).toBeVisible()
await titleField.fill('global title')
await wait(500)
await expect(page.locator('.autosave:has-text("Saving...")')).toBeVisible()
await expect(
page.locator('.autosave:has-text("Last saved less than a minute ago")'),
).toBeVisible()
await waitForAutoSaveToComplete(page)
await expect(titleField).toHaveValue('global title')
// refresh the page and ensure value autosaved
@@ -314,39 +325,37 @@ describe('versions', () => {
test('should retain localized data during autosave', async () => {
const en = 'en'
const es = 'es'
const title = 'english title'
const englishTitle = 'english title'
const spanishTitle = 'spanish title'
const description = 'description'
const newDescription = 'new description'
await page.goto(autosaveURL.create)
await expect(page.locator('.id-label')).toBeVisible()
await wait(500)
await page.waitForURL(`**/${autosaveURL.create}`)
await page.waitForURL(/\/(?!create$)[\w-]+$/)
const titleField = page.locator('#field-title')
const descriptionField = page.locator('#field-description')
// fill out en doc
await titleField.fill(title)
await descriptionField.fill(description)
await wait(500)
await titleField.fill(englishTitle)
await descriptionField.fill('description')
await waitForAutoSaveToComplete(page)
// change locale to spanish
await changeLocale(page, es)
// set localized title field
await titleField.fill(spanishTitle)
await wait(500)
await waitForAutoSaveToComplete(page)
// change locale back to en
await changeLocale(page, en)
// verify en loads its own title
await expect(titleField).toHaveValue(title)
await expect(titleField).toHaveValue(englishTitle)
// change non-localized description field
await descriptionField.fill(newDescription)
await wait(500)
await waitForAutoSaveToComplete(page)
// change locale to spanish
await changeLocale(page, es)
await wait(500)
// reload page in spanish
// title should not be english title
@@ -362,7 +371,7 @@ describe('versions', () => {
const englishTitle = 'english title'
await page.goto(url.create)
await wait(500)
await page.waitForURL(`**/${url.create}`)
// fill out doc in english
await page.locator('#field-title').fill(englishTitle)
@@ -376,6 +385,15 @@ describe('versions', () => {
await page.locator('#field-title').fill(spanishTitle)
await saveDocAndAssert(page)
// wait for the page to load with the new version
await expect
.poll(
async () =>
await page.locator('.doc-tab[aria-label="Versions"] .doc-tab__count').textContent(),
{ timeout: POLL_TOPASS_TIMEOUT },
)
.toEqual('2')
// fill out draft content in spanish
await page.locator('#field-title').fill(`${spanishTitle}--draft`)
await saveDocAndAssert(page, '#action-save-draft')
@@ -391,33 +409,40 @@ describe('versions', () => {
test('collection - autosave should only update the current document', async () => {
// create and save first doc
await page.goto(autosaveURL.create)
await wait(500)
await page.waitForURL(`**/${autosaveURL.create}`)
await page.waitForURL(/\/(?!create$)[\w-]+$/)
await page.locator('#field-title').fill('first post title')
await page.locator('#field-description').fill('first post description')
await page.locator('#action-save').click()
// create and save second doc
await page.goto(autosaveURL.create)
await wait(500)
await page.waitForURL(`**/${autosaveURL.create}`)
await page.waitForURL(/\/(?!create$)[\w-]+$/)
await page.locator('#field-title').fill('second post title')
await page.locator('#field-description').fill('second post description')
await page.locator('#action-save').click()
// publish changes
await saveDocAndAssert(page)
// update second doc and wait for autosave
await page.locator('#field-title').fill('updated second post title')
await page.locator('#field-description').fill('updated second post description')
await wait(500)
await waitForAutoSaveToComplete(page)
// verify that the first doc is unchanged
await page.goto(autosaveURL.list)
await page.locator('tbody tr .cell-title a').nth(1).click()
const secondRowLink = page.locator('tbody tr:nth-child(2) .cell-title a')
const docURL = await secondRowLink.getAttribute('href')
await secondRowLink.click()
await page.waitForURL(`**${docURL}`)
await expect(page.locator('#field-title')).toHaveValue('first post title')
await expect(page.locator('#field-description')).toHaveValue('first post description')
})
test('should save versions with custom IDs', async () => {
await page.goto(customIDURL.create)
await wait(500)
await page.waitForURL(`**/${customIDURL.create}`)
await page.locator('#field-id').fill('custom')
await page.locator('#field-title').fill('title')
await saveDocAndAssert(page)

View File

@@ -1,16 +1,15 @@
import * as React from 'react'
// In your projects, you can import as follows:
// import { CustomPublishButtonProps } from 'payload/types';
'use client'
import type { CustomPublishButtonProps } from 'payload/types'
import { DefaultPublishButton } from '@payloadcms/ui/elements/Publish'
import * as React from 'react'
import classes from './index.module.scss'
export const CustomPublishButton: CustomPublishButtonProps = ({ DefaultButton, ...rest }) => {
export const CustomPublishButton: CustomPublishButtonProps = () => {
return (
<div className={classes.customButton}>
<DefaultButton {...rest} />
<DefaultPublishButton />
</div>
)
}

View File

@@ -37,7 +37,7 @@
],
"paths": {
"@payload-config": [
"./test/_community/config.ts"
"./test/versions/config.ts"
],
"@payloadcms/live-preview": [
"./packages/live-preview/src"
@@ -160,4 +160,4 @@
".next/types/**/*.ts",
"scripts/**/*.ts"
]
}
}