test: field level validation errors (#10614)

Continuation of #10575. Field level validations error were incorrectly
throwing uniqueness errors. This was fixed but lacking tests.
This commit is contained in:
Jacob Fletcher
2025-01-16 15:55:34 -05:00
committed by GitHub
parent e80d67987e
commit 86ff0a434c
7 changed files with 219 additions and 91 deletions

View File

@@ -19,13 +19,12 @@ export const handleError = ({
// Handle uniqueness error from MongoDB // Handle uniqueness error from MongoDB
if ('code' in error && error.code === 11000 && 'keyValue' in error && error.keyValue) { if ('code' in error && error.code === 11000 && 'keyValue' in error && error.keyValue) {
const message = req?.t ? req.t('error:valueMustBeUnique') : 'Value must be unique'
throw new ValidationError( throw new ValidationError(
{ {
collection, collection,
errors: [ errors: [
{ {
message, message: req?.t ? req.t('error:valueMustBeUnique') : 'Value must be unique',
path: Object.keys(error.keyValue)[0], path: Object.keys(error.keyValue)[0],
}, },
], ],

View File

@@ -34,7 +34,7 @@ const collectionWithName = (collectionSlug: string): CollectionConfig => {
} }
} }
export const slug = 'posts' export const postsSlug = 'posts'
export const relationSlug = 'relation' export const relationSlug = 'relation'
export const pointSlug = 'point' export const pointSlug = 'point'
export const customIdSlug = 'custom-id' export const customIdSlug = 'custom-id'
@@ -51,7 +51,7 @@ export default buildConfigWithDefaults({
}, },
collections: [ collections: [
{ {
slug, slug: postsSlug,
access: openAccess, access: openAccess,
fields: [ fields: [
{ {
@@ -346,14 +346,14 @@ export default buildConfigWithDefaults({
// Relation - hasMany // Relation - hasMany
await payload.create({ await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
relationHasManyField: rel1.id, relationHasManyField: rel1.id,
title: 'rel to hasMany', title: 'rel to hasMany',
}, },
}) })
await payload.create({ await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
relationHasManyField: rel2.id, relationHasManyField: rel2.id,
title: 'rel to hasMany 2', title: 'rel to hasMany 2',
@@ -362,7 +362,7 @@ export default buildConfigWithDefaults({
// Relation - relationTo multi // Relation - relationTo multi
await payload.create({ await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
relationMultiRelationTo: { relationMultiRelationTo: {
relationTo: relationSlug, relationTo: relationSlug,
@@ -374,7 +374,7 @@ export default buildConfigWithDefaults({
// Relation - relationTo multi hasMany // Relation - relationTo multi hasMany
await payload.create({ await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
relationMultiRelationToHasMany: [ relationMultiRelationToHasMany: [
{ {

View File

@@ -18,7 +18,7 @@ import {
methods, methods,
pointSlug, pointSlug,
relationSlug, relationSlug,
slug, postsSlug,
} from './config.js' } from './config.js'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
@@ -55,7 +55,7 @@ describe('collections-rest', () => {
it('should find', async () => { it('should find', async () => {
const post1 = await createPost() const post1 = await createPost()
const post2 = await createPost() const post2 = await createPost()
const response = await restClient.GET(`/${slug}`) const response = await restClient.GET(`/${postsSlug}`)
const result = await response.json() const result = await response.json()
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -68,7 +68,7 @@ describe('collections-rest', () => {
it('should count', async () => { it('should count', async () => {
await createPost() await createPost()
await createPost() await createPost()
const response = await restClient.GET(`/${slug}/count`) const response = await restClient.GET(`/${postsSlug}/count`)
const result = await response.json() const result = await response.json()
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -78,7 +78,7 @@ describe('collections-rest', () => {
it('should find where id', async () => { it('should find where id', async () => {
const post1 = await createPost() const post1 = await createPost()
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { id: { equals: post1.id } }, where: { id: { equals: post1.id } },
}, },
@@ -95,7 +95,7 @@ describe('collections-rest', () => {
const post2 = await createPost() const post2 = await createPost()
const { docs, totalDocs } = await payload.find({ const { docs, totalDocs } = await payload.find({
collection: slug, collection: postsSlug,
overrideAccess: false, overrideAccess: false,
pagination: false, pagination: false,
}) })
@@ -111,7 +111,7 @@ describe('collections-rest', () => {
const { id, description } = await createPost({ description: 'desc' }) const { id, description } = await createPost({ description: 'desc' })
const updatedTitle = 'updated-title' const updatedTitle = 'updated-title'
const response = await restClient.PATCH(`/${slug}/${id}`, { const response = await restClient.PATCH(`/${postsSlug}/${id}`, {
body: JSON.stringify({ title: updatedTitle }), body: JSON.stringify({ title: updatedTitle }),
}) })
const { doc } = await response.json() const { doc } = await response.json()
@@ -128,7 +128,7 @@ describe('collections-rest', () => {
} }
const description = 'updated' const description = 'updated'
const response = await restClient.PATCH(`/${slug}`, { const response = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -151,7 +151,7 @@ describe('collections-rest', () => {
} }
const description = 'updated-description' const description = 'updated-description'
const response = await restClient.PATCH(`/${slug}`, { const response = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -167,7 +167,7 @@ describe('collections-rest', () => {
const { docs: resDocs } = await payload.find({ const { docs: resDocs } = await payload.find({
limit: 10, limit: 10,
collection: slug, collection: postsSlug,
where: { id: { in: ids } }, where: { id: { in: ids } },
}) })
expect(resDocs.at(-1).description).toEqual('to-update') expect(resDocs.at(-1).description).toEqual('to-update')
@@ -180,7 +180,7 @@ describe('collections-rest', () => {
const description = 'updated' const description = 'updated'
const response = await restClient.PATCH(`/${slug}`, { const response = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -193,7 +193,7 @@ describe('collections-rest', () => {
expect(errors).toHaveLength(1) expect(errors).toHaveLength(1)
const { docs } = await payload.find({ const { docs } = await payload.find({
collection: slug, collection: postsSlug,
}) })
expect(docs[0].description).not.toEqual(description) expect(docs[0].description).not.toEqual(description)
@@ -206,7 +206,7 @@ describe('collections-rest', () => {
} }
const description = 'updated' const description = 'updated'
const relationFieldResponse = await restClient.PATCH(`/${slug}`, { const relationFieldResponse = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -214,7 +214,7 @@ describe('collections-rest', () => {
}) })
expect(relationFieldResponse.status).toEqual(400) expect(relationFieldResponse.status).toEqual(400)
const relationMultiRelationToResponse = await restClient.PATCH(`/${slug}`, { const relationMultiRelationToResponse = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -223,7 +223,7 @@ describe('collections-rest', () => {
expect(relationMultiRelationToResponse.status).toEqual(400) expect(relationMultiRelationToResponse.status).toEqual(400)
const { docs } = await payload.find({ const { docs } = await payload.find({
collection: slug, collection: postsSlug,
}) })
expect(docs[0].description).not.toEqual(description) expect(docs[0].description).not.toEqual(description)
@@ -232,14 +232,14 @@ describe('collections-rest', () => {
it('should not bulk update with a read restricted field query', async () => { it('should not bulk update with a read restricted field query', async () => {
const { id } = await payload.create({ const { id } = await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
restrictedField: 'restricted', restrictedField: 'restricted',
}, },
}) })
const description = 'description' const description = 'description'
const response = await restClient.PATCH(`/${slug}`, { const response = await restClient.PATCH(`/${postsSlug}`, {
body: JSON.stringify({ body: JSON.stringify({
description, description,
}), }),
@@ -249,7 +249,7 @@ describe('collections-rest', () => {
const doc = await payload.findByID({ const doc = await payload.findByID({
id, id,
collection: slug, collection: postsSlug,
}) })
expect(response.status).toEqual(400) expect(response.status).toEqual(400)
@@ -301,7 +301,7 @@ describe('collections-rest', () => {
await createPost({ description: `desc ${i}` }) await createPost({ description: `desc ${i}` })
} }
const response = await restClient.DELETE(`/${slug}`, { const response = await restClient.DELETE(`/${postsSlug}`, {
query: { where: { title: { equals: 'title' } } }, query: { where: { title: { equals: 'title' } } },
}) })
const { docs } = await response.json() const { docs } = await response.json()
@@ -481,7 +481,7 @@ describe('collections-rest', () => {
it('should delete', async () => { it('should delete', async () => {
const { id } = await createPost() const { id } = await createPost()
const response = await restClient.DELETE(`/${slug}/${id}`) const response = await restClient.DELETE(`/${postsSlug}/${id}`)
const { doc } = await response.json() const { doc } = await response.json()
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -491,7 +491,7 @@ describe('collections-rest', () => {
it('should include metadata', async () => { it('should include metadata', async () => {
await createPosts(11) await createPosts(11)
const result = await restClient.GET(`/${slug}`).then((res) => res.json()) const result = await restClient.GET(`/${postsSlug}`).then((res) => res.json())
expect(result.totalDocs).toBeGreaterThan(0) expect(result.totalDocs).toBeGreaterThan(0)
expect(result.limit).toBe(10) expect(result.limit).toBe(10)
@@ -534,7 +534,7 @@ describe('collections-rest', () => {
describe('regular relationship', () => { describe('regular relationship', () => {
it('query by property value', async () => { it('query by property value', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { equals: relation.id } }, where: { relationField: { equals: relation.id } },
}, },
@@ -547,7 +547,7 @@ describe('collections-rest', () => {
}) })
it('should count query by property value', async () => { it('should count query by property value', async () => {
const response = await restClient.GET(`/${slug}/count`, { const response = await restClient.GET(`/${postsSlug}/count`, {
query: { query: {
where: { relationField: { equals: relation.id } }, where: { relationField: { equals: relation.id } },
}, },
@@ -559,7 +559,7 @@ describe('collections-rest', () => {
}) })
it('query by id', async () => { it('query by id', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { equals: relation.id } }, where: { relationField: { equals: relation.id } },
}, },
@@ -573,13 +573,13 @@ describe('collections-rest', () => {
it('should query LIKE by ID', async () => { it('should query LIKE by ID', async () => {
const post = await payload.create({ const post = await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
title: 'find me buddy', title: 'find me buddy',
}, },
}) })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
id: { id: {
@@ -600,7 +600,7 @@ describe('collections-rest', () => {
relationHasManyField: [relation.id, relation2.id], relationHasManyField: [relation.id, relation2.id],
}) })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { 'relationHasManyField.name': { equals: relation.name } }, where: { 'relationHasManyField.name': { equals: relation.name } },
}, },
@@ -612,7 +612,7 @@ describe('collections-rest', () => {
expect(result.totalDocs).toEqual(1) expect(result.totalDocs).toEqual(1)
// Query second relationship // Query second relationship
const response2 = await restClient.GET(`/${slug}`, { const response2 = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { 'relationHasManyField.name': { equals: relation2.name } }, where: { 'relationHasManyField.name': { equals: relation2.name } },
}, },
@@ -631,7 +631,7 @@ describe('collections-rest', () => {
}) })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { 'relationMultiRelationTo.value': { equals: relation.id } }, where: { 'relationMultiRelationTo.value': { equals: relation.id } },
}, },
@@ -650,7 +650,7 @@ describe('collections-rest', () => {
}) })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
and: [ and: [
@@ -678,7 +678,7 @@ describe('collections-rest', () => {
}) })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { 'relationMultiRelationToHasMany.value': { equals: relation.id } }, where: { 'relationMultiRelationToHasMany.value': { equals: relation.id } },
}, },
@@ -690,7 +690,7 @@ describe('collections-rest', () => {
expect(result.totalDocs).toEqual(1) expect(result.totalDocs).toEqual(1)
// Query second relation // Query second relation
const response2 = await restClient.GET(`/${slug}`, { const response2 = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { 'relationMultiRelationToHasMany.value': { equals: relation.id } }, where: { 'relationMultiRelationToHasMany.value': { equals: relation.id } },
}, },
@@ -711,7 +711,7 @@ describe('collections-rest', () => {
const test = 'test' const test = 'test'
await createPost({ fakeLocalization: test }) await createPost({ fakeLocalization: test })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { fakeLocalization: { equals: test } }, where: { fakeLocalization: { equals: test } },
}, },
@@ -723,7 +723,7 @@ describe('collections-rest', () => {
}) })
it('should not error when attempting to sort on a field that does not exist', async () => { it('should not error when attempting to sort on a field that does not exist', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
sort: 'fake', sort: 'fake',
}, },
@@ -738,7 +738,7 @@ describe('collections-rest', () => {
const valueToQuery = 'valueToQuery' const valueToQuery = 'valueToQuery'
const post1 = await createPost({ title: valueToQuery }) const post1 = await createPost({ title: valueToQuery })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { title: { equals: valueToQuery } }, where: { title: { equals: valueToQuery } },
}, },
@@ -754,7 +754,7 @@ describe('collections-rest', () => {
const post1 = await createPost({ title: 'not-equals' }) const post1 = await createPost({ title: 'not-equals' })
const post2 = await createPost() const post2 = await createPost()
const post3 = await createPost({ title: undefined }) const post3 = await createPost({ title: undefined })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { title: { not_equals: post1.title } }, where: { title: { not_equals: post1.title } },
}, },
@@ -769,7 +769,7 @@ describe('collections-rest', () => {
it('in', async () => { it('in', async () => {
const post1 = await createPost({ title: 'my-title' }) const post1 = await createPost({ title: 'my-title' })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { title: { in: [post1.title] } }, where: { title: { in: [post1.title] } },
}, },
@@ -784,7 +784,7 @@ describe('collections-rest', () => {
it('not_in', async () => { it('not_in', async () => {
const post1 = await createPost({ title: 'not-me' }) const post1 = await createPost({ title: 'not-me' })
const post2 = await createPost() const post2 = await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { title: { not_in: [post1.title] } }, where: { title: { not_in: [post1.title] } },
}, },
@@ -805,7 +805,7 @@ describe('collections-rest', () => {
await createPost({ relationField: relationship.id, title: 'not-me' }) await createPost({ relationField: relationship.id, title: 'not-me' })
// await createPost({ relationMultiRelationTo: relationship.id, title: 'not-me' }) // await createPost({ relationMultiRelationTo: relationship.id, title: 'not-me' })
const post2 = await createPost({ title: 'me' }) const post2 = await createPost({ title: 'me' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { not_in: [relationship.id] } }, where: { relationField: { not_in: [relationship.id] } },
}, },
@@ -817,7 +817,7 @@ describe('collections-rest', () => {
expect(result.totalDocs).toEqual(1) expect(result.totalDocs).toEqual(1)
// do not want to error for empty arrays // do not want to error for empty arrays
const emptyNotInResponse = await restClient.GET(`/${slug}`, { const emptyNotInResponse = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { not_in: [] } }, where: { relationField: { not_in: [] } },
}, },
@@ -835,7 +835,7 @@ describe('collections-rest', () => {
const post1 = await createPost({ relationField: relationship.id, title: 'me' }) const post1 = await createPost({ relationField: relationship.id, title: 'me' })
// await createPost({ relationMultiRelationTo: relationship.id, title: 'not-me' }) // await createPost({ relationMultiRelationTo: relationship.id, title: 'not-me' })
await createPost({ title: 'not-me' }) await createPost({ title: 'not-me' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { in: [relationship.id] } }, where: { relationField: { in: [relationship.id] } },
}, },
@@ -847,7 +847,7 @@ describe('collections-rest', () => {
expect(result.totalDocs).toEqual(1) expect(result.totalDocs).toEqual(1)
// do not want to error for empty arrays // do not want to error for empty arrays
const emptyNotInResponse = await restClient.GET(`/${slug}`, { const emptyNotInResponse = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { relationField: { in: [] } }, where: { relationField: { in: [] } },
}, },
@@ -859,7 +859,7 @@ describe('collections-rest', () => {
it('like', async () => { it('like', async () => {
const post1 = await createPost({ title: 'prefix-value' }) const post1 = await createPost({ title: 'prefix-value' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { title: { like: 'prefix' } }, where: { title: { like: 'prefix' } },
}, },
@@ -881,7 +881,7 @@ describe('collections-rest', () => {
title: specialCharacters, title: specialCharacters,
}) })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
title: { title: {
@@ -902,7 +902,7 @@ describe('collections-rest', () => {
it('like - cyrillic characters', async () => { it('like - cyrillic characters', async () => {
const post1 = await createPost({ title: 'Тест' }) const post1 = await createPost({ title: 'Тест' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
title: { title: {
@@ -921,7 +921,7 @@ describe('collections-rest', () => {
it('like - cyrillic characters in multiple words', async () => { it('like - cyrillic characters in multiple words', async () => {
const post1 = await createPost({ title: 'привет, это тест полезной нагрузки' }) const post1 = await createPost({ title: 'привет, это тест полезной нагрузки' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
title: { title: {
@@ -939,7 +939,7 @@ describe('collections-rest', () => {
it('like - partial word match', async () => { it('like - partial word match', async () => {
const post = await createPost({ title: 'separate words should partially match' }) const post = await createPost({ title: 'separate words should partially match' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
title: { title: {
@@ -958,7 +958,7 @@ describe('collections-rest', () => {
it('like - id should not crash', async () => { it('like - id should not crash', async () => {
const post = await createPost({ title: 'post' }) const post = await createPost({ title: 'post' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
id: { id: {
@@ -974,7 +974,7 @@ describe('collections-rest', () => {
it('exists - true', async () => { it('exists - true', async () => {
const postWithDesc = await createPost({ description: 'exists' }) const postWithDesc = await createPost({ description: 'exists' })
await createPost({ description: undefined }) await createPost({ description: undefined })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
description: { description: {
@@ -993,7 +993,7 @@ describe('collections-rest', () => {
it('exists - false', async () => { it('exists - false', async () => {
const postWithoutDesc = await createPost({ description: undefined }) const postWithoutDesc = await createPost({ description: undefined })
await createPost({ description: 'exists' }) await createPost({ description: 'exists' })
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
description: { description: {
@@ -1018,7 +1018,7 @@ describe('collections-rest', () => {
}) })
it('greater_than', async () => { it('greater_than', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
number: { number: {
@@ -1035,7 +1035,7 @@ describe('collections-rest', () => {
}) })
it('greater_than_equal', async () => { it('greater_than_equal', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
number: { number: {
@@ -1054,7 +1054,7 @@ describe('collections-rest', () => {
}) })
it('less_than', async () => { it('less_than', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
number: { number: {
@@ -1071,7 +1071,7 @@ describe('collections-rest', () => {
}) })
it('less_than_equal', async () => { it('less_than_equal', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
number: { number: {
@@ -1312,7 +1312,7 @@ describe('collections-rest', () => {
const post2 = await createPost({ title: 'post2' }) const post2 = await createPost({ title: 'post2' })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
or: [ or: [
@@ -1342,7 +1342,7 @@ describe('collections-rest', () => {
const post1 = await createPost({ title: 'post1' }) const post1 = await createPost({ title: 'post1' })
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
or: [ or: [
@@ -1374,7 +1374,7 @@ describe('collections-rest', () => {
await createPost({ description, title: 'post2' }) // Diff title, same desc await createPost({ description, title: 'post2' }) // Diff title, same desc
await createPost() await createPost()
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
and: [ and: [
@@ -1427,13 +1427,13 @@ describe('collections-rest', () => {
}, },
}, },
} }
let response = await restClient.GET(`/${slug}`, { query }) let response = await restClient.GET(`/${postsSlug}`, { query })
const page1 = await response.json() const page1 = await response.json()
response = await restClient.GET(`/${slug}`, { query: { ...query, page: 2 } }) response = await restClient.GET(`/${postsSlug}`, { query: { ...query, page: 2 } })
const page2 = await response.json() const page2 = await response.json()
response = await restClient.GET(`/${slug}`, { query: { ...query, page: 3 } }) response = await restClient.GET(`/${postsSlug}`, { query: { ...query, page: 3 } })
const page3 = await response.json() const page3 = await response.json()
expect(page1.hasNextPage).toStrictEqual(true) expect(page1.hasNextPage).toStrictEqual(true)
@@ -1476,13 +1476,13 @@ describe('collections-rest', () => {
}, },
}, },
} }
let response = await restClient.GET(`/${slug}`, { query }) let response = await restClient.GET(`/${postsSlug}`, { query })
const page1 = await response.json() const page1 = await response.json()
response = await restClient.GET(`/${slug}`, { query: { ...query, page: 2 } }) response = await restClient.GET(`/${postsSlug}`, { query: { ...query, page: 2 } })
const page2 = await response.json() const page2 = await response.json()
response = await restClient.GET(`/${slug}`, { query: { ...query, page: 3 } }) response = await restClient.GET(`/${postsSlug}`, { query: { ...query, page: 3 } })
const page3 = await response.json() const page3 = await response.json()
expect(page1.hasNextPage).toStrictEqual(true) expect(page1.hasNextPage).toStrictEqual(true)
@@ -1524,7 +1524,7 @@ describe('collections-rest', () => {
}) })
it('should query a limited set of docs', async () => { it('should query a limited set of docs', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
limit: 15, limit: 15,
where: { where: {
@@ -1541,7 +1541,7 @@ describe('collections-rest', () => {
}) })
it('should query all docs when limit=0', async () => { it('should query all docs when limit=0', async () => {
const response = await restClient.GET(`/${slug}`, { const response = await restClient.GET(`/${postsSlug}`, {
query: { query: {
limit: 0, limit: 0,
where: { where: {
@@ -1566,7 +1566,7 @@ describe('collections-rest', () => {
}) })
const result = await restClient const result = await restClient
.GET(`/${slug}`, { .GET(`/${postsSlug}`, {
query: { query: {
where: { where: {
'D1.D2.D3.D4': { 'D1.D2.D3.D4': {
@@ -1641,11 +1641,11 @@ describe('collections-rest', () => {
const post = await createPost({}) const post = await createPost({})
const response = await restClient.GET( const response = await restClient.GET(
`/${slug}/${typeof post.id === 'number' ? 1000 : randomUUID()}`, `/${postsSlug}/${typeof post.id === 'number' ? 1000 : randomUUID()}`,
) )
expect(response.status).toBe(404) expect(response.status).toBe(404)
expect(collection.slug).toBe(slug) expect(collection.slug).toBe(postsSlug)
expect(err).toBeInstanceOf(NotFound) expect(err).toBeInstanceOf(NotFound)
expect(errResult).toStrictEqual({ expect(errResult).toStrictEqual({
errors: [ errors: [
@@ -1695,7 +1695,7 @@ describe('collections-rest', () => {
async function createPost(overrides?: Partial<Post>) { async function createPost(overrides?: Partial<Post>) {
const { doc } = await restClient const { doc } = await restClient
.POST(`/${slug}`, { .POST(`/${postsSlug}`, {
body: JSON.stringify({ title: 'title', ...overrides }), body: JSON.stringify({ title: 'title', ...overrides }),
}) })
.then((res) => res.json()) .then((res) => res.json())
@@ -1710,7 +1710,7 @@ async function createPosts(count: number) {
async function clearDocs(): Promise<void> { async function clearDocs(): Promise<void> {
await payload.delete({ await payload.delete({
collection: slug, collection: postsSlug,
where: { id: { exists: true } }, where: { id: { exists: true } },
}) })
} }

View File

@@ -8,6 +8,7 @@ import { v4 as uuid } from 'uuid'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js' import { devUser } from '../credentials.js'
import { postsSlug } from './shared.js'
const defaultValueField: TextField = { const defaultValueField: TextField = {
name: 'defaultValue', name: 'defaultValue',
@@ -23,13 +24,72 @@ export default buildConfigWithDefaults({
}, },
collections: [ collections: [
{ {
slug: 'posts', slug: postsSlug,
fields: [ fields: [
{ {
name: 'title', name: 'title',
type: 'text', type: 'text',
required: true, required: true,
}, },
{
type: 'tabs',
tabs: [
{
name: 'D1',
fields: [
{
name: 'D2',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
type: 'collapsible',
fields: [
{
type: 'tabs',
tabs: [
{
fields: [
{
name: 'D3',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
type: 'collapsible',
fields: [
{
name: 'D4',
type: 'text',
},
],
label: 'Collapsible2',
},
],
},
],
},
],
label: 'Tab1',
},
],
},
],
label: 'Collapsible2',
},
],
},
],
},
],
label: 'Tab1',
},
],
},
{ {
name: 'hasTransaction', name: 'hasTransaction',
type: 'checkbox', type: 'checkbox',

View File

@@ -26,6 +26,7 @@ import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js' import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { isMongoose } from '../helpers/isMongoose.js' import { isMongoose } from '../helpers/isMongoose.js'
import removeFiles from '../helpers/removeFiles.js' import removeFiles from '../helpers/removeFiles.js'
import { postsSlug } from './shared.js'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
@@ -34,7 +35,7 @@ let payload: Payload
let user: Record<string, unknown> & TypeWithID let user: Record<string, unknown> & TypeWithID
let token: string let token: string
let restClient: NextRESTClient let restClient: NextRESTClient
const collection = 'posts' const collection = postsSlug
const title = 'title' const title = 'title'
process.env.PAYLOAD_CONFIG_PATH = path.join(dirname, 'config.ts') process.env.PAYLOAD_CONFIG_PATH = path.join(dirname, 'config.ts')
@@ -142,7 +143,7 @@ describe('database', () => {
const blockID = '6764de9af79a863575c5f58c' const blockID = '6764de9af79a863575c5f58c'
const doc = await payload.create({ const doc = await payload.create({
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'test', title: 'test',
arrayWithIDs: [ arrayWithIDs: [
@@ -168,7 +169,7 @@ describe('database', () => {
const blockID = '6764dec58c68f337a758180c' const blockID = '6764dec58c68f337a758180c'
const doc = await payload.create({ const doc = await payload.create({
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'test', title: 'test',
arrayWithIDs: [ arrayWithIDs: [
@@ -186,7 +187,7 @@ describe('database', () => {
}) })
const duplicate = await payload.duplicate({ const duplicate = await payload.duplicate({
collection: 'posts', collection: postsSlug,
id: doc.id, id: doc.id,
}) })
@@ -198,7 +199,7 @@ describe('database', () => {
describe('timestamps', () => { describe('timestamps', () => {
it('should have createdAt and updatedAt timestamps to the millisecond', async () => { it('should have createdAt and updatedAt timestamps to the millisecond', async () => {
const result = await payload.create({ const result = await payload.create({
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'hello', title: 'hello',
}, },
@@ -212,7 +213,7 @@ describe('database', () => {
it('should allow createdAt to be set in create', async () => { it('should allow createdAt to be set in create', async () => {
const createdAt = new Date('2021-01-01T00:00:00.000Z').toISOString() const createdAt = new Date('2021-01-01T00:00:00.000Z').toISOString()
const result = await payload.create({ const result = await payload.create({
collection: 'posts', collection: postsSlug,
data: { data: {
createdAt, createdAt,
title: 'hello', title: 'hello',
@@ -221,7 +222,7 @@ describe('database', () => {
const doc = await payload.findByID({ const doc = await payload.findByID({
id: result.id, id: result.id,
collection: 'posts', collection: postsSlug,
}) })
expect(result.createdAt).toStrictEqual(createdAt) expect(result.createdAt).toStrictEqual(createdAt)
@@ -231,7 +232,7 @@ describe('database', () => {
it('updatedAt cannot be set in create', async () => { it('updatedAt cannot be set in create', async () => {
const updatedAt = new Date('2022-01-01T00:00:00.000Z').toISOString() const updatedAt = new Date('2022-01-01T00:00:00.000Z').toISOString()
const result = await payload.create({ const result = await payload.create({
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'hello', title: 'hello',
updatedAt, updatedAt,
@@ -875,6 +876,53 @@ describe('database', () => {
}) })
}) })
describe('Error Handler', () => {
it('should return proper top-level field validation errors', async () => {
let errorMessage: string = ''
try {
await payload.create({
collection: postsSlug,
data: {
// @ts-expect-error
title: undefined,
},
})
} catch (e: any) {
errorMessage = e.message
}
await expect(errorMessage).toBe('The following field is invalid: title')
})
it('should return proper deeply nested field validation errors', async () => {
try {
await payload.create({
collection: postsSlug,
data: {
title: 'Title',
D1: {
D2: {
D3: {
// @ts-expect-error
D4: {},
},
},
},
},
})
} catch (e: any) {
await expect(e.message).toMatch(
payload.db.name === 'mongoose'
? 'posts validation failed: D1.D2.D3.D4: Cast to string failed for value "{}" (type Object) at path "D4"'
: payload.db.name === 'sqlite'
? 'SQLite3 can only bind numbers, strings, bigints, buffers, and null'
: '',
)
}
})
})
describe('defaultValue', () => { describe('defaultValue', () => {
it('should set default value from db.create', async () => { it('should set default value from db.create', async () => {
// call the db adapter create directly to bypass Payload's default value assignment // call the db adapter create directly to bypass Payload's default value assignment
@@ -1257,7 +1305,7 @@ describe('database', () => {
it('should upsert', async () => { it('should upsert', async () => {
const postShouldCreated = await payload.db.upsert({ const postShouldCreated = await payload.db.upsert({
req: {}, req: {},
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'some-title-here', title: 'some-title-here',
}, },
@@ -1272,7 +1320,7 @@ describe('database', () => {
const postShouldUpdated = await payload.db.upsert({ const postShouldUpdated = await payload.db.upsert({
req: {}, req: {},
collection: 'posts', collection: postsSlug,
data: { data: {
title: 'some-title-here', title: 'some-title-here',
}, },
@@ -1288,9 +1336,9 @@ describe('database', () => {
}) })
it('should enforce unique ids on db level even after delete', async () => { it('should enforce unique ids on db level even after delete', async () => {
const { id } = await payload.create({ collection: 'posts', data: { title: 'ASD' } }) const { id } = await payload.create({ collection: postsSlug, data: { title: 'ASD' } })
await payload.delete({ id, collection: 'posts' }) await payload.delete({ id, collection: postsSlug })
const { id: id_2 } = await payload.create({ collection: 'posts', data: { title: 'ASD' } }) const { id: id_2 } = await payload.create({ collection: postsSlug, data: { title: 'ASD' } })
expect(id_2).not.toBe(id) expect(id_2).not.toBe(id)
}) })
}) })

View File

@@ -88,6 +88,13 @@ export interface UserAuthOperations {
export interface Post { export interface Post {
id: string; id: string;
title: string; title: string;
D1?: {
D2?: {
D3?: {
D4?: string | null;
};
};
};
hasTransaction?: boolean | null; hasTransaction?: boolean | null;
throwAfterChange?: boolean | null; throwAfterChange?: boolean | null;
arrayWithIDs?: arrayWithIDs?:
@@ -440,6 +447,19 @@ export interface PayloadMigration {
*/ */
export interface PostsSelect<T extends boolean = true> { export interface PostsSelect<T extends boolean = true> {
title?: T; title?: T;
D1?:
| T
| {
D2?:
| T
| {
D3?:
| T
| {
D4?: T;
};
};
};
hasTransaction?: T; hasTransaction?: T;
throwAfterChange?: T; throwAfterChange?: T;
arrayWithIDs?: arrayWithIDs?:

1
test/database/shared.ts Normal file
View File

@@ -0,0 +1 @@
export const postsSlug = 'posts'