- When autoLogin is enabled, it will no longer flash an unresponsive "login" screen. Instead, it will straight up open the admin panel. That's because, on the server, we will now always & immediately see the user as authenticated, thus no initial login view is pushed to the client until the client component sends the auth request anymore. Less useless requests. Additionally, jwt verification is now completely skipped - No more auto-login related frontend code. autoLogin handling has been removed from the frontend `Auth` component - less code to maintain, this is way simpler now **For reviewers:** - The new logic for autoFill without prefillOnly is here: [jwt auth strategy](https://github.com/payloadcms/payload/pull/7224/files#diff-7d40839079a8b2abb58233e5904513ab321023a70538229dfaf1dfee067dc8bfR21) - The new logic for autoFill with prefillOnly is here: [Server Login View](https://github.com/payloadcms/payload/pull/7224/files#diff-683770104f196196743398a698fbf8987f00e4426ca1c0ace3658d18ab80e82dL72) => [Client Login Form](https://github.com/payloadcms/payload/pull/7224/files#diff-ac3504d3b3b0489455245663649bef9e84477bf0c1185da5a4d3a612450f01eeL20) **BREAKING** `autoLogin` without `prefillOnly` set now also affects graphQL/Rest operations. Only the user specified in `autoLogin` will be returned. Within the graphQL/Rest/Local API, this should still allow you to authenticate with a different user, as the autoLogin user is only used if no token is set.
642 lines
22 KiB
TypeScript
642 lines
22 KiB
TypeScript
import type { Page } from '@playwright/test'
|
|
|
|
import { expect, test } from '@playwright/test'
|
|
import path from 'path'
|
|
import { wait } from 'payload/shared'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
|
import type {
|
|
FieldsRelationship as CollectionWithRelationships,
|
|
Config,
|
|
RelationOne,
|
|
RelationRestricted,
|
|
RelationTwo,
|
|
RelationWithTitle,
|
|
} from './payload-types.js'
|
|
|
|
import {
|
|
ensureCompilationIsDone,
|
|
initPageConsoleErrorCatch,
|
|
openCreateDocDrawer,
|
|
openDocControls,
|
|
openDocDrawer,
|
|
saveDocAndAssert,
|
|
} from '../helpers.js'
|
|
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
|
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
|
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
|
import {
|
|
mixedMediaCollectionSlug,
|
|
relationFalseFilterOptionSlug,
|
|
relationOneSlug,
|
|
relationRestrictedSlug,
|
|
relationTrueFilterOptionSlug,
|
|
relationTwoSlug,
|
|
relationUpdatedExternallySlug,
|
|
relationWithTitleSlug,
|
|
slug,
|
|
} from './collectionSlugs.js'
|
|
|
|
const filename = fileURLToPath(import.meta.url)
|
|
const dirname = path.dirname(filename)
|
|
|
|
const { beforeAll, beforeEach, describe } = test
|
|
|
|
let payload: PayloadTestSDK<Config>
|
|
|
|
describe('fields - relationship', () => {
|
|
let url: AdminUrlUtil
|
|
let page: Page
|
|
let relationOneDoc: RelationOne
|
|
let anotherRelationOneDoc: RelationOne
|
|
let relationTwoDoc: RelationTwo
|
|
|
|
let docWithExistingRelations: CollectionWithRelationships
|
|
let restrictedRelation: RelationRestricted
|
|
let relationWithTitle: RelationWithTitle
|
|
let serverURL: string
|
|
|
|
beforeAll(async ({ browser }, testInfo) => {
|
|
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
|
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({ dirname }))
|
|
|
|
url = new AdminUrlUtil(serverURL, slug)
|
|
|
|
const context = await browser.newContext()
|
|
page = await context.newPage()
|
|
|
|
initPageConsoleErrorCatch(page)
|
|
await ensureCompilationIsDone({ page, serverURL })
|
|
})
|
|
|
|
beforeEach(async () => {
|
|
await clearAllDocs()
|
|
|
|
// Create docs to relate to
|
|
relationOneDoc = (await payload.create({
|
|
collection: relationOneSlug,
|
|
data: {
|
|
name: 'relation',
|
|
},
|
|
})) as any
|
|
|
|
anotherRelationOneDoc = (await payload.create({
|
|
collection: relationOneSlug,
|
|
data: {
|
|
name: 'relation',
|
|
},
|
|
})) as any
|
|
|
|
relationTwoDoc = (await payload.create({
|
|
collection: relationTwoSlug,
|
|
data: {
|
|
name: 'second-relation',
|
|
},
|
|
})) as any
|
|
|
|
// Create restricted doc
|
|
restrictedRelation = (await payload.create({
|
|
collection: relationRestrictedSlug,
|
|
data: {
|
|
name: 'restricted',
|
|
},
|
|
})) as any
|
|
|
|
// Doc with useAsTitle
|
|
relationWithTitle = (await payload.create({
|
|
collection: relationWithTitleSlug,
|
|
data: {
|
|
name: 'relation-title',
|
|
meta: {
|
|
title: 'relation-title',
|
|
},
|
|
},
|
|
})) as any
|
|
|
|
// Doc with useAsTitle for word boundary test
|
|
await payload.create({
|
|
collection: relationWithTitleSlug,
|
|
data: {
|
|
name: 'word boundary search',
|
|
meta: {
|
|
title: 'word boundary search',
|
|
},
|
|
},
|
|
})
|
|
|
|
// Add restricted doc as relation
|
|
docWithExistingRelations = (await payload.create({
|
|
collection: slug,
|
|
data: {
|
|
name: 'with-existing-relations',
|
|
relationship: relationOneDoc.id,
|
|
relationshipReadOnly: relationOneDoc.id,
|
|
relationshipRestricted: restrictedRelation.id,
|
|
relationshipWithTitle: relationWithTitle.id,
|
|
},
|
|
})) as any
|
|
|
|
await ensureCompilationIsDone({ page, serverURL })
|
|
})
|
|
|
|
test('should create relationship', async () => {
|
|
await page.goto(url.create)
|
|
const field = page.locator('#field-relationship')
|
|
await expect(field.locator('input')).toBeEnabled()
|
|
await field.click({ delay: 100 })
|
|
const options = page.locator('.rs__option')
|
|
await expect(options).toHaveCount(2) // two docs
|
|
await options.nth(0).click()
|
|
await expect(field).toContainText(relationOneDoc.id)
|
|
await saveDocAndAssert(page)
|
|
})
|
|
|
|
// TODO: Flaky test in CI - fix this. https://github.com/payloadcms/payload/actions/runs/8559547748/job/23456806365
|
|
test.skip('should create relations to multiple collections', async () => {
|
|
await page.goto(url.create)
|
|
|
|
const field = page.locator('#field-relationshipMultiple')
|
|
const value = page.locator('#field-relationshipMultiple .relationship--single-value__text')
|
|
|
|
await field.click({ delay: 100 })
|
|
|
|
const options = page.locator('.rs__option')
|
|
|
|
await expect(options).toHaveCount(3) // 3 docs
|
|
|
|
// Add one relationship
|
|
await options.locator(`text=${relationOneDoc.id}`).click()
|
|
await expect(value).toContainText(relationOneDoc.id)
|
|
|
|
// Add relationship of different collection
|
|
await field.click({ delay: 100 })
|
|
await options.locator(`text=${relationTwoDoc.id}`).click()
|
|
await expect(value).toContainText(relationTwoDoc.id)
|
|
|
|
await saveDocAndAssert(page)
|
|
await wait(200)
|
|
await expect(value).toContainText(relationTwoDoc.id)
|
|
})
|
|
|
|
test('should create hasMany relationship', async () => {
|
|
await page.goto(url.create)
|
|
const field = page.locator('#field-relationshipHasMany')
|
|
await expect(field.locator('input')).toBeEnabled()
|
|
await field.click({ delay: 100 })
|
|
const options = page.locator('.rs__option')
|
|
await expect(options).toHaveCount(2) // Two relationship options
|
|
const values = page.locator('#field-relationshipHasMany .relationship--multi-value-label__text')
|
|
await options.locator(`text=${relationOneDoc.id}`).click()
|
|
await expect(values).toHaveText([relationOneDoc.id])
|
|
await expect(values).not.toHaveText([anotherRelationOneDoc.id])
|
|
await field.click({ delay: 100 })
|
|
await options.locator(`text=${anotherRelationOneDoc.id}`).click()
|
|
await expect(values).toHaveText([relationOneDoc.id, anotherRelationOneDoc.id])
|
|
await field.locator('.rs__input').click({ delay: 100 })
|
|
await expect(page.locator('.rs__menu')).toHaveText('No options')
|
|
await saveDocAndAssert(page)
|
|
await wait(200)
|
|
await expect(values).toHaveText([relationOneDoc.id, anotherRelationOneDoc.id])
|
|
})
|
|
|
|
// TODO: Flaky test. Fix this! (This is an actual issue not just an e2e flake)
|
|
test.skip('should create many relations to multiple collections', async () => {
|
|
await page.goto(url.create)
|
|
|
|
const field = page.locator('#field-relationshipHasManyMultiple')
|
|
await field.click({ delay: 100 })
|
|
|
|
const options = page.locator('.rs__option')
|
|
await expect(options).toHaveCount(3)
|
|
|
|
const values = page.locator(
|
|
'#field-relationshipHasManyMultiple .relationship--multi-value-label__text',
|
|
)
|
|
|
|
// Add one relationship
|
|
await options.locator(`text=${relationOneDoc.id}`).click()
|
|
await expect(values).toHaveText([relationOneDoc.id])
|
|
|
|
// Add second relationship
|
|
await field.click({ delay: 100 })
|
|
await options.locator(`text=${relationTwoDoc.id}`).click()
|
|
await expect(values).toHaveText([relationOneDoc.id, relationTwoDoc.id])
|
|
|
|
await saveDocAndAssert(page)
|
|
await wait(200)
|
|
await expect(values).toHaveText([relationOneDoc.id, relationTwoDoc.id])
|
|
})
|
|
|
|
test('should duplicate document with relationships', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
|
|
await openDocControls(page)
|
|
await page.locator('#action-duplicate').click()
|
|
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
|
|
const field = page.locator('#field-relationship .relationship--single-value__text')
|
|
|
|
await expect(field).toHaveText(relationOneDoc.id)
|
|
})
|
|
|
|
async function runFilterOptionsTest(fieldName: string) {
|
|
await page.reload()
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
const field = page.locator('#field-relationship')
|
|
await expect(field.locator('input')).toBeEnabled()
|
|
await field.click({ delay: 100 })
|
|
const options = page.locator('.rs__option')
|
|
await options.nth(0).click()
|
|
await expect(field).toContainText(relationOneDoc.id)
|
|
let filteredField = page.locator(`#field-${fieldName} .react-select`)
|
|
await filteredField.click({ delay: 100 })
|
|
let filteredOptions = filteredField.locator('.rs__option')
|
|
await expect(filteredOptions).toHaveCount(1) // one doc
|
|
await filteredOptions.nth(0).click()
|
|
await expect(filteredField).toContainText(relationOneDoc.id)
|
|
await field.click({ delay: 100 })
|
|
await options.nth(1).click()
|
|
await expect(field).toContainText(anotherRelationOneDoc.id)
|
|
await wait(2000) // Need to wait form state to come back before clicking save
|
|
await page.locator('#action-save').click()
|
|
await expect(page.locator('.payload-toast-container')).toContainText(`is invalid: ${fieldName}`)
|
|
filteredField = page.locator(`#field-${fieldName} .react-select`)
|
|
await filteredField.click({ delay: 100 })
|
|
filteredOptions = filteredField.locator('.rs__option')
|
|
await expect(filteredOptions).toHaveCount(2) // two options because the currently selected option is still there
|
|
await filteredOptions.nth(1).click()
|
|
await expect(filteredField).toContainText(anotherRelationOneDoc.id)
|
|
await saveDocAndAssert(page)
|
|
}
|
|
|
|
// TODO: Flaky test. Fix this! (This is an actual issue not just an e2e flake)
|
|
test('should allow dynamic filterOptions', async () => {
|
|
await runFilterOptionsTest('relationshipFiltered')
|
|
})
|
|
|
|
// TODO: Flaky test. Fix this! (This is an actual issue not just an e2e flake)
|
|
test('should allow dynamic async filterOptions', async () => {
|
|
await runFilterOptionsTest('relationshipFilteredAsync')
|
|
})
|
|
|
|
test('should allow usage of relationTo in filterOptions', async () => {
|
|
const { id: include } = (await payload.create({
|
|
collection: relationOneSlug,
|
|
data: {
|
|
name: 'include',
|
|
},
|
|
})) as any
|
|
const { id: exclude } = (await payload.create({
|
|
collection: relationOneSlug,
|
|
data: {
|
|
name: 'exclude',
|
|
},
|
|
})) as any
|
|
|
|
await page.goto(url.create)
|
|
|
|
// select relationshipMany field that relies on siblingData field above
|
|
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
|
|
|
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
|
await expect(options).toContainText(include)
|
|
await expect(options).not.toContainText(exclude)
|
|
})
|
|
|
|
test('should allow usage of siblingData in filterOptions', async () => {
|
|
await payload.create({
|
|
collection: relationWithTitleSlug,
|
|
data: {
|
|
name: 'exclude',
|
|
},
|
|
})
|
|
|
|
await page.goto(url.create)
|
|
|
|
// enter a filter for relationshipManyFiltered to use
|
|
await page.locator('#field-filter').fill('include')
|
|
|
|
// select relationshipMany field that relies on siblingData field above
|
|
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
|
|
|
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
|
await expect(options).not.toContainText('exclude')
|
|
})
|
|
|
|
// TODO: Flaky test in CI - fix. https://github.com/payloadcms/payload/actions/runs/8559547748/job/23456806365
|
|
test.skip('should not query for a relationship when filterOptions returns false', async () => {
|
|
await payload.create({
|
|
collection: relationFalseFilterOptionSlug,
|
|
data: {
|
|
name: 'whatever',
|
|
},
|
|
})
|
|
|
|
await page.goto(url.create)
|
|
|
|
// select relationshipMany field that relies on siblingData field above
|
|
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
|
|
|
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
|
await expect(options).toContainText('Relation With Titles')
|
|
await expect(options).not.toContainText('whatever')
|
|
})
|
|
|
|
// TODO: Flaky test in CI - fix.
|
|
test('should show a relationship when filterOptions returns true', async () => {
|
|
await payload.create({
|
|
collection: relationTrueFilterOptionSlug,
|
|
data: {
|
|
name: 'truth',
|
|
},
|
|
})
|
|
|
|
await page.goto(url.create)
|
|
// wait for relationship options to load
|
|
const relationFilterOptionsReq = page.waitForResponse(/api\/relation-filter-true/)
|
|
// select relationshipMany field that relies on siblingData field above
|
|
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
|
await relationFilterOptionsReq
|
|
|
|
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
|
await expect(options).toContainText('truth')
|
|
})
|
|
|
|
test('should allow docs with same ID but different collections to be selectable', async () => {
|
|
const mixedMedia = new AdminUrlUtil(serverURL, mixedMediaCollectionSlug)
|
|
await page.goto(mixedMedia.create)
|
|
// wait for relationship options to load
|
|
const podcastsFilterOptionsReq = page.waitForResponse(/api\/podcasts/)
|
|
const videosFilterOptionsReq = page.waitForResponse(/api\/videos/)
|
|
// select relationshipMany field that relies on siblingData field above
|
|
await page.locator('#field-relatedMedia .rs__control').click()
|
|
await podcastsFilterOptionsReq
|
|
await videosFilterOptionsReq
|
|
|
|
const options = page.locator('.rs__option')
|
|
await expect(options).toHaveCount(4) // 4 docs
|
|
await options.locator(`text=Video 0`).click()
|
|
|
|
await page.locator('#field-relatedMedia .rs__control').click()
|
|
const remainingOptions = page.locator('.rs__option')
|
|
await expect(remainingOptions).toHaveCount(3) // 3 docs
|
|
})
|
|
|
|
// TODO: Flaky test in CI - fix.
|
|
test.skip('should open document drawer from read-only relationships', async () => {
|
|
const editURL = url.edit(docWithExistingRelations.id)
|
|
await page.goto(editURL)
|
|
await page.waitForURL(editURL)
|
|
|
|
await openDocDrawer(
|
|
page,
|
|
'#field-relationshipReadOnly button.relationship--single-value__drawer-toggler.doc-drawer__toggler',
|
|
)
|
|
|
|
const documentDrawer = page.locator('[id^=doc-drawer_relation-one_1_]')
|
|
await expect(documentDrawer).toBeVisible()
|
|
})
|
|
|
|
test('should open document drawer and append newly created docs onto the parent field', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
await openCreateDocDrawer(page, '#field-relationshipHasMany')
|
|
const documentDrawer = page.locator('[id^=doc-drawer_relation-one_1_]')
|
|
await expect(documentDrawer).toBeVisible()
|
|
const drawerField = documentDrawer.locator('#field-name')
|
|
await drawerField.fill('Newly created document')
|
|
const saveButton = documentDrawer.locator('#action-save')
|
|
await saveButton.click()
|
|
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
|
|
await expect(
|
|
page.locator('#field-relationshipHasMany .value-container .rs__multi-value'),
|
|
).toHaveCount(1)
|
|
await drawerField.fill('Updated document')
|
|
await saveButton.click()
|
|
await expect(page.locator('.payload-toast-container')).toContainText('Updated successfully')
|
|
await page.locator('.doc-drawer__header-close').click()
|
|
await expect(
|
|
page.locator('#field-relationshipHasMany .value-container .rs__multi-value'),
|
|
).toHaveCount(1)
|
|
})
|
|
|
|
describe('existing relationships', () => {
|
|
test('should highlight existing relationship', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
const field = page.locator('#field-relationship')
|
|
await expect(field.locator('input')).toBeEnabled()
|
|
await field.click({ delay: 100 })
|
|
await expect(page.locator('.rs__option--is-selected')).toHaveCount(1)
|
|
await expect(page.locator('.rs__option--is-selected')).toHaveText(relationOneDoc.id)
|
|
})
|
|
|
|
test('should show untitled ID on restricted relation', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
|
|
const field = page.locator('#field-relationshipRestricted')
|
|
|
|
// Check existing relationship has untitled ID
|
|
await expect(field).toContainText(`Untitled - ID: ${restrictedRelation.id}`)
|
|
|
|
// Check dropdown options
|
|
await field.click({ delay: 100 })
|
|
const options = page.locator('.rs__option')
|
|
|
|
await expect(options).toHaveCount(1) // None + 1 Unitled ID
|
|
})
|
|
|
|
// test.todo('should paginate within the dropdown');
|
|
|
|
test('should search within the relationship field', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
const input = page.locator('#field-relationshipWithTitle input')
|
|
await input.fill('title')
|
|
const options = page.locator('#field-relationshipWithTitle .rs__menu .rs__option')
|
|
await expect(options).toHaveCount(1)
|
|
|
|
await input.fill('non-occurring-string')
|
|
await expect(options).toHaveCount(0)
|
|
})
|
|
|
|
test('should search using word boundaries within the relationship field', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
const input = page.locator('#field-relationshipWithTitle input')
|
|
await input.fill('word search')
|
|
const options = page.locator('#field-relationshipWithTitle .rs__menu .rs__option')
|
|
await expect(options).toHaveCount(1)
|
|
})
|
|
|
|
test('should show useAsTitle on relation', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
|
|
const field = page.locator('#field-relationshipWithTitle')
|
|
const value = field.locator('.relationship--single-value__text')
|
|
|
|
// Check existing relationship for correct title
|
|
await expect(value).toHaveText(relationWithTitle.name)
|
|
|
|
await field.click({ delay: 100 })
|
|
const options = field.locator('.rs__option')
|
|
|
|
await expect(options).toHaveCount(2)
|
|
})
|
|
|
|
test('should show id on relation in list view', async () => {
|
|
await page.goto(url.list)
|
|
await wait(110)
|
|
const relationship = page.locator('.row-1 .cell-relationship')
|
|
await expect(relationship).toHaveText(relationOneDoc.id)
|
|
})
|
|
|
|
test('should show Untitled ID on restricted relation in list view', async () => {
|
|
await page.goto(url.list)
|
|
await wait(110)
|
|
const relationship = page.locator('.row-1 .cell-relationshipRestricted')
|
|
await expect(relationship).toContainText('Untitled - ID: ')
|
|
})
|
|
|
|
test('x in list view', async () => {
|
|
await page.goto(url.list)
|
|
await wait(110)
|
|
const relationship = page.locator('.row-1 .cell-relationshipWithTitle')
|
|
await expect(relationship).toHaveText(relationWithTitle.name)
|
|
})
|
|
|
|
test('should update relationship values on page change in list view', async () => {
|
|
await clearCollectionDocs(slug)
|
|
// create new docs to paginate to
|
|
for (let i = 0; i < 10; i++) {
|
|
await payload.create({
|
|
collection: slug,
|
|
data: {
|
|
relationshipHasManyMultiple: [
|
|
{
|
|
relationTo: relationOneSlug,
|
|
value: relationOneDoc.id,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
}
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
await payload.create({
|
|
collection: slug,
|
|
data: {
|
|
relationshipHasManyMultiple: [
|
|
{
|
|
relationTo: relationTwoSlug,
|
|
value: relationTwoDoc.id,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
}
|
|
|
|
await page.goto(url.list)
|
|
|
|
// check first doc on first page
|
|
const relationship = page.locator('.row-1 .cell-relationshipHasManyMultiple')
|
|
await expect(relationship).toHaveText(relationTwoDoc.id)
|
|
|
|
const paginator = page.locator('.clickable-arrow--right')
|
|
await paginator.click()
|
|
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('page=2')
|
|
|
|
// check first doc on second page (should be different)
|
|
await expect(relationship).toContainText(relationOneDoc.id)
|
|
})
|
|
})
|
|
|
|
describe('externally update relationship field', () => {
|
|
beforeEach(async () => {
|
|
const externalRelationURL = new AdminUrlUtil(serverURL, relationUpdatedExternallySlug)
|
|
await page.goto(externalRelationURL.create)
|
|
})
|
|
|
|
test('has many, one collection', async () => {
|
|
await page.locator('#field-relationHasMany + .pre-populate-field-ui button').click()
|
|
await wait(300)
|
|
await expect(
|
|
page.locator('#field-relationHasMany .rs__value-container > .rs__multi-value'),
|
|
).toHaveCount(15)
|
|
})
|
|
|
|
test('has many, many collections', async () => {
|
|
await page.locator('#field-relationToManyHasMany + .pre-populate-field-ui button').click()
|
|
await wait(300)
|
|
await expect(
|
|
page.locator('#field-relationToManyHasMany .rs__value-container > .rs__multi-value'),
|
|
).toHaveCount(15)
|
|
})
|
|
})
|
|
|
|
describe('field relationship with many items', () => {
|
|
beforeEach(async () => {
|
|
const relations: string[] = []
|
|
const batchSize = 10
|
|
const totalRelations = 300
|
|
const totalBatches = Math.ceil(totalRelations / batchSize)
|
|
for (let i = 0; i < totalBatches; i++) {
|
|
const batchPromises: Promise<RelationOne>[] = []
|
|
const start = i * batchSize
|
|
const end = Math.min(start + batchSize, totalRelations)
|
|
|
|
for (let j = start; j < end; j++) {
|
|
batchPromises.push(
|
|
payload.create({
|
|
collection: relationOneSlug,
|
|
data: {
|
|
name: 'relation',
|
|
},
|
|
}),
|
|
)
|
|
}
|
|
|
|
const batchRelations = await Promise.all(batchPromises)
|
|
relations.push(...batchRelations.map((doc) => doc.id))
|
|
}
|
|
|
|
await payload.update({
|
|
id: docWithExistingRelations.id,
|
|
collection: slug,
|
|
data: {
|
|
relationshipHasMany: relations,
|
|
},
|
|
})
|
|
})
|
|
|
|
test('should update with new relationship', async () => {
|
|
await page.goto(url.edit(docWithExistingRelations.id))
|
|
|
|
const field = page.locator('#field-relationshipHasMany')
|
|
const dropdownIndicator = field.locator('.dropdown-indicator')
|
|
await dropdownIndicator.click({ delay: 100 })
|
|
|
|
const options = page.locator('.rs__option')
|
|
await expect(options).toHaveCount(2)
|
|
|
|
await options.nth(0).click()
|
|
await expect(field).toContainText(relationOneDoc.id)
|
|
|
|
await saveDocAndAssert(page)
|
|
})
|
|
})
|
|
})
|
|
|
|
async function clearAllDocs(): Promise<void> {
|
|
await clearCollectionDocs(slug)
|
|
await clearCollectionDocs(relationOneSlug)
|
|
await clearCollectionDocs(relationTwoSlug)
|
|
await clearCollectionDocs(relationRestrictedSlug)
|
|
await clearCollectionDocs(relationWithTitleSlug)
|
|
}
|
|
|
|
async function clearCollectionDocs(collectionSlug: string): Promise<void> {
|
|
await payload.delete({
|
|
collection: collectionSlug,
|
|
where: {
|
|
id: { exists: true },
|
|
},
|
|
})
|
|
}
|