test: remove all old tests

This commit is contained in:
Elliot DeNolf
2022-07-17 16:07:59 -07:00
parent d433351dbd
commit f5ad7a163a
16 changed files with 33 additions and 2912 deletions

View File

@@ -2,6 +2,7 @@ module.exports = {
verbose: true,
testEnvironment: 'node',
testMatch: [
'**/src/**/*.spec.ts',
'**/test/**/*int.spec.ts',
],
globalSetup: './test/jest.setup.ts',

View File

@@ -1,417 +0,0 @@
/**
* @jest-environment node
*/
import { request, GraphQLClient } from 'graphql-request';
import getConfig from '../../../config/load';
import { email, password } from '../../../mongoose/testCredentials';
require('isomorphic-fetch');
const config = getConfig();
const url = `${config.serverURL}${config.routes.api}${config.routes.graphQL}`;
let client = null;
let token = null;
describe('GrahpQL Resolvers', () => {
beforeAll(async (done) => {
const query = `
mutation {
loginAdmin(
email: "${email}",
password: "${password}"
) {
token
}
}`;
const response = await request(url, query);
token = response.loginAdmin.token;
client = new GraphQLClient(url, { headers: { Authorization: `JWT ${token}` } });
done();
});
describe('Create', () => {
it('should allow a localized post to be created', async () => {
const title = 'gql create';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const data = response.createLocalizedPost;
expect(data.title).toBe(title);
expect(data.id).toStrictEqual(expect.any(String));
// const timestampRegex = /^(\d{4})(?:-?W(\d+)(?:-?(\d+)D?)?|(?:-(\d+))?-(\d+))(?:[T ](\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?)?(?:Z(-?\d*))?$/;
// expect(data.createdAt).toStrictEqual(expect.stringMatching(timestampRegex));
// expect(data.updatedAt).toStrictEqual(expect.stringMatching(timestampRegex));
expect(data.createdAt).toStrictEqual(expect.any(String));
expect(data.updatedAt).toStrictEqual(expect.any(String));
});
});
describe('Read', () => {
it('should be able to read localized post', async () => {
const title = 'gql read 1';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const readQuery = `query {
LocalizedPost(id: "${id}") {
id
}
}`;
const readResponse = await client.request(readQuery);
const retrievedId = readResponse.LocalizedPost.id;
expect(retrievedId).toStrictEqual(id);
});
it('should query exists - true', async () => {
const title = 'gql read 2';
const description = 'description';
const summary = 'summary';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", summary: "${summary}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const readQuery = `query {
LocalizedPosts(where: { summary: { exists: true }}) {
docs {
id
description
summary
}
}
}`;
const readResponse = await client.request(readQuery);
const retrievedId = readResponse.LocalizedPosts.docs[0].id;
expect(readResponse.LocalizedPosts.docs).toHaveLength(1);
expect(retrievedId).toStrictEqual(id);
});
it('should query exists - false', async () => {
const title = 'gql read 3';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const readQuery = `query {
LocalizedPosts(where: { summary: { exists: false }}) {
docs {
id
summary
}
}
}`;
const readResponse = await client.request(readQuery);
const retrievedDoc = readResponse.LocalizedPosts.docs[0];
expect(readResponse.LocalizedPosts.docs.length).toBeGreaterThan(0);
expect(retrievedDoc.id).toStrictEqual(id);
expect(retrievedDoc.summary).toBeNull();
});
});
describe('Update', () => {
it('should allow updating an existing post', async () => {
const title = 'gql update';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: { title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
}
}`;
const createResponse = await client.request(query);
const createData = createResponse.createLocalizedPost;
const { id } = createData;
const updatedDesc = 'updated description';
// language=graphQL
const update = `
mutation {
updateLocalizedPost(id: "${id}" data: {description: "${updatedDesc}"}) {
description
}
}`;
const response = await client.request(update);
const data = response.updateLocalizedPost;
expect(data.description).toBe(updatedDesc);
});
});
describe('Delete', () => {
it('should be able to delete a localized post', async () => {
const title = 'gql delete';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const deleteMutation = `mutation {
deleteLocalizedPost(id: "${id}") {
id
}
}`;
const deleteResponse = await client.request(deleteMutation);
const deletedId = deleteResponse.deleteLocalizedPost.id;
expect(deletedId).toStrictEqual(id);
});
});
describe('Error Handler', () => {
it('should return have an array of errors when making a bad request', async () => {
let error;
// language=graphQL
const query = `query {
LocalizedPosts(where: { summary: { exists: true }}) {
docs {
badFieldName
}
}
}`;
await client.request(query).catch((err) => {
error = err;
});
expect(Array.isArray(error.response.errors)).toBe(true);
expect(typeof error.response.errors[0].message).toBe('string');
});
it('should return have an array of errors when failing to pass validation', async () => {
let error;
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {priority: 10}) {
id
priority
createdAt
updatedAt
}
}`;
await client.request(query).catch((err) => {
error = err;
});
expect(Array.isArray(error.response.errors)).toBe(true);
expect(typeof error.response.errors[0].message).toBe('string');
});
});
describe('Custom ID', () => {
it('should create', async () => {
const id = 10;
const query = `mutation {
createCustomID(data: {
id: ${id},
name: "custom"
}) {
id,
name
}
}`;
const response = await client.request(query);
const data = response.createCustomID;
expect(data.id).toStrictEqual(id);
});
it('should update', async () => {
const id = 11;
const name = 'custom name';
const query = `
mutation {
createCustomID(data: {
id: ${id},
name: "${name}"
}) {
id
name
}
}`;
await client.request(query);
const updatedName = 'updated name';
const update = `
mutation {
updateCustomID(id: ${id} data: {name: "${updatedName}"}) {
name
}
}`;
const response = await client.request(update);
const data = response.updateCustomID;
expect(data.name).toStrictEqual(updatedName);
expect(data.name).not.toStrictEqual(name);
});
it('should query on id', async () => {
const id = 15;
const name = 'custom name';
const create = `mutation {
createCustomID(data: {
id: ${id},
name: "${name}"
}) {
id
name
}
}`;
await client.request(create);
const query = `
query {
CustomIDs(where: { id: { equals: ${id} } }) {
docs {
id
name
}
}
}`;
const response = await client.request(query);
const [doc] = response.CustomIDs.docs;
expect(doc.id).toStrictEqual(id);
expect(doc.name).toStrictEqual(name);
});
it('should delete', async () => {
const id = 12;
const query = `mutation {
createCustomID(data: {
id: ${id},
name: "delete me"
}) {
id
name
}
}`;
await client.request(query);
const deleteMutation = `mutation {
deleteCustomID(id: ${id}) {
id
}
}`;
const deleteResponse = await client.request(deleteMutation);
const deletedId = deleteResponse.deleteCustomID.id;
expect(deletedId).toStrictEqual(id);
});
it('should allow relationships', async () => {
const id = 13;
const query = `mutation {
createCustomID(data: {
id: ${id},
name: "relate me"
}) {
id
name
}
}`;
await client.request(query);
const relation = `mutation {
createRelationshipA(data: {
customID: [ ${id} ]
}) {
customID {
id
}
}
}`;
const relationResponse = await client.request(relation);
const { customID } = relationResponse.createRelationshipA;
expect(customID).toHaveLength(1);
expect(customID).toHaveLength(1);
});
});
});

View File

