Files
payloadcms/test/joins/int.spec.ts
Alessio Gravili e6fea1d132 fix: localized fields within block references were not handled properly if any parent is localized (#11207)
The `localized` properly was not stripped out of referenced block fields, if any parent was localized. For normal fields, this is done in sanitizeConfig. As the same referenced block config can be used in both a localized and non-localized config, we are not able to strip it out inside sanitizeConfig by modifying the block config.

Instead, this PR had to bring back tedious logic to handle it everywhere the `field.localized` property is accessed. For backwards-compatibility, we need to keep the existing sanitizeConfig logic. In 4.0, we should remove it to benefit from better test coverage of runtime field.localized handling - for now, this is done for our test suite using the `PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY` flag.
2025-02-17 19:50:32 +00:00

1040 lines
30 KiB
TypeScript

import type { Payload, TypeWithID } from 'payload'
import path from 'path'
import { getFileByPath } from 'payload'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type { Category, Config, DepthJoins1, DepthJoins3, Post, Singular } from './payload-types.js'
import { devUser } from '../credentials.js'
import { idToString } from '../helpers/idToString.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import {
categoriesJoinRestrictedSlug,
categoriesSlug,
postsSlug,
restrictedCategoriesSlug,
restrictedPostsSlug,
uploadsSlug,
} from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
let payload: Payload
let token: string
let restClient: NextRESTClient
const { email, password } = devUser
describe('Joins Field', () => {
let category: Category
let otherCategory: Category
let categoryID
let user
// --__--__--__--__--__--__--__--__--__
// Boilerplate test setup/teardown
// --__--__--__--__--__--__--__--__--__
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(dirname))
const data = await restClient
.POST('/users/login', {
body: JSON.stringify({
email,
password,
}),
})
.then((res) => res.json())
token = data.token
user = data.user
category = await payload.create({
collection: categoriesSlug,
data: {
name: 'paginate example',
group: {},
},
})
otherCategory = await payload.create({
collection: categoriesSlug,
data: {
name: 'otherCategory',
group: {},
},
})
// create an upload
const imageFilePath = path.resolve(dirname, './image.png')
const imageFile = await getFileByPath(imageFilePath)
const { id: uploadedImage } = await payload.create({
collection: uploadsSlug,
data: {},
file: imageFile,
})
categoryID = idToString(category.id, payload)
for (let i = 0; i < 15; i++) {
let categories = [category.id]
if (i % 2 === 0) {
categories = [category.id, otherCategory.id]
}
await createPost({
title: `test ${i}`,
category: category.id,
upload: uploadedImage,
categories,
categoriesLocalized: categories,
polymorphic: {
relationTo: 'categories',
value: category.id,
},
polymorphics: [
{
relationTo: 'categories',
value: category.id,
},
],
localizedPolymorphic: {
relationTo: 'categories',
value: category.id,
},
localizedPolymorphics: [
{
relationTo: 'categories',
value: category.id,
},
],
group: {
category: category.id,
camelCaseCategory: category.id,
},
array: [{ category: category.id }],
localizedArray: [{ category: category.id }],
blocks: [{ blockType: 'block', category: category.id }],
})
}
})
afterAll(async () => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
})
it('should populate joins using findByID', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
'group.relatedPosts': {
sort: '-title',
},
},
collection: categoriesSlug,
})
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.group.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should not populate joins if not selected', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
'group.relatedPosts': {
sort: '-title',
},
},
select: {},
collection: categoriesSlug,
})
expect(Object.keys(categoryWithPosts)).toStrictEqual(['id'])
})
it('should populate joins if selected', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
'group.relatedPosts': {
sort: '-title',
},
},
select: {
group: {
relatedPosts: true,
},
},
collection: categoriesSlug,
})
expect(Object.keys(categoryWithPosts)).toStrictEqual(['id', 'group'])
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.group.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should populate relationships in joins', async () => {
const { docs } = await payload.find({
limit: 1,
collection: postsSlug,
depth: 2,
})
expect(docs[0].category.id).toBeDefined()
expect(docs[0].category.name).toBeDefined()
expect(docs[0].category.relatedPosts.docs).toHaveLength(5) // uses defaultLimit
})
it('should populate relationships in joins with camelCase names', async () => {
const { docs } = await payload.find({
limit: 1,
collection: postsSlug,
})
expect(docs[0].group.camelCaseCategory.id).toBeDefined()
expect(docs[0].group.camelCaseCategory.name).toBeDefined()
expect(docs[0].group.camelCaseCategory.group.camelCasePosts.docs).toHaveLength(10)
})
it('should populate joins with array relationships', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
collection: categoriesSlug,
})
expect(categoryWithPosts.arrayPosts.docs).toBeDefined()
expect(categoryWithPosts.arrayPosts.docs).toHaveLength(10)
})
it('should populate joins with localized array relationships', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
collection: categoriesSlug,
})
expect(categoryWithPosts.localizedArrayPosts.docs).toBeDefined()
expect(categoryWithPosts.localizedArrayPosts.docs).toHaveLength(10)
})
it('should populate joins with blocks relationships', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
collection: categoriesSlug,
})
expect(categoryWithPosts.blocksPosts.docs).toBeDefined()
})
it('should populate uploads in joins', async () => {
const { docs } = await payload.find({
limit: 1,
collection: postsSlug,
})
expect(docs[0].upload.id).toBeDefined()
expect(docs[0].upload.relatedPosts.docs).toHaveLength(10)
})
it('should join on polymorphic relationships', async () => {
const categoryWithPosts = await payload.findByID({
collection: categoriesSlug,
id: category.id,
})
expect(categoryWithPosts.polymorphic.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.polymorphics.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.localizedPolymorphic.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.localizedPolymorphics.docs[0]).toHaveProperty('id')
})
it('should filter joins using where query', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
relatedPosts: {
sort: '-title',
where: {
title: {
equals: 'test 9',
},
},
},
},
collection: categoriesSlug,
})
expect(categoryWithPosts.relatedPosts.docs).toHaveLength(1)
expect(categoryWithPosts.relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should populate joins using find', async () => {
const result = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: category.id },
},
})
const [categoryWithPosts] = result.docs
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.group.relatedPosts.docs[0].title).toBe('test 14')
})
it('should populate joins using find with hasMany relationships', async () => {
const result = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: category.id },
},
})
const otherResult = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: otherCategory.id },
},
})
const [categoryWithPosts] = result.docs
const [otherCategoryWithPosts] = otherResult.docs
expect(categoryWithPosts.hasManyPosts.docs).toHaveLength(10)
expect(categoryWithPosts.hasManyPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.hasManyPosts.docs[0].title).toBe('test 14')
expect(otherCategoryWithPosts.hasManyPosts.docs).toHaveLength(8)
expect(otherCategoryWithPosts.hasManyPosts.docs[0]).toHaveProperty('title')
expect(otherCategoryWithPosts.hasManyPosts.docs[0].title).toBe('test 14')
})
it('should populate joins using find with hasMany localized relationships', async () => {
const post_1 = await createPost(
{
title: `test es localized 1`,
categoriesLocalized: [category.id],
group: {
category: category.id,
camelCaseCategory: category.id,
},
},
'es',
)
const post_2 = await createPost(
{
title: `test es localized 2`,
categoriesLocalized: [otherCategory.id],
group: {
category: category.id,
camelCaseCategory: category.id,
},
},
'es',
)
const resultEn = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: category.id },
},
})
const otherResultEn = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: otherCategory.id },
},
})
const [categoryWithPostsEn] = resultEn.docs
const [otherCategoryWithPostsEn] = otherResultEn.docs
expect(categoryWithPostsEn.hasManyPostsLocalized.docs).toHaveLength(10)
expect(categoryWithPostsEn.hasManyPostsLocalized.docs[0]).toHaveProperty('title')
expect(categoryWithPostsEn.hasManyPostsLocalized.docs[0].title).toBe('test 14')
expect(otherCategoryWithPostsEn.hasManyPostsLocalized.docs).toHaveLength(8)
expect(otherCategoryWithPostsEn.hasManyPostsLocalized.docs[0]).toHaveProperty('title')
expect(otherCategoryWithPostsEn.hasManyPostsLocalized.docs[0].title).toBe('test 14')
const resultEs = await payload.find({
collection: categoriesSlug,
locale: 'es',
where: {
id: { equals: category.id },
},
})
const otherResultEs = await payload.find({
collection: categoriesSlug,
locale: 'es',
where: {
id: { equals: otherCategory.id },
},
})
const [categoryWithPostsEs] = resultEs.docs
const [otherCategoryWithPostsEs] = otherResultEs.docs
expect(categoryWithPostsEs.hasManyPostsLocalized.docs).toHaveLength(1)
expect(categoryWithPostsEs.hasManyPostsLocalized.docs[0].title).toBe('test es localized 1')
expect(otherCategoryWithPostsEs.hasManyPostsLocalized.docs).toHaveLength(1)
expect(otherCategoryWithPostsEs.hasManyPostsLocalized.docs[0].title).toBe('test es localized 2')
// clean up
await payload.delete({
collection: postsSlug,
where: {
id: {
in: [post_1.id, post_2.id],
},
},
})
})
it('should not error when deleting documents with joins', async () => {
const category = await payload.create({
collection: categoriesSlug,
data: {
name: 'category with post',
},
})
await createPost({
category: category.id,
})
const result = await payload.delete({
collection: categoriesSlug,
// id: category.id,
where: {
id: { equals: category.id },
},
})
expect(result.docs[0].id).toStrictEqual(category.id)
})
describe('`where` filters', () => {
let categoryWithFilteredPost
beforeAll(async () => {
categoryWithFilteredPost = await payload.create({
collection: categoriesSlug,
data: {
name: 'category with filtered post',
},
})
await createPost({
title: 'filtered post',
category: categoryWithFilteredPost.id,
isFiltered: true,
})
await createPost({
title: 'unfiltered post',
category: categoryWithFilteredPost.id,
isFiltered: false,
})
categoryWithFilteredPost = await payload.findByID({
id: categoryWithFilteredPost.id,
collection: categoriesSlug,
})
})
it('should filter joins using where from field config', () => {
expect(categoryWithFilteredPost.filtered.docs).toHaveLength(1)
})
it('should filter joins using where from field config and the requested filter', async () => {
categoryWithFilteredPost = await payload.findByID({
id: categoryWithFilteredPost.id,
collection: categoriesSlug,
joins: {
filtered: {
where: {
title: { not_equals: 'unfiltered post' },
},
},
},
})
expect(categoryWithFilteredPost.filtered.docs).toHaveLength(0)
})
})
describe('Joins with localization', () => {
let localizedCategory: Category
beforeAll(async () => {
localizedCategory = await payload.create({
collection: 'localized-categories',
locale: 'en',
data: {
name: 'localized category',
},
})
const post1 = await payload.create({
collection: 'localized-posts',
locale: 'en',
data: {
title: 'english post 1',
category: localizedCategory.id,
},
})
await payload.update({
collection: 'localized-posts',
id: post1.id,
locale: 'es',
data: {
title: 'spanish post',
category: localizedCategory.id,
},
})
await payload.create({
collection: 'localized-posts',
locale: 'en',
data: {
title: 'english post 2',
category: localizedCategory.id,
},
})
})
it('should populate joins using findByID with localization on the relationship', async () => {
const enCategory = await payload.findByID({
id: localizedCategory.id,
collection: 'localized-categories',
locale: 'en',
})
const esCategory = await payload.findByID({
id: localizedCategory.id,
collection: 'localized-categories',
locale: 'es',
})
expect(enCategory.relatedPosts.docs).toHaveLength(2)
expect(esCategory.relatedPosts.docs).toHaveLength(1)
})
})
describe('Joins with versions', () => {
afterEach(async () => {
await payload.delete({ collection: 'versions', where: {} })
await payload.delete({ collection: 'categories-versions', where: {} })
})
it('should populate joins when versions on both sides draft false', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersion: category.id },
})
const res = await payload.find({ collection: 'categories-versions', draft: false })
expect(res.docs[0].relatedVersions.docs[0].id).toBe(version.id)
})
it('should populate joins with hasMany relationships when versions on both sides draft false', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersions: [category.id] },
})
const res = await payload.find({ collection: 'categories-versions', draft: false })
expect(res.docs[0].relatedVersionsMany.docs[0].id).toBe(version.id)
})
it('should populate joins with hasMany relationships when versions on both sides draft true payload.db.queryDrafts', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersion: category.id },
})
const res = await payload.find({
collection: 'categories-versions',
draft: true,
})
expect(res.docs[0].relatedVersions.docs[0].id).toBe(version.id)
})
it('should populate joins when versions on both sides draft true payload.db.queryDrafts', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersions: [category.id] },
})
const res = await payload.find({
collection: 'categories-versions',
draft: true,
})
expect(res.docs[0].relatedVersionsMany.docs[0].id).toBe(version.id)
})
})
describe('REST', () => {
it('should have simple paginate for joins', async () => {
const query = {
depth: 1,
where: {
name: { equals: 'paginate example' },
},
joins: {
relatedPosts: {
sort: 'createdAt',
limit: 4,
},
},
}
const pageWithLimit = await restClient.GET(`/categories`, { query }).then((res) => res.json())
query.joins.relatedPosts.limit = 0
const unlimited = await restClient.GET(`/categories`, { query }).then((res) => res.json())
expect(pageWithLimit.docs[0].relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(pageWithLimit.docs[0].relatedPosts.hasNextPage).toStrictEqual(true)
expect(unlimited.docs[0].relatedPosts.docs).toHaveLength(15)
expect(unlimited.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(unlimited.docs[0].relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should respect access control for join collections', async () => {
const { docs } = await payload.find({
collection: categoriesJoinRestrictedSlug,
where: {
name: { equals: 'categoryJoinRestricted' },
},
overrideAccess: false,
user,
})
const [categoryWithRestrictedPosts] = docs
expect(categoryWithRestrictedPosts.collectionRestrictedJoin.docs).toHaveLength(1)
expect(categoryWithRestrictedPosts.collectionRestrictedJoin.docs[0].title).toStrictEqual(
'should allow read',
)
})
it('should respect access control for join request `where` queries', async () => {
await expect(async () => {
await payload.findByID({
id: category.id,
collection: categoriesSlug,
overrideAccess: false,
user,
joins: {
relatedPosts: {
where: {
restrictedField: { equals: 'restricted' },
},
},
},
})
}).rejects.toThrow('The following path cannot be queried: restrictedField')
})
it('should respect access control of join field configured `where` queries', async () => {
const restrictedCategory = await payload.create({
collection: restrictedCategoriesSlug,
data: {
name: 'restricted category',
},
})
await createPost({
collection: restrictedPostsSlug,
data: {
title: 'restricted post',
category: restrictedCategory.id,
},
})
await expect(async () => {
await payload.findByID({
id: category.id,
collection: restrictedCategoriesSlug,
overrideAccess: false,
user,
})
}).rejects.toThrow('The following path cannot be queried: restrictedField')
})
it('should sort joins', async () => {
const response = await restClient
.GET(`/categories/${category.id}?joins[relatedPosts][sort]=-title`)
.then((res) => res.json())
expect(response.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should query in on collections with joins', async () => {
const response = await restClient
.GET(`/categories?where[id][in]=${category.id}`)
.then((res) => res.json())
expect(response.docs[0].name).toStrictEqual(category.name)
})
})
describe('GraphQL', () => {
it('should have simple paginate for joins', async () => {
const queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
relatedPosts(
sort: "createdAt",
limit: 4
) {
docs {
title
}
hasNextPage
}
}
}
}`
const pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
const queryUnlimited = `query {
Categories(
where: {
name: { equals: "paginate example" }
}
) {
docs {
relatedPosts(
sort: "createdAt",
limit: 0
) {
docs {
title
createdAt
}
hasNextPage
}
}
}
}`
const unlimited = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryUnlimited }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.hasNextPage).toStrictEqual(true)
expect(unlimited.data.Categories.docs[0].relatedPosts.docs).toHaveLength(15)
expect(unlimited.data.Categories.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(unlimited.data.Categories.docs[0].relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should have simple paginate for joins inside groups', async () => {
const queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
group {
relatedPosts(
sort: "createdAt",
limit: 4
) {
docs {
title
}
hasNextPage
}
}
}
}
}`
const pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
const queryUnlimited = `query {
Categories(
where: {
name: { equals: "paginate example" }
}
) {
docs {
group {
relatedPosts(
sort: "createdAt",
limit: 0
) {
docs {
title
}
hasNextPage
}
}
}
}
}`
const unlimited = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryUnlimited }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.hasNextPage).toStrictEqual(
true,
)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.docs).toHaveLength(15)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should sort joins', async () => {
const query = `query {
Category(id: ${categoryID}) {
relatedPosts(
sort: "-title"
) {
docs {
title
}
}
}
}`
const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json())
expect(response.data.Category.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should query in on collections with joins', async () => {
const query = `query {
Category(id: ${categoryID}) {
relatedPosts(
where: {
title: {
equals: "test 3"
}
}
) {
docs {
title
}
}
}
}`
const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json())
expect(response.data.Category.relatedPosts.docs[0].title).toStrictEqual('test 3')
})
it('should respect access control for join collections', async () => {
const query = `query {
CategoriesJoinRestricteds {
docs {
name
collectionRestrictedJoin {
docs {
title
canRead
}
}
}
}
}`
const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json())
const [categoryWithRestrictedPosts] = response.data.CategoriesJoinRestricteds.docs
expect(categoryWithRestrictedPosts.collectionRestrictedJoin.docs).toHaveLength(1)
expect(categoryWithRestrictedPosts.collectionRestrictedJoin.docs[0].title).toStrictEqual(
'should allow read',
)
})
})
it('should work id.in command delimited querying with joins', async () => {
const allCategories = await payload.find({ collection: categoriesSlug, pagination: false })
const allCategoriesByIds = await restClient
.GET(`/categories`, {
query: {
where: {
id: {
in: allCategories.docs.map((each) => each.id).join(','),
},
},
},
})
.then((res) => res.json())
expect(allCategories.totalDocs).toBe(allCategoriesByIds.totalDocs)
})
it('should join with singular collection name', async () => {
const {
docs: [category],
} = await payload.find({ collection: categoriesSlug, limit: 1, depth: 0 })
const singular = await payload.create({
collection: 'singular',
data: { category: category.id },
})
const categoryWithJoins = await payload.findByID({
collection: categoriesSlug,
id: category.id,
})
expect((categoryWithJoins.singulars.docs[0] as Singular).id).toBe(singular.id)
})
it('local API should not populate individual join by providing schemaPath=false', async () => {
const {
docs: [res],
} = await payload.find({
collection: categoriesSlug,
where: {
id: { equals: category.id },
},
joins: {
relatedPosts: false,
},
})
// removed from the result
expect(res.relatedPosts).toBeUndefined()
expect(res.hasManyPosts.docs).toBeDefined()
expect(res.hasManyPostsLocalized.docs).toBeDefined()
expect(res.group.relatedPosts.docs).toBeDefined()
expect(res.group.camelCasePosts.docs).toBeDefined()
})
it('rEST API should not populate individual join by providing schemaPath=false', async () => {
const {
docs: [res],
} = await restClient
.GET(`/${categoriesSlug}`, {
query: {
where: {
id: { equals: category.id },
},
joins: {
relatedPosts: false,
},
},
})
.then((res) => res.json())
// removed from the result
expect(res.relatedPosts).toBeUndefined()
expect(res.hasManyPosts.docs).toBeDefined()
expect(res.hasManyPostsLocalized.docs).toBeDefined()
expect(res.group.relatedPosts.docs).toBeDefined()
expect(res.group.camelCasePosts.docs).toBeDefined()
})
it('should have correct totalDocs', async () => {
for (let i = 0; i < 50; i++) {
await payload.create({ collection: categoriesSlug, data: { name: 'totalDocs' } })
}
const count = await payload.count({
collection: categoriesSlug,
where: { name: { equals: 'totalDocs' } },
})
expect(count.totalDocs).toBe(50)
const find = await payload.find({
collection: categoriesSlug,
limit: 5,
where: { name: { equals: 'totalDocs' } },
})
expect(find.totalDocs).toBe(50)
expect(find.docs).toHaveLength(5)
await payload.delete({ collection: categoriesSlug, where: { name: { equals: 'totalDocs' } } })
})
it('should self join', async () => {
const doc_1 = await payload.create({ collection: 'self-joins', data: {} })
const doc_2 = await payload.create({ collection: 'self-joins', data: { rel: doc_1 }, depth: 0 })
const data = await payload.findByID({ collection: 'self-joins', id: doc_1.id, depth: 1 })
expect((data.joins.docs[0] as TypeWithID).id).toBe(doc_2.id)
})
it('should populate joins on depth 2', async () => {
const depthJoin_2 = await payload.create({ collection: 'depth-joins-2', data: {}, depth: 0 })
const depthJoin_1 = await payload.create({
collection: 'depth-joins-1',
data: { rel: depthJoin_2 },
depth: 0,
})
const depthJoin_3 = await payload.create({
collection: 'depth-joins-3',
data: { rel: depthJoin_1 },
depth: 0,
})
const data = await payload.findByID({
collection: 'depth-joins-2',
id: depthJoin_2.id,
depth: 2,
})
const joinedDoc = data.joins.docs[0] as DepthJoins1
expect(joinedDoc.id).toBe(depthJoin_1.id)
const joinedDoc2 = joinedDoc.joins.docs[0] as DepthJoins3
expect(joinedDoc2.id).toBe(depthJoin_3.id)
})
})
async function createPost(overrides?: Partial<Post>, locale?: Config['locale']) {
return payload.create({
collection: postsSlug,
locale,
data: {
title: 'test',
...overrides,
},
})
}