test: dedicated bulk edit test suite (#11756)
Consolidates all bulk edit related tests into a single, dedicated suite. Currently, bulk edit tests are dispersed throughout the Admin > General and the Versions test suites, which are considerably bloated for their own purposes. This made them very hard to locate, mentally digest, and add on new tests. Going forward, many more tests specifically for bulk edit will need to be written. This gives us a simple, isolated place for that. With this change are also a few improvements to the tests themselves to make them more predictable and efficient.
This commit is contained in:
@@ -137,64 +137,6 @@ export const Posts: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'arrayOfFields',
|
||||
type: 'array',
|
||||
admin: {
|
||||
initCollapsed: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'optional',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'innerArrayOfFields',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'innerOptional',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'defaultValueField',
|
||||
type: 'text',
|
||||
defaultValue: 'testing',
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'someBlock',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'textBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'textFieldForBlock',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'defaultValueField',
|
||||
type: 'text',
|
||||
defaultValue: 'testing',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Config, Geo, Post } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
getRoutes,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
@@ -781,205 +780,6 @@ describe('General', () => {
|
||||
expect(page.url()).toContain(postsUrl.list)
|
||||
})
|
||||
|
||||
test('should bulk delete all on page', async () => {
|
||||
await deleteAllPosts()
|
||||
await Promise.all([createPost(), createPost(), createPost()])
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('.delete-documents__toggle').click()
|
||||
await page.locator('#delete-posts #confirm-action').click()
|
||||
|
||||
await expect(page.locator('.payload-toast-container .toast-success')).toHaveText(
|
||||
'Deleted 3 Posts successfully.',
|
||||
)
|
||||
|
||||
// Poll until router has refreshed
|
||||
await expect.poll(() => page.locator('.collection-list__no-results').isVisible()).toBeTruthy()
|
||||
})
|
||||
|
||||
test('should bulk delete with filters and across pages', async () => {
|
||||
await deleteAllPosts()
|
||||
|
||||
Array.from({ length: 6 }).forEach(async (_, i) => {
|
||||
await createPost({ title: `Post ${i + 1}` })
|
||||
})
|
||||
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('#search-filter-input').fill('Post')
|
||||
await page.waitForURL(/search=Post/)
|
||||
await expect(page.locator('.table table > tbody > tr')).toHaveCount(5)
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('button#select-all-across-pages').click()
|
||||
await page.locator('.delete-documents__toggle').click()
|
||||
await page.locator('#delete-posts #confirm-action').click()
|
||||
|
||||
await expect(page.locator('.payload-toast-container .toast-success')).toHaveText(
|
||||
'Deleted 6 Posts successfully.',
|
||||
)
|
||||
|
||||
// Poll until router has refreshed
|
||||
await expect.poll(() => page.locator('.table table > tbody > tr').count()).toBe(0)
|
||||
})
|
||||
|
||||
test('should bulk update', async () => {
|
||||
// First, delete all posts created by the seed
|
||||
await deleteAllPosts()
|
||||
const post1Title = 'Post'
|
||||
const updatedPostTitle = `${post1Title} (Updated)`
|
||||
await Promise.all([createPost({ title: post1Title }), createPost(), createPost()])
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('.edit-many__toggle').click()
|
||||
await page.locator('.field-select .rs__control').click()
|
||||
|
||||
const titleOption = page.locator('.field-select .rs__option', {
|
||||
hasText: exactText('Title'),
|
||||
})
|
||||
|
||||
await expect(titleOption).toBeVisible()
|
||||
await titleOption.click()
|
||||
const titleInput = page.locator('#field-title')
|
||||
await expect(titleInput).toBeVisible()
|
||||
await titleInput.fill(updatedPostTitle)
|
||||
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
|
||||
|
||||
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
|
||||
'Updated 3 Posts successfully.',
|
||||
)
|
||||
|
||||
await expect(page.locator('.row-1 .cell-title')).toContainText(updatedPostTitle)
|
||||
await expect(page.locator('.row-2 .cell-title')).toContainText(updatedPostTitle)
|
||||
await expect(page.locator('.row-3 .cell-title')).toContainText(updatedPostTitle)
|
||||
})
|
||||
|
||||
test('should not override un-edited values in bulk edit if it has a defaultValue', async () => {
|
||||
await deleteAllPosts()
|
||||
const post1Title = 'Post'
|
||||
|
||||
const postData = {
|
||||
title: 'Post',
|
||||
arrayOfFields: [
|
||||
{
|
||||
optional: 'some optional array field',
|
||||
innerArrayOfFields: [
|
||||
{
|
||||
innerOptional: 'some inner optional array field',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
group: {
|
||||
defaultValueField: 'not the group default value',
|
||||
title: 'some title',
|
||||
},
|
||||
someBlock: [
|
||||
{
|
||||
textFieldForBlock: 'some text for block text',
|
||||
blockType: 'textBlock',
|
||||
},
|
||||
],
|
||||
defaultValueField: 'not the default value',
|
||||
}
|
||||
|
||||
const updatedPostTitle = `${post1Title} (Updated)`
|
||||
await createPost(postData)
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('.edit-many__toggle').click()
|
||||
await page.locator('.field-select .rs__control').click()
|
||||
|
||||
const titleOption = page.locator('.field-select .rs__option', {
|
||||
hasText: exactText('Title'),
|
||||
})
|
||||
|
||||
await titleOption.click()
|
||||
const titleInput = page.locator('#field-title')
|
||||
await titleInput.fill(updatedPostTitle)
|
||||
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
|
||||
|
||||
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
|
||||
'Updated 1 Post successfully.',
|
||||
)
|
||||
|
||||
const updatedPost = await payload.find({
|
||||
collection: 'posts',
|
||||
limit: 1,
|
||||
})
|
||||
|
||||
expect(updatedPost.docs[0].title).toBe(updatedPostTitle)
|
||||
expect(updatedPost.docs[0].arrayOfFields.length).toBe(1)
|
||||
expect(updatedPost.docs[0].arrayOfFields[0].optional).toBe('some optional array field')
|
||||
expect(updatedPost.docs[0].arrayOfFields[0].innerArrayOfFields.length).toBe(1)
|
||||
expect(updatedPost.docs[0].someBlock[0].textFieldForBlock).toBe('some text for block text')
|
||||
expect(updatedPost.docs[0].defaultValueField).toBe('not the default value')
|
||||
})
|
||||
|
||||
test('should not show "select all across pages" button if already selected all', async () => {
|
||||
await deleteAllPosts()
|
||||
await createPost({ title: `Post 1` })
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('input#select-all').check()
|
||||
await expect(page.locator('button#select-all-across-pages')).toBeHidden()
|
||||
})
|
||||
|
||||
test('should bulk update with filters and across pages', async () => {
|
||||
// First, delete all posts created by the seed
|
||||
await deleteAllPosts()
|
||||
|
||||
Array.from({ length: 6 }).forEach(async (_, i) => {
|
||||
await createPost({ title: `Post ${i + 1}` })
|
||||
})
|
||||
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('#search-filter-input').fill('Post')
|
||||
await page.waitForURL(/search=Post/)
|
||||
await expect(page.locator('.table table > tbody > tr')).toHaveCount(5)
|
||||
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('button#select-all-across-pages').click()
|
||||
|
||||
await page.locator('.edit-many__toggle').click()
|
||||
await page.locator('.field-select .rs__control').click()
|
||||
|
||||
const titleOption = page.locator('.field-select .rs__option', {
|
||||
hasText: exactText('Title'),
|
||||
})
|
||||
|
||||
await expect(titleOption).toBeVisible()
|
||||
await titleOption.click()
|
||||
const titleInput = page.locator('#field-title')
|
||||
await expect(titleInput).toBeVisible()
|
||||
const updatedTitle = `Post (Updated)`
|
||||
await titleInput.fill(updatedTitle)
|
||||
|
||||
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
|
||||
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
|
||||
'Updated 6 Posts successfully.',
|
||||
)
|
||||
|
||||
// Poll until router has refreshed
|
||||
await expect.poll(() => page.locator('.table table > tbody > tr').count()).toBe(5)
|
||||
await expect(page.locator('.row-1 .cell-title')).toContainText(updatedTitle)
|
||||
})
|
||||
|
||||
test('should update selection state after deselecting item following select all', async () => {
|
||||
await deleteAllPosts()
|
||||
|
||||
Array.from({ length: 6 }).forEach(async (_, i) => {
|
||||
await createPost({ title: `Post ${i + 1}` })
|
||||
})
|
||||
|
||||
await page.goto(postsUrl.list)
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('button#select-all-across-pages').click()
|
||||
|
||||
// Deselect the first row
|
||||
await page.locator('.row-1 input').click()
|
||||
|
||||
// eslint-disable-next-line jest-dom/prefer-checked
|
||||
await expect(page.locator('input#select-all')).not.toHaveAttribute('checked', '')
|
||||
})
|
||||
|
||||
test('should save globals', async () => {
|
||||
await page.goto(postsUrl.global(globalSlug))
|
||||
|
||||
@@ -1079,10 +879,6 @@ describe('General', () => {
|
||||
})
|
||||
})
|
||||
|
||||
async function deleteAllPosts() {
|
||||
await payload.delete({ collection: postsCollectionSlug, where: { id: { exists: true } } })
|
||||
}
|
||||
|
||||
async function createPost(overrides?: Partial<Post>): Promise<Post> {
|
||||
return payload.create({
|
||||
collection: postsCollectionSlug,
|
||||
|
||||
@@ -54,6 +54,7 @@ export type SupportedTimezones =
|
||||
| 'Asia/Singapore'
|
||||
| 'Asia/Tokyo'
|
||||
| 'Asia/Seoul'
|
||||
| 'Australia/Brisbane'
|
||||
| 'Australia/Sydney'
|
||||
| 'Pacific/Guam'
|
||||
| 'Pacific/Noumea'
|
||||
@@ -232,31 +233,6 @@ export interface Post {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
arrayOfFields?:
|
||||
| {
|
||||
optional?: string | null;
|
||||
innerArrayOfFields?:
|
||||
| {
|
||||
innerOptional?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
group?: {
|
||||
defaultValueField?: string | null;
|
||||
title?: string | null;
|
||||
};
|
||||
someBlock?:
|
||||
| {
|
||||
textFieldForBlock?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}[]
|
||||
| null;
|
||||
defaultValueField?: string | null;
|
||||
relationship?: (string | null) | Post;
|
||||
users?: (string | null) | User;
|
||||
customCell?: string | null;
|
||||
@@ -676,36 +652,6 @@ export interface PostsSelect<T extends boolean = true> {
|
||||
description?: T;
|
||||
number?: T;
|
||||
richText?: T;
|
||||
arrayOfFields?:
|
||||
| T
|
||||
| {
|
||||
optional?: T;
|
||||
innerArrayOfFields?:
|
||||
| T
|
||||
| {
|
||||
innerOptional?: T;
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
group?:
|
||||
| T
|
||||
| {
|
||||
defaultValueField?: T;
|
||||
title?: T;
|
||||
};
|
||||
someBlock?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
textFieldForBlock?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
defaultValueField?: T;
|
||||
relationship?: T;
|
||||
users?: T;
|
||||
customCell?: T;
|
||||
|
||||
Reference in New Issue
Block a user