@@ -1,246 +0,0 @@
import fs from 'fs';
import path from 'path';
import { UploadedFile } from 'express-fileupload';
import payload from '../../..';
import getFileByPath from '../../../uploads/getFileByPath';
let createdMediaID;
payload.init({
secret: 'SECRET_KEY',
mongoURL: 'mongodb://localhost/payload',
local: true,
});
describe('Collections - Local', () => {
describe('Create', () => {
it('should allow an upload-enabled file to be created and uploaded', async () => {
const alt = 'Alt Text Here';
const filePath = path.resolve(
__dirname,
'../../../admin/assets/images/generic-block-image.svg',
);
const { size } = fs.statSync(filePath);
const result: Media = await payload.create({
collection: 'media',
data: {
alt,
},
filePath,
});
expect(result.id).toBeDefined();
expect(result.alt).toStrictEqual(alt);
expect(result.filename).toStrictEqual('generic-block-image.svg');
expect(result.filesize).toStrictEqual(size);
createdMediaID = result.id;
});
it('should allow an upload-enabled file to be created from file instead of filepath', async () => {
const name = 'upload-local.svg';
const alt = 'Alt Text Here';
const filePath = path.resolve(
__dirname,
'../../../admin/assets/images/generic-block-image.svg',
);
const { size } = fs.statSync(filePath);
const file = getFileByPath(filePath);
file.name = name;
const result: Media = await payload.create({
collection: 'media',
data: {
alt,
},
file,
});
expect(result.id).toBeDefined();
expect(result.alt).toStrictEqual(alt);
expect(result.filename).toStrictEqual(name);
expect(result.filesize).toStrictEqual(size);
createdMediaID = result.id;
});
});
describe('Update', () => {
it('should allow an upload-enabled file to be re-uploaded and alt-text to be changed.', async () => {
const newAltText = 'New Alt Text Here';
const result: Media = await payload.update({
collection: 'media',
id: createdMediaID,
data: {
alt: newAltText,
},
filePath: path.resolve(__dirname, '../../../admin/assets/images/og-image.png'),
});
expect(result.alt).toStrictEqual(newAltText);
expect(result.sizes.mobile.width).toStrictEqual(320);
expect(result.width).toStrictEqual(640);
});
});
describe('Read with Hidden Fields', () => {
it('should allow a document with nested hidden fields to be retrieved with hidden fields shown.', async () => {
const demoHiddenField = 'this is going to be hidden';
const result = await payload.create({
collection: 'localized-posts',
data: {
title: 'this post has a hidden field present',
description: 'desc',
priority: 1,
nonLocalizedGroup: {
text: '40w5g534gw34j',
},
localizedGroup: {
text: '34lijgw45ligjw4li5j',
demoHiddenField,
},
},
});
expect(result.id).toBeDefined();
expect(result.localizedGroup).toBeDefined();
expect(result.localizedGroup.demoHiddenField).toBeUndefined();
const withHiddenFields = await payload.findByID({
collection: 'localized-posts',
id: result.id,
showHiddenFields: true,
});
expect(withHiddenFields.localizedGroup.demoHiddenField).toStrictEqual(demoHiddenField);
expect(withHiddenFields.id).toStrictEqual(result.id);
});
it('should allow a document with a relationship to a document with hidden fields to read the related document hidden fields', async () => {
const demoHiddenField = 'this is going to be hidden';
const relationshipA = await payload.create({
collection: 'relationship-a',
data: {
demoHiddenField,
},
});
expect(relationshipA.id).toBeDefined();
expect(relationshipA.demoHiddenField).toBeUndefined();
const relationshipB = await payload.create({
collection: 'relationship-b',
data: {
title: 'pretty clever name here',
post: [relationshipA.id],
},
});
expect(relationshipB.id).toBeDefined();
const relationshipBWithHiddenNestedField = await payload.findByID({
collection: 'relationship-b',
id: relationshipB.id,
showHiddenFields: true,
});
expect(relationshipBWithHiddenNestedField.post[0].demoHiddenField).toStrictEqual(
demoHiddenField,
);
});
describe('Find', () => {
const title = 'local-find';
beforeAll(async (done) => {
const data = {
title,
description: 'a description',
priority: 1,
nonLocalizedGroup: {
text: 'english',
},
localizedGroup: {
text: 'english',
},
nonLocalizedArray: [
{
localizedEmbeddedText: 'english',
},
],
richTextBlocks: [
{
blockType: 'richTextBlock',
blockName: 'Test Block Name',
content: [
{
children: [{ text: 'english' }],
},
],
},
],
};
await payload.create({
collection: 'localized-posts',
data,
});
Array.from(Array(10).keys()).map(async (i) => {
const uniqueTitle = `${title}-${i}`;
await payload.create({
collection: 'localized-posts',
data: {
...data,
title: uniqueTitle,
},
});
});
done();
});
it('should find collection with query', async () => {
const result = await payload.find({
collection: 'localized-posts',
where: {
title: {
equals: title,
},
},
});
const doc = result.docs[0];
expect(doc.id).toBeDefined();
expect(doc.title).toStrictEqual(title);
});
it('should allow disable pagination to return all docs', async () => {
const result = await payload.find({
collection: 'localized-posts',
pagination: false,
limit: 5, // limit will not be used
});
expect(result.docs.length).toBeGreaterThan(10);
});
});
});
});
interface ImageSize {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
}
interface Media {
id?: string;
filename?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
maintainedAspectRatio?: ImageSize;
tablet?: ImageSize;
mobile?: ImageSize;
icon?: ImageSize;
};
alt: string;
}

View File

