test: adds e2e tests for auth enabled collections with trash enabled (#13317)
### What? - Added new end-to-end tests covering trash functionality for auth-enabled collections (e.g., `users`). - Implemented test cases for: - Display of the trash tab in the list view. - Trashing a user and verifying its appearance in the trash view. - Accessing the trashed user edit view. - Ensuring all auth fields are properly disabled in trashed state. - Restoring a trashed user and verifying its status. ### Why? - To ensure that the trash (soft-delete) feature works consistently for collections with `auth: true`. - To prevent regressions in user management flows, especially around disabling and restoring trashed users. ### How? - Added a new `Auth enabled collection` test suite in the E2E `Trash` tests.
This commit is contained in:
@@ -7,6 +7,7 @@ export const Users: CollectionConfig = {
|
|||||||
admin: {
|
admin: {
|
||||||
useAsTitle: 'name',
|
useAsTitle: 'name',
|
||||||
},
|
},
|
||||||
|
trash: true,
|
||||||
auth: true,
|
auth: true,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
|||||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||||
import { pagesSlug } from './collections/Pages/index.js'
|
import { pagesSlug } from './collections/Pages/index.js'
|
||||||
import { postsSlug } from './collections/Posts/index.js'
|
import { postsSlug } from './collections/Posts/index.js'
|
||||||
|
import { usersSlug } from './collections/Users/index.js'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
@@ -26,10 +27,12 @@ let postsUrl: AdminUrlUtil
|
|||||||
let pagesUrl: AdminUrlUtil
|
let pagesUrl: AdminUrlUtil
|
||||||
let payload: PayloadTestSDK<Config>
|
let payload: PayloadTestSDK<Config>
|
||||||
let serverURL: string
|
let serverURL: string
|
||||||
|
let usersUrl: AdminUrlUtil
|
||||||
|
|
||||||
let pagesDocOne: PageType
|
let pagesDocOne: PageType
|
||||||
let postsDocOne: Post
|
let postsDocOne: Post
|
||||||
let postsDocTwo: Post
|
let postsDocTwo: Post
|
||||||
|
let devUserID: number | string
|
||||||
|
|
||||||
describe('Trash', () => {
|
describe('Trash', () => {
|
||||||
beforeAll(async ({ browser }, testInfo) => {
|
beforeAll(async ({ browser }, testInfo) => {
|
||||||
@@ -37,6 +40,7 @@ describe('Trash', () => {
|
|||||||
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({ dirname }))
|
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({ dirname }))
|
||||||
postsUrl = new AdminUrlUtil(serverURL, postsSlug)
|
postsUrl = new AdminUrlUtil(serverURL, postsSlug)
|
||||||
pagesUrl = new AdminUrlUtil(serverURL, pagesSlug)
|
pagesUrl = new AdminUrlUtil(serverURL, pagesSlug)
|
||||||
|
usersUrl = new AdminUrlUtil(serverURL, usersSlug)
|
||||||
|
|
||||||
const context = await browser.newContext()
|
const context = await browser.newContext()
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
@@ -993,6 +997,110 @@ describe('Trash', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('Auth enabled collection', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
// Ensure Dev user exists and store its ID
|
||||||
|
const { docs } = await payload.find({
|
||||||
|
collection: usersSlug,
|
||||||
|
limit: 1,
|
||||||
|
where: { name: { equals: 'Dev' } },
|
||||||
|
trash: true,
|
||||||
|
})
|
||||||
|
if (docs.length === 0) {
|
||||||
|
throw new Error('Dev user not found! Ensure test seed data includes a Dev user.')
|
||||||
|
}
|
||||||
|
devUserID = docs[0]?.id as number | string
|
||||||
|
})
|
||||||
|
|
||||||
|
async function ensureDevUserTrashed() {
|
||||||
|
const { docs } = await payload.find({
|
||||||
|
collection: usersSlug,
|
||||||
|
where: {
|
||||||
|
and: [{ name: { equals: 'Dev' } }, { deletedAt: { exists: true } }],
|
||||||
|
},
|
||||||
|
limit: 1,
|
||||||
|
trash: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (docs.length === 0) {
|
||||||
|
// Trash the user if it's not already trashed
|
||||||
|
await payload.update({
|
||||||
|
collection: usersSlug,
|
||||||
|
id: devUserID,
|
||||||
|
data: { deletedAt: new Date().toISOString() },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('Should show trash tab in the list view of a collection with auth enabled', async () => {
|
||||||
|
await page.goto(usersUrl.list)
|
||||||
|
|
||||||
|
await expect(page.locator('#trash-view-pill')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should successfully trash a user from the list view and show it in the trash view', async () => {
|
||||||
|
await page.goto(usersUrl.list)
|
||||||
|
|
||||||
|
await page.locator('.row-1 .cell-_select input').check()
|
||||||
|
await page.locator('.list-selection__button[aria-label="Delete"]').click()
|
||||||
|
|
||||||
|
// Skip the checkbox to delete permanently and default to trashing
|
||||||
|
await page.locator('#confirm-delete-many-docs #confirm-action').click()
|
||||||
|
await expect(page.locator('.payload-toast-container .toast-success')).toHaveText(
|
||||||
|
'1 User moved to trash.',
|
||||||
|
)
|
||||||
|
// Navigate to the trash view
|
||||||
|
await page.locator('#trash-view-pill').click()
|
||||||
|
await expect(page.locator('.row-1 .cell-name')).toHaveText('Dev')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should be able to access trashed doc edit view from the trash view', async () => {
|
||||||
|
await ensureDevUserTrashed()
|
||||||
|
|
||||||
|
await page.goto(usersUrl.trash)
|
||||||
|
|
||||||
|
await expect(page.locator('.row-1 .cell-name')).toHaveText('Dev')
|
||||||
|
await page.locator('.row-1 .cell-name').click()
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(/\/users\/trash\/[a-f0-9]{24}$/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should properly disable auth fields in the trashed user edit view', async () => {
|
||||||
|
await ensureDevUserTrashed()
|
||||||
|
|
||||||
|
await page.goto(usersUrl.trash)
|
||||||
|
|
||||||
|
await page.locator('.row-1 .cell-name').click()
|
||||||
|
|
||||||
|
await expect(page.locator('input[name="email"]')).toBeDisabled()
|
||||||
|
await expect(page.locator('#change-password')).toBeDisabled()
|
||||||
|
|
||||||
|
await expect(page.locator('#field-name')).toBeDisabled()
|
||||||
|
await expect(page.locator('#field-roles .rs__input')).toBeDisabled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should properly restore trashed user as draft', async () => {
|
||||||
|
await ensureDevUserTrashed()
|
||||||
|
|
||||||
|
await page.goto(usersUrl.trash)
|
||||||
|
|
||||||
|
await expect(page.locator('.row-1 .cell-name')).toHaveText('Dev')
|
||||||
|
await page.locator('.row-1 .cell-name').click()
|
||||||
|
|
||||||
|
await page.locator('.doc-controls__controls #action-restore').click()
|
||||||
|
|
||||||
|
await expect(page.locator(`#restore-${devUserID} #confirm-action`)).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.locator(`#restore-${devUserID} .confirmation-modal__content`),
|
||||||
|
).toContainText('You are about to restore the User')
|
||||||
|
|
||||||
|
await page.locator(`#restore-${devUserID} #confirm-action`).click()
|
||||||
|
|
||||||
|
await expect(page.locator('.payload-toast-container .toast-success')).toHaveText(
|
||||||
|
'User "Dev" successfully restored.',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function createPageDoc(data: Partial<PageType>): Promise<PageType> {
|
async function createPageDoc(data: Partial<PageType>): Promise<PageType> {
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ export interface User {
|
|||||||
roles?: ('is_user' | 'is_admin')[] | null;
|
roles?: ('is_user' | 'is_admin')[] | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
deletedAt?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
resetPasswordToken?: string | null;
|
resetPasswordToken?: string | null;
|
||||||
resetPasswordExpiration?: string | null;
|
resetPasswordExpiration?: string | null;
|
||||||
@@ -284,6 +285,7 @@ export interface UsersSelect<T extends boolean = true> {
|
|||||||
roles?: T;
|
roles?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
|
deletedAt?: T;
|
||||||
email?: T;
|
email?: T;
|
||||||
resetPasswordToken?: T;
|
resetPasswordToken?: T;
|
||||||
resetPasswordExpiration?: T;
|
resetPasswordExpiration?: T;
|
||||||
|
|||||||
Reference in New Issue
Block a user