fix(next, ui): exclude expired locks for globals (#8914)

Continued PR off of https://github.com/payloadcms/payload/pull/8899
This commit is contained in:
Patrik
2024-10-28 21:49:50 -04:00
committed by GitHub
parent 1e002acce9
commit e74906f555
10 changed files with 455 additions and 86 deletions

View File

@@ -2,6 +2,7 @@ import type { Page } from '@playwright/test'
import type { TypeWithID } from 'payload'
import { expect, test } from '@playwright/test'
import exp from 'constants'
import * as path from 'path'
import { mapAsync } from 'payload'
import { wait } from 'payload/shared'
@@ -32,6 +33,7 @@ let page: Page
let globalUrl: AdminUrlUtil
let postsUrl: AdminUrlUtil
let pagesUrl: AdminUrlUtil
let testsUrl: AdminUrlUtil
let payload: PayloadTestSDK<Config>
let serverURL: string
@@ -43,6 +45,7 @@ describe('locked documents', () => {
globalUrl = new AdminUrlUtil(serverURL, 'menu')
postsUrl = new AdminUrlUtil(serverURL, 'posts')
pagesUrl = new AdminUrlUtil(serverURL, 'pages')
testsUrl = new AdminUrlUtil(serverURL, 'tests')
const context = await browser.newContext()
page = await context.newPage()
@@ -82,6 +85,8 @@ describe('locked documents', () => {
let anotherPostDoc
let user2
let lockedDoc
let testDoc
let testLockedDoc
beforeAll(async () => {
postDoc = await createPostDoc({
@@ -92,6 +97,10 @@ describe('locked documents', () => {
text: 'another post',
})
testDoc = await createTestDoc({
text: 'test doc',
})
user2 = await payload.create({
collection: 'users',
data: {
@@ -114,6 +123,21 @@ describe('locked documents', () => {
},
},
})
testLockedDoc = await payload.create({
collection: lockedDocumentCollection,
data: {
document: {
relationTo: 'tests',
value: testDoc.id,
},
globalSlug: undefined,
user: {
relationTo: 'users',
value: user2.id,
},
},
})
})
afterAll(async () => {
@@ -122,6 +146,11 @@ describe('locked documents', () => {
id: lockedDoc.id,
})
await payload.delete({
collection: lockedDocumentCollection,
id: testLockedDoc.id,
})
await payload.delete({
collection: 'posts',
id: postDoc.id,
@@ -132,6 +161,11 @@ describe('locked documents', () => {
id: anotherPostDoc.id,
})
await payload.delete({
collection: 'tests',
id: testDoc.id,
})
await payload.delete({
collection: 'users',
id: user2.id,
@@ -140,18 +174,31 @@ describe('locked documents', () => {
test('should show lock icon on document row if locked', async () => {
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await page.waitForURL(new RegExp(postsUrl.list))
await expect(page.locator('.table .row-2 .locked svg')).toBeVisible()
})
test('should not show lock icon on document row if unlocked', async () => {
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await page.waitForURL(new RegExp(postsUrl.list))
await expect(page.locator('.table .row-3 .checkbox-input__input')).toBeVisible()
})
test('should not show lock icon on document if expired', async () => {
await page.goto(testsUrl.list)
await page.waitForURL(new RegExp(testsUrl.list))
// Need to wait for lock duration to expire (lockDuration: 5 seconds)
// eslint-disable-next-line payload/no-wait-function
await wait(5000)
await page.reload()
await expect(page.locator('.table .row-1 .checkbox-input__input')).toBeVisible()
})
test('should not show lock icon on document row if locked by current user', async () => {
await page.goto(postsUrl.edit(anotherPostDoc.id))
await page.waitForURL(postsUrl.edit(anotherPostDoc.id))
@@ -162,7 +209,7 @@ describe('locked documents', () => {
await page.reload()
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await page.waitForURL(new RegExp(postsUrl.list))
await expect(page.locator('.table .row-1 .checkbox-input__input')).toBeVisible()
})
@@ -280,18 +327,146 @@ describe('locked documents', () => {
describe('document locking / unlocking - one user', () => {
let postDoc
let postDocTwo
let expiredDocOne
let expiredLockedDocOne
let expiredDocTwo
let expiredLockedDocTwo
let testDoc
let user2
beforeAll(async () => {
postDoc = await createPostDoc({
text: 'hello',
})
postDocTwo = await createPostDoc({
text: 'post doc two',
})
user2 = await payload.create({
collection: 'users',
data: {
email: 'user2@payloadcms.com',
password: '1234',
},
})
expiredDocOne = await createTestDoc({
text: 'expired doc one',
})
expiredLockedDocOne = await payload.create({
collection: lockedDocumentCollection,
data: {
document: {
relationTo: 'tests',
value: expiredDocOne.id,
},
globalSlug: undefined,
user: {
relationTo: 'users',
value: user2.id,
},
},
})
expiredDocTwo = await createTestDoc({
text: 'expired doc two',
})
expiredLockedDocTwo = await payload.create({
collection: lockedDocumentCollection,
data: {
document: {
relationTo: 'tests',
value: expiredDocTwo.id,
},
globalSlug: undefined,
user: {
relationTo: 'users',
value: user2.id,
},
},
})
testDoc = await createTestDoc({ text: 'hello' })
})
afterAll(async () => {
await payload.delete({
collection: lockedDocumentCollection,
id: expiredDocOne.id,
})
await payload.delete({
collection: lockedDocumentCollection,
id: expiredDocTwo.id,
})
await payload.delete({
collection: 'posts',
id: postDoc.id,
})
await payload.delete({
collection: 'posts',
id: postDocTwo.id,
})
await payload.delete({
collection: 'tests',
id: expiredDocOne.id,
})
await payload.delete({
collection: 'tests',
id: expiredDocTwo.id,
})
await payload.delete({
collection: 'tests',
id: testDoc.id,
})
})
test('should delete all expired locked documents upon initial editing of unlocked document', async () => {
await page.goto(testsUrl.list)
await page.waitForURL(new RegExp(testsUrl.list))
await expect(page.locator('.table .row-2 .locked svg')).toBeVisible()
await expect(page.locator('.table .row-3 .locked svg')).toBeVisible()
// eslint-disable-next-line payload/no-wait-function
await wait(5000)
await page.reload()
await expect(page.locator('.table .row-2 .checkbox-input__input')).toBeVisible()
await expect(page.locator('.table .row-3 .checkbox-input__input')).toBeVisible()
const lockedTestDocs = await payload.find({
collection: lockedDocumentCollection,
pagination: false,
})
expect(lockedTestDocs.docs.length).toBe(2)
await page.goto(testsUrl.edit(testDoc.id))
await page.waitForURL(testsUrl.edit(testDoc.id))
const textInput = page.locator('#field-text')
await textInput.fill('some test doc')
// eslint-disable-next-line payload/no-wait-function
await wait(500)
const lockedDocs = await payload.find({
collection: lockedDocumentCollection,
pagination: false,
})
expect(lockedDocs.docs.length).toBe(1)
})
test('should lock document upon initial editing of unlocked document', async () => {
@@ -316,53 +491,6 @@ describe('locked documents', () => {
expect(lockedDocs.docs.length).toBe(1)
})
test('should unlock document on navigate away', async () => {
await page.goto(postsUrl.edit(postDoc.id))
await page.waitForURL(postsUrl.edit(postDoc.id))
const textInput = page.locator('#field-text')
await textInput.fill('hello world')
// eslint-disable-next-line payload/no-wait-function
await wait(500)
const lockedDocs = await payload.find({
collection: lockedDocumentCollection,
limit: 1,
pagination: false,
where: {
'document.value': { equals: postDoc.id },
},
})
expect(lockedDocs.docs.length).toBe(1)
await page.locator('header.app-header a[href="/admin/collections/posts"]').click()
// Locate the modal container
const modalContainer = page.locator('.payload__modal-container')
await expect(modalContainer).toBeVisible()
// Click the "Leave anyway" button
await page.locator('.leave-without-saving__controls .btn--style-primary').click()
// eslint-disable-next-line payload/no-wait-function
await wait(500)
expect(page.url()).toContain(postsUrl.list)
const unlockedDocs = await payload.find({
collection: lockedDocumentCollection,
limit: 1,
pagination: false,
where: {
'document.value': { equals: postDoc.id },
},
})
expect(unlockedDocs.docs.length).toBe(0)
})
test('should unlock document on save / publish', async () => {
await page.goto(postsUrl.edit(postDoc.id))
await page.waitForURL(postsUrl.edit(postDoc.id))
@@ -444,6 +572,60 @@ describe('locked documents', () => {
})
expect(unlockedDocs.docs.length).toBe(1)
await payload.delete({
collection: lockedDocumentCollection,
where: {
'document.value': { equals: postDoc.id },
},
})
})
test('should unlock document on navigate away', async () => {
await page.goto(postsUrl.edit(postDocTwo.id))
await page.waitForURL(postsUrl.edit(postDocTwo.id))
const textInput = page.locator('#field-text')
await textInput.fill('hello world')
// eslint-disable-next-line payload/no-wait-function
await wait(1000)
const lockedDocs = await payload.find({
collection: lockedDocumentCollection,
limit: 1,
pagination: false,
where: {
'document.value': { equals: postDocTwo.id },
},
})
expect(lockedDocs.docs.length).toBe(1)
await page.locator('header.app-header a[href="/admin/collections/posts"]').click()
// Locate the modal container
const modalContainer = page.locator('.payload__modal-container')
await expect(modalContainer).toBeVisible()
// Click the "Leave anyway" button
await page.locator('.leave-without-saving__controls .btn--style-primary').click()
// eslint-disable-next-line payload/no-wait-function
await wait(500)
expect(page.url()).toContain(postsUrl.list)
const unlockedDocs = await payload.find({
collection: lockedDocumentCollection,
limit: 1,
pagination: false,
where: {
'document.value': { equals: postDoc.id },
},
})
expect(unlockedDocs.docs.length).toBe(0)
})
})
@@ -451,12 +633,18 @@ describe('locked documents', () => {
let postDoc
let user2
let lockedDoc
let expiredTestDoc
let expiredTestLockedDoc
beforeAll(async () => {
postDoc = await createPostDoc({
text: 'hello',
})
expiredTestDoc = await createTestDoc({
text: 'expired doc',
})
user2 = await payload.create({
collection: 'users',
data: {
@@ -479,6 +667,21 @@ describe('locked documents', () => {
},
},
})
expiredTestLockedDoc = await payload.create({
collection: lockedDocumentCollection,
data: {
document: {
relationTo: 'tests',
value: expiredTestDoc.id,
},
globalSlug: undefined,
user: {
relationTo: 'users',
value: user2.id,
},
},
})
})
afterAll(async () => {
@@ -492,10 +695,20 @@ describe('locked documents', () => {
id: lockedDoc.id,
})
await payload.delete({
collection: lockedDocumentCollection,
id: expiredTestLockedDoc.id,
})
await payload.delete({
collection: 'posts',
id: postDoc.id,
})
await payload.delete({
collection: 'tests',
id: expiredTestDoc.id,
})
})
test('should show Document Locked modal for incoming user when entering locked document', async () => {
@@ -514,7 +727,7 @@ describe('locked documents', () => {
await wait(500)
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await page.waitForURL(new RegExp(postsUrl.list))
// eslint-disable-next-line payload/no-wait-function
await wait(500)
@@ -531,6 +744,37 @@ describe('locked documents', () => {
expect(page.url()).toContain(postsUrl.list)
})
test('should not show Document Locked modal for incoming user when entering expired locked document', async () => {
const lockedDoc = await payload.find({
collection: lockedDocumentCollection,
limit: 1,
pagination: false,
where: {
'document.value': { equals: expiredTestDoc.id },
},
})
expect(lockedDoc.docs.length).toBe(1)
// eslint-disable-next-line payload/no-wait-function
await wait(500)
await page.goto(testsUrl.list)
await page.waitForURL(new RegExp(testsUrl.list))
// Need to wait for lock duration to expire (lockDuration: 5 seconds)
// eslint-disable-next-line payload/no-wait-function
await wait(5000)
await page.reload()
await page.goto(testsUrl.edit(expiredTestDoc.id))
await page.waitForURL(testsUrl.edit(expiredTestDoc.id))
const modalContainer = page.locator('.payload__modal-container')
await expect(modalContainer).toBeHidden()
})
test('should show fields in read-only if incoming user views locked doc in read-only mode', async () => {
await page.goto(postsUrl.edit(postDoc.id))
await page.waitForURL(postsUrl.edit(postDoc.id))
@@ -954,7 +1198,7 @@ describe('locked documents', () => {
test('should show lock on document card in dashboard view if locked', async () => {
await page.goto(postsUrl.admin)
await page.waitForURL(postsUrl.admin)
await page.waitForURL(new RegExp(postsUrl.admin))
const globalCardList = page.locator('.dashboard__group').nth(1)
await expect(globalCardList.locator('#card-menu .locked svg')).toBeVisible()
@@ -970,7 +1214,7 @@ describe('locked documents', () => {
await wait(500)
await page.goto(postsUrl.admin)
await page.waitForURL(postsUrl.admin)
await page.waitForURL(new RegExp(postsUrl.admin))
const globalCardList = page.locator('.dashboard__group').nth(1)
await expect(globalCardList.locator('#card-menu .locked')).toBeHidden()
@@ -986,7 +1230,7 @@ describe('locked documents', () => {
await page.reload()
await page.goto(postsUrl.admin)
await page.waitForURL(postsUrl.admin)
await page.waitForURL(new RegExp(postsUrl.admin))
const globalCardList = page.locator('.dashboard__group').nth(1)
await expect(globalCardList.locator('#card-menu .locked')).toBeHidden()
@@ -994,13 +1238,6 @@ describe('locked documents', () => {
})
})
async function createPostDoc(data: any): Promise<Record<string, unknown> & TypeWithID> {
return payload.create({
collection: 'posts',
data,
}) as unknown as Promise<Record<string, unknown> & TypeWithID>
}
async function createPageDoc(data: any): Promise<Record<string, unknown> & TypeWithID> {
return payload.create({
collection: 'pages',
@@ -1008,6 +1245,20 @@ async function createPageDoc(data: any): Promise<Record<string, unknown> & TypeW
}) as unknown as Promise<Record<string, unknown> & TypeWithID>
}
async function createPostDoc(data: any): Promise<Record<string, unknown> & TypeWithID> {
return payload.create({
collection: 'posts',
data,
}) as unknown as Promise<Record<string, unknown> & TypeWithID>
}
async function createTestDoc(data: any): Promise<Record<string, unknown> & TypeWithID> {
return payload.create({
collection: 'tests',
data,
}) as unknown as Promise<Record<string, unknown> & TypeWithID>
}
async function deleteAllPosts() {
await payload.delete({ collection: postsSlug, where: { id: { exists: true } } })
}