@@ -1,892 +0,0 @@
import { v4 as uuid } from 'uuid';
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = {};
let localizedPost;
let relationshipBID;
const localizedPostTitle = 'title';
const englishPostDesc = 'english description';
const spanishPostDesc = 'spanish description';
describe('Collections - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Create', () => {
let response;
let data;
beforeAll(async () => {
response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: localizedPostTitle,
description: englishPostDesc,
priority: 1,
nonLocalizedGroup: {
text: 'english',
},
localizedGroup: {
text: 'english',
},
nonLocalizedArray: [
{
localizedEmbeddedText: 'english',
},
],
richTextBlocks: [
{
blockType: 'richTextBlock',
blockName: 'Test Block Name',
content: [
{
children: [{ text: 'english' }],
},
],
},
],
}),
headers,
method: 'post',
});
data = await response.json();
});
it('should allow a localized post to be created', async () => {
expect(response.status).toBe(201);
expect(data.doc.title).toBeDefined();
expect(data.doc.id).toBeDefined();
expect(data.doc.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.doc.localizedGroup.text).toStrictEqual('english');
expect(data.doc.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.doc.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
const timestampRegex = /^(\d{4})(?:-?W(\d+)(?:-?(\d+)D?)?|(?:-(\d+))?-(\d+))(?:[T ](\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?)?(?:Z(-?\d*))?$/;
expect(data.doc.createdAt).toStrictEqual(expect.stringMatching(timestampRegex));
expect(data.doc.updatedAt).toStrictEqual(expect.stringMatching(timestampRegex));
localizedPost = data.doc;
});
it('should add id to array items', async () => {
expect(data.doc.nonLocalizedArray[0].id).toBeDefined();
});
it('should add id to block items', async () => {
expect(data.doc.richTextBlocks[0].id).toBeDefined();
});
});
describe('Update', () => {
it('should allow updating an existing post', async () => {
const createResponse = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'newTitle',
description: 'original description',
richText: [
{
children: [{ text: 'english' }],
},
],
priority: 1,
}),
headers,
method: 'post',
});
expect(createResponse.status).toBe(201);
const createData = await createResponse.json();
const { id } = createData.doc;
const updatedDesc = 'updated description';
const updatedRichText = [
{
children: [{ text: 'english update' }],
},
];
const updatedNonLocalizedArray = [
{
localizedEmbeddedText: 'english',
},
{
localizedEmbeddedText: 'english update',
},
];
const response = await fetch(`${url}/api/localized-posts/${id}`, {
body: JSON.stringify({
title: 'newTitle',
description: updatedDesc,
richText: updatedRichText,
nonLocalizedArray: updatedNonLocalizedArray,
priority: '',
}),
headers,
method: 'put',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.doc.description).toBe(updatedDesc);
expect(data.doc.priority).not.toStrictEqual(1);
expect(data.doc.nonLocalizedArray).toHaveLength(2);
expect(data.doc.richText[0].children[0].text).toBe('english update');
// make certain the stored object is exactly the same as the returned object
const stored = await (
await fetch(`${url}/api/localized-posts/${id}`, {
method: 'get',
headers,
})
).json();
expect(data.doc).toMatchObject(stored);
});
it('should allow a Spanish locale to be added to an existing post', async () => {
const response = await fetch(`${url}/api/localized-posts/${localizedPost.id}?locale=es`, {
body: JSON.stringify({
title: 'title in spanish',
description: spanishPostDesc,
priority: 1,
nonLocalizedGroup: {
text: 'spanish',
},
localizedGroup: {
text: 'spanish',
},
nonLocalizedArray: [
{
id: localizedPost.nonLocalizedArray[0].id,
localizedEmbeddedText: 'spanish',
},
],
richTextBlocks: [
{
id: localizedPost.richTextBlocks[0].id,
blockType: 'richTextBlock',
blockName: 'Test Block Name',
content: [
{
children: [{ text: 'spanish' }],
},
],
},
],
}),
headers,
method: 'put',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.doc.description).toBe(spanishPostDesc);
expect(data.doc.nonLocalizedGroup.text).toStrictEqual('spanish');
expect(data.doc.localizedGroup.text).toStrictEqual('spanish');
expect(data.doc.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('spanish');
expect(data.doc.richTextBlocks[0].content[0].children[0].text).toStrictEqual('spanish');
});
});
describe('Read', () => {
describe('Localized', () => {
it('should allow a localized post to be retrieved in unspecified locale, defaulting to English', async () => {
const response = await fetch(`${url}/api/localized-posts/${localizedPost.id}`);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.description).toBe(englishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.localizedGroup.text).toStrictEqual('english');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
});
it('should allow a localized post to be retrieved in specified English locale', async () => {
const response = await fetch(`${url}/api/localized-posts/${localizedPost.id}?locale=en`);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.description).toBe(englishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.localizedGroup.text).toStrictEqual('english');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
});
it('should allow a localized post to be retrieved in Spanish', async () => {
const response = await fetch(`${url}/api/localized-posts/${localizedPost.id}?locale=es`);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.description).toBe(spanishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('spanish');
expect(data.localizedGroup.text).toStrictEqual('spanish');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('spanish');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('spanish');
});
it('should allow a localized post to be retrieved in all locales', async () => {
const response = await fetch(`${url}/api/localized-posts/${localizedPost.id}?locale=all`);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.description.es).toBe(spanishPostDesc);
expect(data.description.en).toBe(englishPostDesc);
});
});
it('should allow querying by id', async () => {
const response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'another title',
description: 'description',
priority: 1,
}),
headers,
method: 'post',
});
expect(response.status).toBe(201);
const data = await response.json();
const getResponse = await fetch(`${url}/api/localized-posts/${data.doc.id}`);
expect(getResponse.status).toBe(200);
const getData = await getResponse.json();
expect(getData.id).toBeDefined();
});
it('should allow querying on a field', async () => {
const desc = 'query test';
const response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'unique title here',
description: desc,
priority: 1,
nonLocalizedGroup: {
text: 'sample content',
},
}),
headers,
method: 'post',
});
expect(response.status).toBe(201);
const getResponse = await fetch(
`${url}/api/localized-posts?where[description][equals]=${desc}`,
);
const data = await getResponse.json();
expect(getResponse.status).toBe(200);
expect(data.docs[0].description).toBe(desc);
expect(data.docs).toHaveLength(1);
const getResponse2 = await fetch(
`${url}/api/localized-posts?where[nonLocalizedGroup.text][equals]=sample content`,
);
const data2 = await getResponse2.json();
expect(getResponse2.status).toBe(200);
expect(data2.docs[0].description).toBe(desc);
expect(data2.docs).toHaveLength(1);
});
it('should allow querying with OR', async () => {
const title1 = 'Or1';
const title2 = 'Or2';
const response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: title1,
description: 'desc',
priority: 1,
}),
headers,
method: 'post',
});
const response2 = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: title2,
description: 'desc',
priority: 1,
}),
headers,
method: 'post',
});
expect(response.status).toBe(201);
expect(response2.status).toBe(201);
const queryResponse = await fetch(
`${url}/api/localized-posts?where[or][0][title][equals]=${title1}&where[or][1][title][equals]=${title2}`,
);
const data = await queryResponse.json();
expect(queryResponse.status).toBe(200);
expect(data.docs).toHaveLength(2);
expect(data.docs).toContainEqual(expect.objectContaining({ title: title1 }));
expect(data.docs).toContainEqual(expect.objectContaining({ title: title2 }));
});
it('should allow querying with OR, 1 result', async () => {
const title1 = 'OrNegative1';
const title2 = 'OrNegative2';
const response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: title1,
description: 'desc',
priority: 1,
}),
headers,
method: 'post',
});
const response2 = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: title2,
description: 'desc',
priority: 1,
}),
headers,
method: 'post',
});
expect(response.status).toBe(201);
expect(response2.status).toBe(201);
const queryResponse = await fetch(
`${url}/api/localized-posts?where[or][0][title][equals]=${title1}&where[or][1][title][equals]=nonexistent`,
);
const data = await queryResponse.json();
expect(queryResponse.status).toBe(200);
expect(data.docs).toHaveLength(1);
expect(data.docs[0].title).toBe(title1);
});
it('should allow querying by a non-localized nested relationship property', async () => {
const relationshipBTitle = 'test';
const relationshipBRes = await fetch(`${url}/api/relationship-b?depth=0`, {
body: JSON.stringify({
title: relationshipBTitle,
}),
headers,
method: 'post',
});
const relationshipBData = await relationshipBRes.json();
const res = await fetch(`${url}/api/relationship-a?depth=0`, {
body: JSON.stringify({
post: relationshipBData.doc.id,
}),
headers,
method: 'post',
});
const additionalRelationshipARes = await fetch(`${url}/api/relationship-a?depth=0`, {
body: JSON.stringify({
postLocalizedMultiple: [
{
relationTo: 'localized-posts',
value: localizedPost.id,
},
],
}),
headers,
method: 'post',
});
const relationshipAData = await res.json();
expect(res.status).toBe(201);
expect(additionalRelationshipARes.status).toBe(201);
expect(relationshipAData.doc.post).toBe(relationshipBData.doc.id);
const queryRes = await fetch(
`${url}/api/relationship-a?where[post.title][equals]=${relationshipBTitle}`,
);
const data = await queryRes.json();
expect(data.docs).toHaveLength(1);
});
it('should allow querying by a localized nested relationship property', async () => {
const res = await fetch(`${url}/api/relationship-a`, {
body: JSON.stringify({
LocalizedPost: [localizedPost.id],
}),
headers,
method: 'post',
});
expect(res.status).toBe(201);
const queryRes1 = await fetch(
`${url}/api/relationship-a?where[LocalizedPost.title][in]=${localizedPostTitle}`,
);
const data1 = await queryRes1.json();
expect(data1.docs).toHaveLength(1);
});
it('should allow querying by a localized nested relationship with many relationTos', async () => {
const relationshipBTitle = 'lawleifjawelifjew';
const relationshipB = await fetch(`${url}/api/relationship-b?depth=0`, {
body: JSON.stringify({
title: relationshipBTitle,
}),
headers,
method: 'post',
}).then((res) => res.json());
expect(relationshipB.doc.id).toBeDefined();
const res = await fetch(`${url}/api/relationship-a`, {
body: JSON.stringify({
postManyRelationships: {
value: relationshipB.doc.id,
relationTo: 'relationship-b',
},
}),
headers,
method: 'post',
});
expect(res.status).toBe(201);
const queryRes = await fetch(
`${url}/api/relationship-a?where[postManyRelationships.value][equals]=${relationshipB.doc.id}`,
);
const data = await queryRes.json();
expect(data.docs).toHaveLength(1);
});
it('should allow querying by a non-localized relationship with many relationTos', async () => {
const relationshipB = await fetch(`${url}/api/relationship-b?depth=0`, {
body: JSON.stringify({
title: 'awefjlaiwejfalweiijfaew',
nonLocalizedRelationToMany: {
relationTo: 'localized-posts',
value: localizedPost.id,
},
}),
headers,
method: 'post',
}).then((res) => res.json());
expect(relationshipB.doc.id).toBeDefined();
relationshipBID = relationshipB.doc.id;
const queryRes = await fetch(
`${url}/api/relationship-b?where[nonLocalizedRelationToMany.value][equals]=${localizedPost.id}`,
);
const data = await queryRes.json();
expect(data.docs).toHaveLength(1);
});
it('should propagate locale through populated documents', async () => {
const relationshipB = await fetch(`${url}/api/relationship-b/${relationshipBID}?locale=es`, {
headers,
});
const data = await relationshipB.json();
expect(data.nonLocalizedRelationToMany.value.description).toBe(spanishPostDesc);
});
it('should allow querying by a numeric custom ID', async () => {
const customID = 1988;
const customIDResult = await fetch(`${url}/api/custom-id?depth=0`, {
body: JSON.stringify({
id: customID,
name: 'woohoo',
}),
headers,
method: 'post',
}).then((res) => res.json());
expect(customIDResult.doc.id).toStrictEqual(customID);
await fetch(`${url}/api/custom-id?depth=0`, {
body: JSON.stringify({
id: 2343452,
name: 'another post',
}),
headers,
method: 'post',
}).then((res) => res.json());
const queryRes1 = await fetch(`${url}/api/custom-id?where[id][equals]=${customID}`, {
headers,
});
const data1 = await queryRes1.json();
expect(data1.docs).toHaveLength(1);
const queryByIDRes = await fetch(`${url}/api/custom-id/${customID}`, {
headers,
});
const queryByIDData = await queryByIDRes.json();
expect(queryByIDData.id).toStrictEqual(customID);
});
it('should allow querying by a field within a group', async () => {
const text = 'laiwjefliajwe';
await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'super great title',
description: 'desc',
priority: 1,
nonLocalizedGroup: {
text,
},
localizedGroup: {
text,
},
}),
headers,
method: 'post',
});
const queryRes1 = await fetch(
`${url}/api/localized-posts?where[nonLocalizedGroup.text][equals]=${text}`,
);
const data1 = await queryRes1.json();
expect(data1.docs).toHaveLength(1);
const queryRes2 = await fetch(
`${url}/api/localized-posts?where[localizedGroup.text][equals]=${text}`,
);
const data2 = await queryRes2.json();
expect(queryRes2.status).toBe(200);
expect(data2.docs).toHaveLength(1);
});
});
describe('Delete', () => {
it('should allow a post to be deleted', async () => {
const response = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'title to be deleted',
description: englishPostDesc,
priority: 1,
}),
headers,
method: 'post',
});
const data = await response.json();
const docId = data.doc.id;
expect(response.status).toBe(201);
expect(docId).toBeDefined();
const deleteResponse = await fetch(`${url}/api/localized-posts/${docId}`, {
headers,
method: 'delete',
});
const deleteData = await deleteResponse.json();
expect(deleteResponse.status).toBe(200);
expect(deleteData.id).toBe(docId);
});
});
describe('Metadata', () => {
async function createPost(title, description) {
await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: title || uuid(),
description,
priority: 1,
}),
headers,
method: 'post',
});
}
it('should include metadata', async () => {
const desc = 'metadataDesc';
for (let i = 0; i < 12; i += 1) {
// eslint-disable-next-line no-await-in-loop
await createPost(null, desc);
}
const getResponse = await fetch(
`${url}/api/localized-posts?where[description][equals]=${desc}`,
);
const data = await getResponse.json();
expect(getResponse.status).toBe(200);
// TODO: Assert exact length once query bug is fixed
expect(data.docs.length).toBeGreaterThan(0);
expect(data.totalDocs).toBeGreaterThan(0);
expect(data.limit).toBe(10);
expect(data.page).toBe(1);
expect(data.pagingCounter).toBe(1);
expect(data.hasPrevPage).toBe(false);
expect(data.hasNextPage).toBe(true);
expect(data.prevPage).toBeNull();
expect(data.nextPage).toBe(2);
});
it('should sort the results', async () => {
const desc = 'sort';
// Create 2 posts with different titles, same desc
const req1 = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'aaa',
description: desc,
priority: 1,
}),
headers,
method: 'post',
});
const req2 = await fetch(`${url}/api/localized-posts`, {
body: JSON.stringify({
title: 'zzz',
description: desc,
priority: 1,
}),
headers,
method: 'post',
});
const req1data = await req1.json();
const req2data = await req2.json();
const id1 = req1data.doc.id;
const id2 = req2data.doc.id;
// Query on shared desc and sort ascending
const getResponse = await fetch(
`${url}/api/localized-posts?where[description][equals]=${desc}&sort=title`,
);
const data = await getResponse.json();
expect(getResponse.status).toBe(200);
expect(data.docs).toHaveLength(2);
expect(data.docs[0].id).toStrictEqual(id1);
expect(data.docs[1].id).toStrictEqual(id2);
// Query on shared desc and sort descending
const getResponseSorted = await fetch(
`${url}/api/localized-posts?where[description][equals]=${desc}&sort=-title`,
);
const sortedData = await getResponseSorted.json();
expect(getResponse.status).toBe(200);
expect(sortedData.docs).toHaveLength(2);
// Opposite order from first request
expect(sortedData.docs[0].id).toStrictEqual(id2);
expect(sortedData.docs[1].id).toStrictEqual(id1);
});
});
describe('Field Access', () => {
it('should properly prevent / allow public users from reading a restricted field', async () => {
const firstArrayText1 = 'test 1';
const firstArrayText2 = 'test 2';
const response = await fetch(`${url}/api/localized-arrays`, {
body: JSON.stringify({
array: [
{
arrayText1: firstArrayText1,
arrayText2: 'test 2',
arrayText3: 'test 3',
allowPublicReadability: true,
},
{
arrayText1: firstArrayText2,
arrayText2: 'test 2',
arrayText3: 'test 3',
allowPublicReadability: false,
},
],
}),
headers,
method: 'post',
});
const data = await response.json();
const docId = data.doc.id;
expect(response.status).toBe(201);
expect(data.doc.array[1].arrayText1).toStrictEqual(firstArrayText2);
const unauthenticatedResponse = await fetch(`${url}/api/localized-arrays/${docId}`, {
headers: {
'Content-Type': 'application/json',
},
});
expect(unauthenticatedResponse.status).toBe(200);
const unauthenticatedData = await unauthenticatedResponse.json();
// This string should be allowed to come back
expect(unauthenticatedData.array[0].arrayText1).toBe(firstArrayText1);
// This string should be prevented from coming back
expect(unauthenticatedData.array[1].arrayText1).toBeUndefined();
const authenticatedResponse = await fetch(`${url}/api/localized-arrays/${docId}`, {
headers,
});
const authenticatedData = await authenticatedResponse.json();
// If logged in, we should get this field back
expect(authenticatedData.array[1].arrayText1).toStrictEqual(firstArrayText2);
});
});
describe('Unique', () => {
it('should prevent unique fields from duplicating data', async () => {
const nonUniqueTitle = 'title';
const response = await fetch(`${url}/api/uniques`, {
body: JSON.stringify({
title: nonUniqueTitle,
}),
headers,
method: 'post',
});
const data = await response.json();
expect(response.status).toBe(201);
expect(data.doc.title).toStrictEqual(nonUniqueTitle);
const failedResponse = await fetch(`${url}/api/uniques`, {
body: JSON.stringify({
title: nonUniqueTitle,
}),
headers,
method: 'post',
});
expect(failedResponse.status).toStrictEqual(500);
});
});
describe('Custom ID', () => {
const document = {
id: 1,
name: 'name',
};
let data;
beforeAll(async (done) => {
// create document
const create = await fetch(`${url}/api/custom-id`, {
body: JSON.stringify(document),
headers,
method: 'post',
});
data = await create.json();
done();
});
it('should create collections with custom ID', async () => {
expect(data.doc.id).toBe(document.id);
});
it('should read collections by custom ID', async () => {
const response = await fetch(`${url}/api/custom-id/${document.id}`, {
headers,
method: 'get',
});
const result = await response.json();
expect(result.id).toStrictEqual(document.id);
expect(result.name).toStrictEqual(document.name);
});
it('should update collection by custom ID', async () => {
const updatedDoc = { id: 'cannot-update-id', name: 'updated' };
const response = await fetch(`${url}/api/custom-id/${document.id}`, {
headers,
body: JSON.stringify(updatedDoc),
method: 'put',
});
const result = await response.json();
expect(result.doc.id).not.toStrictEqual(updatedDoc.id);
expect(result.doc.name).not.toStrictEqual(document.name);
expect(result.doc.name).toStrictEqual(updatedDoc.name);
});
it('should delete collection by custom ID', async () => {
const doc = {
id: 2,
name: 'delete me',
};
const createResponse = await fetch(`${url}/api/custom-id`, {
body: JSON.stringify(doc),
headers,
method: 'post',
});
const result = await createResponse.json();
const response = await fetch(`${url}/api/custom-id/${result.doc.id}`, {
headers,
method: 'delete',
});
expect(response.status).toBe(200);
const deleteData = await response.json();
expect(deleteData.id).toBe(doc.id);
});
it('should allow querying by custom ID', async () => {
const response = await fetch(`${url}/api/custom-id?where[id][equals]=${document.id}`, {
headers,
method: 'get',
});
const emptyResponse = await fetch(`${url}/api/custom-id?where[id][equals]=900`, {
headers,
method: 'get',
});
const result = await response.json();
const emptyResult = await emptyResponse.json();
expect(result.docs).toHaveLength(1);
expect(emptyResult.docs).toHaveLength(0);
});
});
});

