import type { CollectionSlug, Payload } from 'payload' import path from 'path' import { fileURLToPath } from 'url' import type { NextRESTClient } from '../helpers/NextRESTClient.js' import { initPayloadInt } from '../helpers/initPayloadInt.js' let payload: Payload let restClient: NextRESTClient const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) describe('Sort', () => { beforeAll(async () => { // @ts-expect-error: initPayloadInt does not have a proper type definition ;({ payload, restClient } = await initPayloadInt(dirname)) }) afterAll(async () => { if (typeof payload.db.destroy === 'function') { await payload.db.destroy() } }) describe('Local API', () => { beforeAll(async () => { await createData('posts', [ { text: 'Post 1', number: 1, number2: 10, group: { number: 100 } }, { text: 'Post 2', number: 2, number2: 10, group: { number: 200 } }, { text: 'Post 3', number: 3, number2: 5, group: { number: 150 } }, { text: 'Post 10', number: 10, number2: 5, group: { number: 200 } }, { text: 'Post 11', number: 11, number2: 20, group: { number: 150 } }, { text: 'Post 12', number: 12, number2: 20, group: { number: 100 } }, ]) await createData('default-sort', [ { text: 'Post default-5 b', number: 5 }, { text: 'Post default-10', number: 10 }, { text: 'Post default-5 a', number: 5 }, { text: 'Post default-1', number: 1 }, ]) }) afterAll(async () => { await payload.delete({ collection: 'posts', where: {} }) await payload.delete({ collection: 'default-sort', where: {} }) }) describe('Default sort', () => { it('should sort posts by default definition in collection', async () => { const posts = await payload.find({ collection: 'default-sort', // 'number,-text' }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post default-1', 'Post default-5 b', 'Post default-5 a', 'Post default-10', ]) }) }) describe('Sinlge sort field', () => { it('should sort posts by text field', async () => { const posts = await payload.find({ collection: 'posts', sort: 'text', }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 1', 'Post 10', 'Post 11', 'Post 12', 'Post 2', 'Post 3', ]) }) it('should sort posts by text field desc', async () => { const posts = await payload.find({ collection: 'posts', sort: '-text', }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 3', 'Post 2', 'Post 12', 'Post 11', 'Post 10', 'Post 1', ]) }) it('should sort posts by number field', async () => { const posts = await payload.find({ collection: 'posts', sort: 'number', }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 1', 'Post 2', 'Post 3', 'Post 10', 'Post 11', 'Post 12', ]) }) it('should sort posts by number field desc', async () => { const posts = await payload.find({ collection: 'posts', sort: '-number', }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 12', 'Post 11', 'Post 10', 'Post 3', 'Post 2', 'Post 1', ]) }) }) describe('Sort by multiple fields', () => { it('should sort posts by multiple fields', async () => { const posts = await payload.find({ collection: 'posts', sort: ['number2', 'number'], }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 3', // 5, 3 'Post 10', // 5, 10 'Post 1', // 10, 1 'Post 2', // 10, 2 'Post 11', // 20, 11 'Post 12', // 20, 12 ]) }) it('should sort posts by multiple fields asc and desc', async () => { const posts = await payload.find({ collection: 'posts', sort: ['number2', '-number'], }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 10', // 5, 10 'Post 3', // 5, 3 'Post 2', // 10, 2 'Post 1', // 10, 1 'Post 12', // 20, 12 'Post 11', // 20, 11 ]) }) it('should sort posts by multiple fields with group', async () => { const posts = await payload.find({ collection: 'posts', sort: ['-group.number', '-number'], }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 10', // 200, 10 'Post 2', // 200, 2 'Post 11', // 150, 11 'Post 3', // 150, 3 'Post 12', // 100, 12 'Post 1', // 100, 1 ]) }) }) describe('Sort with drafts', () => { beforeAll(async () => { const testData1 = await payload.create({ collection: 'drafts', data: { text: 'Post 1 draft', number: 10 }, draft: true, }) await payload.update({ collection: 'drafts', id: testData1.id, data: { text: 'Post 1 draft updated', number: 20 }, draft: true, }) await payload.update({ collection: 'drafts', id: testData1.id, data: { text: 'Post 1 draft updated', number: 30 }, draft: true, }) await payload.update({ collection: 'drafts', id: testData1.id, data: { text: 'Post 1 published', number: 15 }, draft: false, }) const testData2 = await payload.create({ collection: 'drafts', data: { text: 'Post 2 draft', number: 1 }, draft: true, }) await payload.update({ collection: 'drafts', id: testData2.id, data: { text: 'Post 2 published', number: 2 }, draft: false, }) await payload.update({ collection: 'drafts', id: testData2.id, data: { text: 'Post 2 newdraft', number: 100 }, draft: true, }) await payload.create({ collection: 'drafts', data: { text: 'Post 3 draft', number: 3 }, draft: true, }) }) it('should sort latest without draft', async () => { const posts = await payload.find({ collection: 'drafts', sort: 'number', draft: false, }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 2 published', // 2 'Post 3 draft', // 3 'Post 1 published', // 15 ]) }) it('should sort latest with draft', async () => { const posts = await payload.find({ collection: 'drafts', sort: 'number', draft: true, }) expect(posts.docs.map((post) => post.text)).toEqual([ 'Post 3 draft', // 3 'Post 1 published', // 15 'Post 2 newdraft', // 100 ]) }) it('should sort versions', async () => { const posts = await payload.findVersions({ collection: 'drafts', sort: 'version.number', draft: false, }) expect(posts.docs.map((post) => post.version.text)).toEqual([ 'Post 2 draft', // 1 'Post 2 published', // 2 'Post 3 draft', // 3 'Post 1 draft', // 10 'Post 1 published', // 15 'Post 1 draft updated', // 20 'Post 1 draft updated', // 30 'Post 2 newdraft', // 100 ]) }) }) describe('Localized sort', () => { beforeAll(async () => { const testData1 = await payload.create({ collection: 'localized', data: { text: 'Post 1 english', number: 10 }, locale: 'en', }) await payload.update({ collection: 'localized', id: testData1.id, data: { text: 'Post 1 norsk', number: 20 }, locale: 'nb', }) const testData2 = await payload.create({ collection: 'localized', data: { text: 'Post 2 english', number: 25 }, locale: 'en', }) await payload.update({ collection: 'localized', id: testData2.id, data: { text: 'Post 2 norsk', number: 5 }, locale: 'nb', }) }) it('should sort localized field', async () => { const englishPosts = await payload.find({ collection: 'localized', sort: 'number', locale: 'en', }) expect(englishPosts.docs.map((post) => post.text)).toEqual([ 'Post 1 english', // 10 'Post 2 english', // 20 ]) const norwegianPosts = await payload.find({ collection: 'localized', sort: 'number', locale: 'nb', }) expect(norwegianPosts.docs.map((post) => post.text)).toEqual([ 'Post 2 norsk', // 5 'Post 1 norsk', // 25 ]) }) }) }) describe('REST API', () => { beforeAll(async () => { await createData('posts', [ { text: 'Post 1', number: 1, number2: 10 }, { text: 'Post 2', number: 2, number2: 10 }, { text: 'Post 3', number: 3, number2: 5 }, { text: 'Post 10', number: 10, number2: 5 }, { text: 'Post 11', number: 11, number2: 20 }, { text: 'Post 12', number: 12, number2: 20 }, ]) }) afterAll(async () => { await payload.delete({ collection: 'posts', where: {} }) }) describe('Sinlge sort field', () => { it('should sort posts by text field', async () => { const res = await restClient .GET(`/posts`, { query: { sort: 'text', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 1', 'Post 10', 'Post 11', 'Post 12', 'Post 2', 'Post 3', ]) }) it('should sort posts by text field desc', async () => { const res = await restClient .GET(`/posts`, { query: { sort: '-text', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 3', 'Post 2', 'Post 12', 'Post 11', 'Post 10', 'Post 1', ]) }) it('should sort posts by number field', async () => { const res = await restClient .GET(`/posts`, { query: { sort: 'number', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 1', 'Post 2', 'Post 3', 'Post 10', 'Post 11', 'Post 12', ]) }) it('should sort posts by number field desc', async () => { const res = await restClient .GET(`/posts`, { query: { sort: '-number', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 12', 'Post 11', 'Post 10', 'Post 3', 'Post 2', 'Post 1', ]) }) }) describe('Sort by multiple fields', () => { it('should sort posts by multiple fields', async () => { const res = await restClient .GET(`/posts`, { query: { sort: 'number2,number', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 3', // 5, 3 'Post 10', // 5, 10 'Post 1', // 10, 1 'Post 2', // 10, 2 'Post 11', // 20, 11 'Post 12', // 20, 12 ]) }) it('should sort posts by multiple fields asc and desc', async () => { const res = await restClient .GET(`/posts`, { query: { sort: 'number2,-number', }, }) .then((res) => res.json()) expect(res.docs.map((post) => post.text)).toEqual([ 'Post 10', // 5, 10 'Post 3', // 5, 3 'Post 2', // 10, 2 'Post 1', // 10, 1 'Post 12', // 20, 12 'Post 11', // 20, 11 ]) }) }) }) }) async function createData(collection: CollectionSlug, data: Record[]) { for (const item of data) { await payload.create({ collection, data: item }) } }