View File

@@ -1,109 +0,0 @@
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = null;
describe('DefaultValue - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('DefaultValues', () => {
let document;
beforeAll(async (done) => {
const result = await fetch(`${url}/api/default-values`, {
body: JSON.stringify({}),
headers,
method: 'post',
});
const data = await result.json();
document = data.doc;
done();
});
it('should create with defaultValues saved', async () => {
expect(document.id).toBeDefined();
expect(document.function).toStrictEqual('function');
expect(document.asyncText).toStrictEqual('asyncFunction');
expect(document.array[0].arrayText1).toStrictEqual('Get out');
expect(document.group.nestedText1).toStrictEqual('this should take priority');
expect(document.group.nestedText2).toStrictEqual('nested default text 2');
expect(document.group.nestedText3).toStrictEqual('neat');
});
it('should not overwrite other locales when updating', async () => {
const slug = 'updated';
const esSlug = 'spanish';
const createResult = await fetch(`${url}/api/default-values`, {
body: JSON.stringify({
text: 'unique',
slug: 'unique',
}),
headers,
method: 'post',
});
const createData = await createResult.json();
const { id } = createData.doc;
const enResult = await fetch(`${url}/api/default-values/${id}?locale=en`, {
body: JSON.stringify({
slug,
}),
headers,
method: 'put',
});
const enData = await enResult.json();
const esResult = await fetch(`${url}/api/default-values/${id}?locale=es`, {
body: JSON.stringify({
slug: esSlug,
}),
headers,
method: 'put',
});
const esData = await esResult.json();
const allResult = await fetch(`${url}/api/default-values/${id}?locale=all`, {
headers,
method: 'get',
});
const allData = await allResult.json();
expect(createData.doc.slug).toStrictEqual('unique');
expect(enData.doc.slug).toStrictEqual(slug);
expect(esData.doc.slug).toStrictEqual(esSlug);
expect(allData.slug.en).toStrictEqual(slug);
expect(allData.slug.es).toStrictEqual(esSlug);
});
});
});

View File

@@ -1,73 +0,0 @@
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = null;
describe('Collections - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Endpoints', () => {
it('should GET a static endpoint', async () => {
const response = await fetch(`${url}/api/endpoints/say-hello/joe-bloggs`, {
headers: {
...headers,
},
method: 'get',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.message).toStrictEqual(`Hey Joey! Welcome to ${url}/api`);
});
it('should GET an endpoint with a parameter', async () => {
const response = await fetch(`${url}/api/endpoints/say-hello/George`, {
headers: {
...headers,
},
method: 'get',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.message).toStrictEqual('Hello George!');
});
it('should POST an endpoint with data', async () => {
const params = { name: 'George', age: 29 };
const response = await fetch(`${url}/api/endpoints/whoami`, {
body: JSON.stringify(params),
headers: {
...headers,
},
method: 'post',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.name).toStrictEqual(params.name);
expect(data.age).toStrictEqual(params.age);
});
});
});

View File

@@ -1,170 +0,0 @@
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = null;
describe('Collections - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Hooks', () => {
describe('Before', () => {
it('beforeChange', async () => {
const response = await fetch(`${url}/api/hooks`, {
body: JSON.stringify({
title: 'title 1',
description: 'Original',
priority: 1,
}),
headers: {
...headers,
hook: 'beforeChange', // Used by hook
},
method: 'post',
});
const data = await response.json();
expect(response.status).toBe(201);
expect(data.doc.description).toStrictEqual('Original-beforeChangeSuffix');
});
it('beforeDelete', async () => {
const createResponse = await fetch(`${url}/api/hooks`, {
body: JSON.stringify({
title: 'title 2',
description: 'Original',
priority: 1,
}),
headers,
method: 'post',
});
const createData = await createResponse.json();
const { id } = createData.doc;
const response = await fetch(`${url}/api/hooks/${id}`, {
headers: {
...headers,
hook: 'beforeDelete', // Used by hook
},
method: 'delete',
});
const data = await response.json();
expect(response.status).toBe(200);
// Intentionally afterDeleteHook - beforeDelete hook is setting header in order to trigger afterDelete hook
expect(data.afterDeleteHook).toStrictEqual(true);
});
});
describe('After', () => {
it('afterRead', async () => {
const response = await fetch(`${url}/api/hooks`, {
body: JSON.stringify({
title: 'title 3',
description: 'afterRead',
priority: 1,
}),
headers: {
...headers,
hook: 'afterRead', // Used by hook
},
method: 'post',
});
const data = await response.json();
const getResponse = await fetch(`${url}/api/hooks/${data.doc.id}`);
const getResponseData = await getResponse.json();
expect(getResponse.status).toBe(200);
expect(getResponseData.afterReadHook).toStrictEqual(true);
expect(getResponseData.findMany).toBeUndefined();
const getManyResponse = await fetch(`${url}/api/hooks`);
const getManyResponseData = await getManyResponse.json();
expect(getManyResponseData.docs[0].findMany).toStrictEqual(true);
});
it('afterChange', async () => {
const createResponse = await fetch(`${url}/api/hooks`, {
body: JSON.stringify({
title: 'title 4',
description: 'Original',
priority: 1,
}),
headers,
method: 'post',
});
const createData = await createResponse.json();
const { id } = createData.doc;
const response = await fetch(`${url}/api/hooks/${id}`, {
body: JSON.stringify({
description: 'afterChange',
}),
headers: {
...headers,
hook: 'afterChange', // Used by hook
},
method: 'put',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.doc.afterChangeHook).toStrictEqual(true);
});
it('afterDelete', async () => {
const createResponse = await fetch(`${url}/api/hooks`, {
body: JSON.stringify({
title: 'title 5',
description: 'Original',
priority: 1,
}),
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
method: 'post',
});
const createData = await createResponse.json();
const { id } = createData.doc;
const response = await fetch(`${url}/api/hooks/${id}`, {
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
hook: 'afterDelete', // Used by hook
},
method: 'delete',
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.afterDeleteHook).toStrictEqual(true);
});
});
});
});

View File

@@ -1,250 +0,0 @@
import { GraphQLClient } from 'graphql-request';
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL, routes } = getConfig();
let token = null;
let headers = null;
describe('GeoJSON', () => {
beforeAll(async (done) => {
const response = await fetch(`${serverURL}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Point Field - REST', () => {
let location = [10, 20];
const localizedPoint = [30, 40];
const group = { point: [15, 25] };
let doc;
beforeAll(async (done) => {
const create = await fetch(`${serverURL}/api/geolocation`, {
body: JSON.stringify({ location, localizedPoint, group }),
headers,
method: 'post',
});
({ doc } = await create.json());
done();
});
it('should create and read collections with points', async () => {
expect(doc.id).toBeDefined();
expect(doc.location).toStrictEqual(location);
expect(doc.localizedPoint).toStrictEqual(localizedPoint);
});
it('should query where near point', async () => {
const [lng, lat] = location;
const hitResponse = await fetch(`${serverURL}/api/geolocation?where[location][near]=${lng + 0.01},${lat + 0.01},10000`, {
headers,
method: 'get',
});
const hitData = await hitResponse.json();
const hitDocs = hitData.docs;
const missResponse = await fetch(`${serverURL}/api/geolocation?where[location][near]=-${lng},-${lat},5000`, {
headers,
method: 'get',
});
const missData = await missResponse.json();
const missDocs = missData.docs;
expect(hitDocs).toHaveLength(1);
expect(missDocs).toHaveLength(0);
});
it('should query where near localized point', async () => {
const [lng, lat] = localizedPoint;
const hitResponse = await fetch(`${serverURL}/api/geolocation?where[localizedPoint][near]=${lng + 0.01},${lat + 0.01},10000`, {
headers,
method: 'get',
});
const hitData = await hitResponse.json();
const hitDocs = hitData.docs;
const missResponse = await fetch(`${serverURL}/api/geolocation?where[localizedPoint][near]=-${lng},-${lat},5000`, {
headers,
method: 'get',
});
const missData = await missResponse.json();
const missDocs = missData.docs;
expect(hitDocs).toHaveLength(1);
expect(missDocs).toHaveLength(0);
});
it('should query near a nested point', async () => {
const [x, y] = group.point;
const hitResponse = await fetch(`${serverURL}/api/geolocation?where[group.point][near]=${x + 0.01},${y + 0.01},10000`, {
headers,
method: 'get',
});
const hitData = await hitResponse.json();
const hitDocs = hitData.docs;
const missResponse = await fetch(`${serverURL}/api/geolocation?where[group.point][near]=-${x},-${y},5000`, {
headers,
method: 'get',
});
const missData = await missResponse.json();
const missDocs = missData.docs;
expect(hitDocs).toHaveLength(1);
expect(missDocs).toHaveLength(0);
});
it('should save with non-required point', async () => {
location = undefined;
const create = await fetch(`${serverURL}/api/geolocation`, {
body: JSON.stringify({ location }),
headers,
method: 'post',
});
const { doc } = await create.json();
expect(doc.id).toBeDefined();
expect(doc.location).toStrictEqual(location);
});
});
describe('Point Field - GraphQL', () => {
const url = `${serverURL}${routes.api}${routes.graphQL}`;
let client = null;
const location = [50, 60];
const localizedPoint = [70, 80];
const group = { point: [50.5, 60.5] };
let doc;
beforeAll(async (done) => {
client = new GraphQLClient(url, { headers: { Authorization: `JWT ${token}` } });
// language=graphQL
const query = `mutation {
createGeolocation (
data: {
location: [${location[0]}, ${location[1]}],
localizedPoint: [${localizedPoint[0]}, ${localizedPoint[1]}],
group: {
point: [${group.point[0]}, ${group.point[1]}]
}
}
) {
id
location
localizedPoint
}
}`;
const response = await client.request(query);
const { id } = response.createGeolocation;
// language=graphQL
const readQuery = `query {
Geolocation(id: "${id}") {
id
location
localizedPoint
}
}`;
const readResponse = await client.request(readQuery);
doc = readResponse.Geolocation;
done();
});
it('should create and read collections with points', async () => {
expect(doc.id).toBeDefined();
expect(doc.location).toStrictEqual(location);
expect(doc.localizedPoint).toStrictEqual(localizedPoint);
});
it('should query where near point', async () => {
const [lng, lat] = location;
// language=graphQL
const hitQuery = `query getGeos {
Geolocations(where: { location: { near: [${lng + 0.01},${lat + 0.01},10000]}}) {
docs {
id
location
localizedPoint
}
}
}`;
const hitResponse = await client.request(hitQuery);
const hitDocs = hitResponse.Geolocations.docs;
const missQuery = `query getGeos {
Geolocations(where: { location: { near: [${-lng},${-lat},10000]}}) {
docs {
id
location
localizedPoint
}
}
}`;
const missResponse = await client.request(missQuery);
const missDocs = missResponse.Geolocations.docs;
expect(hitDocs).toHaveLength(1);
expect(missDocs).toHaveLength(0);
});
it('should query where near a point in a group', async () => {
const [x, y] = group.point;
// language=graphQL
const hitQuery = `query getGeos {
Geolocations(where: { group__point: { near: [${x + 0.01},${y + 0.01},10000]}}) {
docs {
id
group {
point
}
}
}
}`;
const hitResponse = await client.request(hitQuery);
const hitDocs = hitResponse.Geolocations.docs;
const missQuery = `query getGeos {
Geolocations(where: { group__point: { near: [${-x},${-y},10000]}}) {
docs {
id
group {
point
}
}
}
}`;
const missResponse = await client.request(missQuery);
const missDocs = missResponse.Geolocations.docs;
expect(hitDocs).toHaveLength(1);
expect(missDocs).toHaveLength(0);
});
});
});

View File

@@ -1,227 +0,0 @@
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
import { PaginatedDocs } from '../../mongoose/types';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = null;
type RelationshipA = {
id: string
post?: string | RelationshipB
}
type RelationshipB = {
id: string
post?: (string | RelationshipA)[]
}
describe('Collections - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Relationships', () => {
let documentA;
let documentB;
let strictAccessDoc;
beforeAll(async (done) => {
const strictAccessRes = await fetch(`${url}/api/strict-access`, {
body: JSON.stringify({
address: '123 Test Lane',
city: 'Grand Rapids',
state: 'MI',
zip: 49504,
}),
headers,
method: 'post',
});
const strictAccessJSON = await strictAccessRes.json();
strictAccessDoc = strictAccessJSON.doc;
// create document a
const createA = await fetch(`${url}/api/relationship-a`, {
body: JSON.stringify({}),
headers,
method: 'post',
});
const createAData = await createA.json();
// create document b, related to a
const createB = await fetch(`${url}/api/relationship-b`, {
body: JSON.stringify({
post: [createAData.doc.id],
strictAccess: strictAccessDoc.id,
}),
headers,
method: 'post',
});
// update a to relate to b
const createBData = await createB.json();
documentB = createBData.doc;
const updateA = await fetch(`${url}/api/relationship-a/${createAData.doc.id}`, {
body: JSON.stringify({
post: documentB.id,
postMaxDepth: documentB.id,
}),
headers,
method: 'put',
});
const updateAData = await updateA.json();
documentA = updateAData.doc;
done();
});
it('should create and read collections with relationships', async () => {
expect(documentA.post).toBeDefined();
expect(documentB.post).toHaveLength(1);
});
it('should prevent an unauthorized population of strict access', async () => {
const response = await fetch(`${url}/api/relationship-b/${documentB.id}`);
const data = await response.json();
expect(typeof data.strictAccess).not.toBe('object');
});
it('should populate strict access when authorized', async () => {
const response = await fetch(`${url}/api/relationship-b/${documentB.id}`, {
headers,
});
const data = await response.json();
expect(typeof data.strictAccess).toBe('object');
});
it('should use depth to limit the number of relationships returned', async () => {
const response = await fetch(`${url}/api/relationship-a?depth=3`, {
headers,
method: 'get',
});
const data: PaginatedDocs<RelationshipA> = await response.json();
const [depth0] = data.docs;
expect(depth0.id).toBe(documentA.id);
const depth1 = depth0.post as RelationshipB;
expect(depth1.id).toBe(documentB.id);
const [depth2] = depth1.post as RelationshipA[];
expect(depth2.id).toBe(documentA.id);
const depth3 = depth2.post as RelationshipB;
expect(depth3.id).toBe(documentB.id);
const [depth4] = depth3.post as RelationshipA[];
expect(depth4).not.toHaveProperty('post');
expect(depth4).toBe(documentA.id);
});
it('should respect max depth at the field level', async () => {
const response = await fetch(`${url}/api/relationship-a?depth=1`, {
headers,
method: 'get',
});
const data = await response.json();
const [doc] = data.docs;
// asserts postMaxDepth is not populated
expect(doc.postMaxDepth).toBe(documentB.id);
expect(doc.postMaxDepth).not.toHaveProperty('post');
});
it('should allow a custom id relation', async () => {
const customID = {
id: 30,
name: 'custom',
};
const newCustomID = await fetch(`${url}/api/custom-id`, {
headers,
body: JSON.stringify(customID),
method: 'post',
});
const custom = await newCustomID.json();
const response = await fetch(`${url}/api/relationship-a/${documentA.id}`, {
headers,
body: JSON.stringify({
...documentA,
post: documentB.id,
customID: [custom.doc.id],
}),
method: 'put',
});
const { doc } = await response.json();
expect(doc.customID[0].id).toBe(customID.id);
});
it('should allow a custom id relation and parse the id type', async () => {
const customID = {
id: '40',
name: 'custom',
};
const newCustomID = await fetch(`${url}/api/custom-id`, {
headers,
body: JSON.stringify(customID),
method: 'post',
});
const custom = await newCustomID.json();
const response = await fetch(`${url}/api/relationship-a/${documentA.id}`, {
headers,
body: JSON.stringify({
...documentA,
post: documentB.id,
customID: [custom.doc.id],
}),
method: 'put',
});
const { doc } = await response.json();
expect(custom.doc.id).toBe(parseFloat(customID.id));
expect(doc.customID[0].id).toBe(parseFloat(customID.id));
});
it('should use filterOptions to limit relationship options', async () => {
// update documentB to disable relations
await fetch(`${url}/api/relationship-b/${documentB.id}`, {
headers,
body: JSON.stringify({
disableRelation: true,
}),
method: 'put',
});
// attempt to save relationship to documentB
const response = await fetch(`${url}/api/relationship-a/${documentA.id}`, {
headers,
body: JSON.stringify({
filterRelationship: documentB.id,
}),
method: 'put',
});
const result = await response.json();
expect(result.errors).toBeDefined();
});
});
});

View File

@@ -1,81 +0,0 @@
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
let headers = null;
describe('Validations - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
headers = {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
};
done();
});
describe('Validations', () => {
let validation;
beforeAll(async (done) => {
const result = await fetch(`${url}/api/validations`, {
body: JSON.stringify({
validationOptions: 'ok',
text: 'test',
lessThan10: 9,
greaterThan10LessThan50: 20,
atLeast3Rows: [
{ greaterThan30: 40 },
{ greaterThan30: 50 },
{ greaterThan30: 60 },
],
array: [
{ lessThan20: 10 },
],
}),
headers,
method: 'post',
});
const data = await result.json();
validation = data.doc;
done();
});
it('should create with custom validation', async () => {
expect(validation.id).toBeDefined();
});
it('should update with custom validation', async () => {
const result = await fetch(`${url}/api/validations/${validation.id}`, {
body: JSON.stringify({
validationOptions: 'update',
}),
headers,
method: 'put',
});
const data = await result.json();
validation = data.doc;
expect(validation.validationOptions).toStrictEqual('update');
});
});
});

View File

@@ -1,112 +0,0 @@
import { Response } from 'express';
import Logger from '../../utilities/logger';
import errorHandler from './errorHandler';
import { APIError } from '../../errors';
import { PayloadRequest } from '../types';
import { SanitizedConfig } from '../../config/types';
const logger = Logger('payload');
const testError = new APIError('test error', 503);
describe('errorHandler', () => {
const res = generateResponse();
const next = jest.fn();
const req = generateRequest() as PayloadRequest;
it('should send the response with the error', async () => {
const handler = errorHandler(generateConfig({ debug: true }), logger);
await handler(testError, req, res, next);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({ errors: [{ message: 'test error' }] }),
);
});
it('should include stack trace when config debug is on', async () => {
const handler = errorHandler(generateConfig({ debug: true }), logger);
await handler(testError, req, res, next);
expect(res.send).toHaveBeenCalledWith(expect.objectContaining({ stack: expect.any(String) }));
});
it('should not include stack trace when config debug is not set', async () => {
const handler = errorHandler(generateConfig({ debug: undefined }), logger);
await handler(testError, req, res, next);
expect(res.send).toHaveBeenCalledWith(
expect.not.objectContaining({ stack: expect.any(String) }),
);
});
it('should not include stack trace when config debug is false', async () => {
const handler = errorHandler(generateConfig({ debug: false }), logger);
await handler(testError, req, res, next);
expect(res.send).toHaveBeenCalledWith(
expect.not.objectContaining({ stack: expect.any(String) }),
);
});
it('should show the status code when given an error with a code', async () => {
const handler = errorHandler(generateConfig(), logger);
await handler(testError, req, res, next);
expect(res.status).toHaveBeenCalledWith(503);
});
it('should default to 500 when an error does not have a status code', async () => {
const handler = errorHandler(generateConfig(), logger);
testError.status = undefined;
await handler(testError, req, res, next);
expect(res.status).toHaveBeenCalledWith(500);
});
it('should call payload config afterError hook', async () => {
const afterError = jest.fn();
const handler = errorHandler(
generateConfig({
hooks: { afterError },
}),
logger,
);
await handler(testError, req, res, next);
expect(afterError)
// eslint-disable-next-line jest/prefer-called-with
.toHaveBeenCalled();
});
it('should call collection config afterError hook', async () => {
const handler = errorHandler(generateConfig(), logger);
await handler(testError, req, res, next);
expect(req.collection.config.hooks.afterError)
// eslint-disable-next-line jest/prefer-called-with
.toHaveBeenCalled();
});
});
function generateResponse() {
const res = {
status: jest.fn(),
send: jest.fn(),
};
jest.spyOn(res, 'status').mockImplementation().mockReturnValue(res);
jest.spyOn(res, 'send').mockImplementation().mockReturnValue(res);
return res as unknown as Response;
}
function generateRequest(): PayloadRequest {
return {
collection: {
config: {
hooks: {
afterError: jest.fn(),
},
},
},
} as unknown as PayloadRequest;
}
function generateConfig(overrides?: Partial<SanitizedConfig>): SanitizedConfig {
return {
debug: false,
hooks: { afterError: jest.fn() },
...overrides,
} as unknown as SanitizedConfig;
}

View File

@@ -1,192 +0,0 @@
/**
* @jest-environment node
*/
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const { serverURL: url } = getConfig();
let token = null;
const navData = {
en: {
nav1: {
text: 'Navigation 1',
textarea: 'Some navigation text',
},
nav2: {
text: 'Navigation 2',
textarea: 'Some more navigation text',
},
},
es: {
nav1: {
text: 'Navegación 1',
textarea: 'algún texto de navegación',
},
nav2: {
text: 'Navegación 2',
textarea: 'un poco más de texto de navegación',
},
},
};
describe('Globals - REST', () => {
beforeAll(async (done) => {
const response = await fetch(`${url}/api/admins/login`, {
body: JSON.stringify({
email,
password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
const data = await response.json();
({ token } = data);
done();
});
describe('Create', () => {
it('should create one', async () => {
const response = await fetch(`${url}/api/globals/navigation-array`, {
body: JSON.stringify({
array: [
{
text: navData.en.nav1.text,
textarea: navData.en.nav1.textarea,
},
],
}),
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
method: 'post',
});
expect(response.status).toBe(200);
const data = await response.json();
const { text, textarea } = data.result.array[0];
expect(text).toStrictEqual(navData.en.nav1.text);
expect(textarea).toStrictEqual(navData.en.nav1.textarea);
});
});
describe('Update', () => {
it('should update one', async () => {
const response = await fetch(`${url}/api/globals/navigation-array`, {
body: JSON.stringify({
array: [
{
text: navData.en.nav1.text,
textarea: navData.en.nav1.textarea,
},
{
text: navData.en.nav2.text,
textarea: navData.en.nav2.textarea,
},
],
}),
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
method: 'post',
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data.result.array).toHaveLength(2);
const { text, textarea } = data.result.array[1];
expect(text).toStrictEqual(navData.en.nav2.text);
expect(textarea).toStrictEqual(navData.en.nav2.textarea);
});
it('should allow Spanish update', async () => {
const response = await fetch(`${url}/api/globals/navigation-array?locale=es`, {
body: JSON.stringify({
array: [
{
text: navData.es.nav1.text,
textarea: navData.es.nav1.textarea,
},
{
text: navData.es.nav2.text,
textarea: navData.es.nav2.textarea,
},
],
}),
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
method: 'post',
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data.result.array).toHaveLength(2);
const { text, textarea } = data.result.array[0];
expect(text).toStrictEqual(navData.es.nav1.text);
expect(textarea).toStrictEqual(navData.es.nav1.textarea);
});
});
describe('Read', () => {
it('should read one', async () => {
const response = await fetch(`${url}/api/globals/navigation-array`, {
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
});
expect(response.status).toBe(200);
const data = await response.json();
const { text, textarea } = data.array[0];
expect(text).toStrictEqual(navData.en.nav1.text);
expect(textarea).toStrictEqual(navData.en.nav1.textarea);
});
it('should read Spanish', async () => {
const response = await fetch(`${url}/api/globals/navigation-array?locale=es`, {
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
});
expect(response.status).toBe(200);
const data = await response.json();
const { text, textarea } = data.array[0];
expect(text).toStrictEqual(navData.es.nav1.text);
expect(textarea).toStrictEqual(navData.es.nav1.textarea);
});
});
describe('Endpoints', () => {
it('should respond with number of navigation items', async () => {
const response = await fetch(`${url}/api/globals/navigation-array/count`, {
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data.count).toStrictEqual(2);
});
});
});

View File

@@ -1,135 +0,0 @@
/**
* @jest-environment node
*/
import { request, GraphQLClient } from 'graphql-request';
import getConfig from '../../config/load';
import { email, password } from '../../mongoose/testCredentials';
require('isomorphic-fetch');
const config = getConfig();
const url = `${config.serverURL}${config.routes.api}${config.routes.graphQL}`;
let client = null;
let token = null;
describe('GrahpQL Preferences', () => {
beforeAll(async (done) => {
const query = `
mutation {
loginAdmin(
email: "${email}",
password: "${password}"
) {
token
}
}`;
const response = await request(url, query);
token = response.loginAdmin.token;
client = new GraphQLClient(url, { headers: { Authorization: `JWT ${token}` } });
done();
});
describe('Update', () => {
it('should allow a preference to be saved', async () => {
const key = 'preference-key';
const value = 'preference-value';
// language=graphQL
const query = `mutation {
updatePreference(key: "${key}", value: "${value}") {
key
value
}
}`;
const response = await client.request(query);
const data = response.updatePreference;
expect(data.key).toBe(key);
expect(data.value).toBe(value);
});
});
describe('Read', () => {
it('should be able to read user preference', async () => {
const key = 'preference-key';
const value = 'preference-value';
// language=graphQL
const query = `mutation {
updatePreference(key: "${key}", value: "${value}") {
key
value
}
}`;
const response = await client.request(query);
const { key: responseKey, value: responseValue } = response.updatePreference;
// language=graphQL
const readQuery = `query {
Preference(key: "${responseKey}") {
key
value
}
}`;
const readResponse = await client.request(readQuery);
expect(responseKey).toStrictEqual(key);
expect(readResponse.Preference.key).toStrictEqual(key);
expect(responseValue).toStrictEqual(value);
expect(readResponse.Preference.value).toStrictEqual(value);
});
});
describe('Delete', () => {
it('should be able to delete a preference', async () => {
const key = 'gql-delete';
const value = 'description';
// language=graphQL
const query = `mutation {
updatePreference(key: "${key}" value: "${value}") {
key
value
}
}`;
const response = await client.request(query);
const { key: responseKey } = response.updatePreference;
// language=graphQL
const deleteMutation = `mutation {
deletePreference(key: "${key}") {
key
value
}
}`;
const deleteResponse = await client.request(deleteMutation);
const deleteKey = deleteResponse.deletePreference.key;
expect(responseKey).toStrictEqual(key);
expect(deleteKey).toStrictEqual(key);
});
});
it('should return null when query key is not found', async () => {
const key = 'bad-key';
const readQuery = `query {
Preference(key: "${key}") {
key
value
}
}`;
const response = await client.request(readQuery);
expect(response.Preference).toBeNull();
});
});

View File

@@ -0,0 +1,3 @@
describe('array stuff', () => {
it.todo('should properly prevent / allow public users from reading a restricted field');
});

View File

@@ -87,6 +87,7 @@ describe('collections-rest', () => {
});
describe('Querying', () => {
it.todo('should allow querying by a field within a group');
describe('Relationships', () => {
let post: Post;
let relation: Relation;

View File

@@ -29,6 +29,21 @@ type FindArgs = {
slug?: string;
query?: Where;
auth?: boolean;
depth?: number
page?: number
limit?: number
};
type FindByIDArgs = {
id: string;
slug?: string;
query?: Where;
auth?: boolean;
options?: {
depth?: number
page?: number
limit?: number
},
};
type UpdateArgs<T = any> = {
@@ -133,14 +148,10 @@ export class RESTClient {
const options = {
headers: {
...headers,
Authorization: '',
Authorization: args?.auth !== false && this.token ? `JWT ${this.token}` : '',
},
};
if (args?.auth) {
options.headers.Authorization = `JWT ${this.token}`;
}
const slug = args?.slug || this.defaultSlug;
const whereQuery = qs.stringify(args?.query ? { where: args.query } : {}, {
addQueryPrefix: true,
@@ -169,9 +180,18 @@ export class RESTClient {
return { status, doc: json.doc };
}
async findByID<T = any>(id: string, args?: { slug?: string }): Promise<DocResponse<T>> {
const response = await fetch(`${this.serverURL}/api/${args?.slug || this.defaultSlug}/${id}`, {
headers,
async findByID<T = any>(args: FindByIDArgs): Promise<DocResponse<T>> {
const options = {
headers: {
...headers,
Authorization: args?.auth !== false && this.token ? `JWT ${this.token}` : '',
},
};
const formattedOpts = qs.stringify(args?.options || {}, { addQueryPrefix: true });
const fetchURL = `${this.serverURL}/api/${args?.slug || this.defaultSlug}/${args.id}${formattedOpts}`;
const response = await fetch(fetchURL, {
headers: options.headers,
method: 'get',
});
const { status } = response;