diff --git a/src/admin/components/utilities/Config/index.tsx b/src/admin/components/utilities/Config/index.tsx index 73eb27ed23..f21593df49 100644 --- a/src/admin/components/utilities/Config/index.tsx +++ b/src/admin/components/utilities/Config/index.tsx @@ -3,10 +3,29 @@ import { SanitizedConfig } from '../../../../config/types'; const Context = createContext({} as SanitizedConfig); -export const ConfigProvider: React.FC<{config: SanitizedConfig, children: React.ReactNode}> = ({ children, config }) => ( - - {children} - -); +export const ConfigProvider: React.FC<{config: SanitizedConfig, children: React.ReactNode}> = ({ children, config: incomingConfig }) => { + const [config, setConfig] = React.useState(); + const hasAwaited = React.useRef(false); + + React.useEffect(() => { + if (incomingConfig && !hasAwaited.current) { + hasAwaited.current = true; + + const awaitConfig = async () => { + const resolvedConfig = await incomingConfig; + setConfig(resolvedConfig); + }; + awaitConfig(); + } + }, [incomingConfig]); + + if (!config) return null; + + return ( + + {children} + + ); +}; export const useConfig = (): SanitizedConfig => useContext(Context); diff --git a/src/bin/build.ts b/src/bin/build.ts index d9940ea09c..7271485757 100755 --- a/src/bin/build.ts +++ b/src/bin/build.ts @@ -9,7 +9,7 @@ const rawConfigPath = findConfig(); export const build = async (): Promise => { try { - const config = loadConfig(); + const config = await loadConfig(); const webpackProdConfig = getWebpackProdConfig(config); diff --git a/src/bin/generateGraphQLSchema.ts b/src/bin/generateGraphQLSchema.ts index 5c4ae8a5f0..808b01a0a3 100644 --- a/src/bin/generateGraphQLSchema.ts +++ b/src/bin/generateGraphQLSchema.ts @@ -7,7 +7,7 @@ import payload from '..'; export async function generateGraphQLSchema(): Promise { const logger = Logger(); - const config = loadConfig(); + const config = await loadConfig(); await payload.init({ secret: '--unused--', diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index b90c640708..c45f4d4d60 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -32,9 +32,9 @@ function configToJsonSchema(config: SanitizedConfig): JSONSchema4 { }; } -export function generateTypes(): void { +export async function generateTypes(): Promise { const logger = Logger(); - const config = loadConfig(); + const config = await loadConfig(); const outputFile = process.env.PAYLOAD_TS_OUTPUT_PATH || config.typescript.outputFile; logger.info('Compiling TS types for Collections and Globals...'); diff --git a/src/config/build.ts b/src/config/build.ts index 4327eac466..19576f5ed3 100644 --- a/src/config/build.ts +++ b/src/config/build.ts @@ -8,14 +8,17 @@ import sanitize from './sanitize'; * @param config Payload Config * @returns Built and sanitized Payload Config */ -export function buildConfig(config: Config): SanitizedConfig { +export async function buildConfig(config: Config): Promise { if (Array.isArray(config.plugins)) { - const configWithPlugins = config.plugins.reduce( - (updatedConfig, plugin) => plugin(updatedConfig), - config, + const configAfterPlugins = await config.plugins.reduce( + async (acc, plugin) => { + const configAfterPlugin = await acc; + return plugin(configAfterPlugin); + }, + Promise.resolve(config), ); - const sanitizedConfig = sanitize(configWithPlugins); + const sanitizedConfig = sanitize(configAfterPlugins); return sanitizedConfig; } diff --git a/src/config/load.ts b/src/config/load.ts index c55ba97710..fe7f183e69 100644 --- a/src/config/load.ts +++ b/src/config/load.ts @@ -9,7 +9,7 @@ import findConfig from './find'; import validate from './validate'; import { clientFiles } from './clientFiles'; -const loadConfig = (logger?: pino.Logger): SanitizedConfig => { +const loadConfig = async (logger?: pino.Logger): Promise => { const localLogger = logger ?? Logger(); const configPath = findConfig(); @@ -19,18 +19,18 @@ const loadConfig = (logger?: pino.Logger): SanitizedConfig => { }); // eslint-disable-next-line @typescript-eslint/no-var-requires - let config = require(configPath); + const configPromise = require(configPath); - if (config.default) config = config.default; + let config = await configPromise; - let validatedConfig = config; + if (config.default) config = await config.default; if (process.env.NODE_ENV !== 'production') { - validatedConfig = validate(config, localLogger); + config = await validate(config, localLogger); } return { - ...validatedConfig, + ...config, paths: { configDir: path.dirname(configPath), config: configPath, diff --git a/src/config/types.ts b/src/config/types.ts index 5a490f4a25..e5034b2c1b 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -27,7 +27,7 @@ type Email = { }; // eslint-disable-next-line no-use-before-define -export type Plugin = (config: Config) => Config; +export type Plugin = (config: Config) => Promise | Config; type GeneratePreviewURLOptions = { locale: string; diff --git a/src/config/validate.ts b/src/config/validate.ts index b5d0e0723c..99fac849e5 100644 --- a/src/config/validate.ts +++ b/src/config/validate.ts @@ -32,7 +32,7 @@ const validateFields = (context: string, entity: SanitizedCollectionConfig | San return errors; }; -const validateCollections = (collections: SanitizedCollectionConfig[]): string[] => { +const validateCollections = async (collections: SanitizedCollectionConfig[]): Promise => { const errors: string[] = []; collections.forEach((collection) => { const result = collectionSchema.validate(collection, { abortEarly: false }); @@ -62,13 +62,13 @@ const validateGlobals = (globals: SanitizedGlobalConfig[]): string[] => { return errors; }; -const validateSchema = (config: SanitizedConfig, logger: Logger): SanitizedConfig => { +const validateSchema = async (config: SanitizedConfig, logger: Logger): Promise => { const result = schema.validate(config, { abortEarly: false, }); const nestedErrors = [ - ...validateCollections(config.collections), + ...await validateCollections(config.collections), ...validateGlobals(config.globals), ]; diff --git a/src/payload.ts b/src/payload.ts index 1b1fddbaec..26d9e2f382 100644 --- a/src/payload.ts +++ b/src/payload.ts @@ -181,7 +181,7 @@ export class BasePayload { } else { // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require const loadConfig = require('./config/load').default; - this.config = loadConfig(this.logger); + this.config = await loadConfig(this.logger); } // Configure email service diff --git a/test/access-control/e2e.spec.ts b/test/access-control/e2e.spec.ts index 628a678b16..62e5c9529b 100644 --- a/test/access-control/e2e.spec.ts +++ b/test/access-control/e2e.spec.ts @@ -76,7 +76,7 @@ describe('access control', () => { let existingDoc: ReadOnlyCollection; beforeAll(async () => { - existingDoc = await payload.create({ + existingDoc = await payload.create({ collection: readOnlySlug, data: { name: 'name', @@ -114,7 +114,7 @@ describe('access control', () => { let existingDoc: ReadOnlyCollection; beforeAll(async () => { - existingDoc = await payload.create({ + existingDoc = await payload.create({ collection: readOnlySlug, data: { name: 'name', @@ -162,7 +162,7 @@ describe('access control', () => { let existingDoc: RestrictedVersion; beforeAll(async () => { - existingDoc = await payload.create({ + existingDoc = await payload.create({ collection: restrictedVersionsSlug, data: { name: 'name', @@ -172,7 +172,7 @@ describe('access control', () => { test('versions sidebar should not show', async () => { await page.goto(restrictedVersionsUrl.edit(existingDoc.id)); - await expect(page.locator('.versions-count')).not.toBeVisible(); + await expect(page.locator('.versions-count')).toBeHidden(); }); }); diff --git a/test/access-control/int.spec.ts b/test/access-control/int.spec.ts index 391d3fe3e0..fd337dfa86 100644 --- a/test/access-control/int.spec.ts +++ b/test/access-control/int.spec.ts @@ -5,7 +5,7 @@ import { Forbidden } from '../../src/errors'; import type { PayloadRequest } from '../../src/types'; import { initPayloadTest } from '../helpers/configHelpers'; import { relyOnRequestHeadersSlug, requestHeaders, restrictedSlug, siblingDataSlug, slug } from './config'; -import type { Restricted, Post, SiblingDatum, RelyOnRequestHeader } from './payload-types'; +import type { Restricted, Post, RelyOnRequestHeader } from './payload-types'; import { firstArrayText, secondArrayText } from './shared'; describe('Access Control', () => { @@ -17,12 +17,12 @@ describe('Access Control', () => { }); beforeEach(async () => { - post1 = await payload.create({ + post1 = await payload.create({ collection: slug, data: { name: 'name' }, }); - restricted = await payload.create({ + restricted = await payload.create({ collection: restrictedSlug, data: { name: 'restricted' }, }); @@ -37,7 +37,7 @@ describe('Access Control', () => { it.todo('should properly prevent / allow public users from reading a restricted field'); it('should be able to restrict access based upon siblingData', async () => { - const { id } = await payload.create({ + const { id } = await payload.create({ collection: siblingDataSlug, data: { array: [ @@ -53,7 +53,7 @@ describe('Access Control', () => { }, }); - const doc = await payload.findByID({ + const doc = await payload.findByID({ id, collection: siblingDataSlug, overrideAccess: false, @@ -64,7 +64,7 @@ describe('Access Control', () => { expect(doc.array?.[1].text).toBeUndefined(); // Retrieve with default of overriding access - const docOverride = await payload.findByID({ + const docOverride = await payload.findByID({ id, collection: siblingDataSlug, }); @@ -150,7 +150,7 @@ describe('Access Control', () => { describe('Override Access', () => { describe('Fields', () => { it('should allow overrideAccess: false', async () => { - const req = async () => payload.update({ + const req = async () => payload.update({ collection: slug, id: post1.id, data: { restrictedField: restricted.id }, @@ -161,7 +161,7 @@ describe('Access Control', () => { }); it('should allow overrideAccess: true', async () => { - const doc = await payload.update({ + const doc = await payload.update({ collection: slug, id: post1.id, data: { restrictedField: restricted.id }, @@ -172,7 +172,7 @@ describe('Access Control', () => { }); it('should allow overrideAccess by default', async () => { - const doc = await payload.update({ + const doc = await payload.update({ collection: slug, id: post1.id, data: { restrictedField: restricted.id }, @@ -221,7 +221,7 @@ describe('Access Control', () => { }); async function createDoc(data: Partial, overrideSlug = slug, options?: Partial>): Promise { - return payload.create({ + return payload.create({ ...options, collection: overrideSlug, data: data ?? {}, diff --git a/test/array-update/int.spec.ts b/test/array-update/int.spec.ts index 5670a96b70..ce1f4978b4 100644 --- a/test/array-update/int.spec.ts +++ b/test/array-update/int.spec.ts @@ -1,13 +1,14 @@ import mongoose from 'mongoose'; import { initPayloadTest } from '../helpers/configHelpers'; import payload from '../../src'; -import config from './config'; -import type { Array as ArrayCollection } from './payload-types'; +import configPromise from './config'; -const collection = config.collections[0]?.slug; +let collection: string; describe('array-update', () => { beforeAll(async () => { + const config = await configPromise; + collection = config.collections[0]?.slug; await initPayloadTest({ __dirname }); }); @@ -45,7 +46,7 @@ describe('array-update', () => { required: updatedText, }; - const updatedDoc = await payload.update({ + const updatedDoc = await payload.update({ id: doc.id, collection, data: { @@ -67,7 +68,7 @@ describe('array-update', () => { optional: 'optional test', }; - const doc = await payload.create({ + const doc = await payload.create({ collection, data: { array: [ @@ -80,7 +81,7 @@ describe('array-update', () => { }, }); - const updatedDoc = await payload.update({ + const updatedDoc = await payload.update({ id: doc.id, collection, data: { diff --git a/test/buildConfig.ts b/test/buildConfig.ts index 038f3dca03..166275b57c 100644 --- a/test/buildConfig.ts +++ b/test/buildConfig.ts @@ -1,7 +1,7 @@ import { Config, SanitizedConfig } from '../src/config/types'; import { buildConfig as buildPayloadConfig } from '../src/config/build'; -export function buildConfig(config?: Partial): SanitizedConfig { +export function buildConfig(config?: Partial): Promise { const [name] = process.argv.slice(2); const baseConfig: Config = { telemetry: false, diff --git a/test/collections-graphql/config.ts b/test/collections-graphql/config.ts index 5bcd50bd80..89dc56a128 100644 --- a/test/collections-graphql/config.ts +++ b/test/collections-graphql/config.ts @@ -1,7 +1,6 @@ import type { CollectionConfig } from '../../src/collections/config/types'; import { devUser } from '../credentials'; import { buildConfig } from '../buildConfig'; -import type { Post } from './payload-types'; export interface Relation { id: string; @@ -88,20 +87,20 @@ export default buildConfig({ }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'post1', }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'post2', }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'with-description', @@ -109,14 +108,14 @@ export default buildConfig({ }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'numPost1', number: 1, }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'numPost2', @@ -124,13 +123,13 @@ export default buildConfig({ }, }); - const rel1 = await payload.create({ + const rel1 = await payload.create({ collection: relationSlug, data: { name: 'name', }, }); - const rel2 = await payload.create({ + const rel2 = await payload.create({ collection: relationSlug, data: { name: 'name2', @@ -138,14 +137,14 @@ export default buildConfig({ }); // Relation - hasMany - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to hasMany', relationHasManyField: rel1.id, }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to hasMany 2', @@ -154,7 +153,7 @@ export default buildConfig({ }); // Relation - relationTo multi - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to multi', @@ -166,7 +165,7 @@ export default buildConfig({ }); // Relation - relationTo multi hasMany - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to multi hasMany', diff --git a/test/collections-graphql/int.spec.ts b/test/collections-graphql/int.spec.ts index 9ab9d84cad..8ba8cbe3c1 100644 --- a/test/collections-graphql/int.spec.ts +++ b/test/collections-graphql/int.spec.ts @@ -1,11 +1,11 @@ import mongoose from 'mongoose'; import { GraphQLClient } from 'graphql-request'; import { initPayloadTest } from '../helpers/configHelpers'; -import config from './config'; +import configPromise from './config'; import payload from '../../src'; import type { Post } from './payload-types'; -const slug = config.collections[0]?.slug; +let slug = ''; const title = 'title'; let client: GraphQLClient; @@ -13,6 +13,8 @@ let client: GraphQLClient; describe('collections-graphql', () => { beforeAll(async () => { const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }); + const config = await configPromise; + slug = config.collections[0]?.slug; const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`; client = new GraphQLClient(url); }); @@ -354,7 +356,7 @@ describe('collections-graphql', () => { }); async function createPost(overrides?: Partial) { - const doc = await payload.create({ + const doc = await payload.create({ collection: slug, data: { title: 'title', ...overrides }, }); diff --git a/test/collections-rest/config.ts b/test/collections-rest/config.ts index f541effc81..ab20a7bd96 100644 --- a/test/collections-rest/config.ts +++ b/test/collections-rest/config.ts @@ -1,7 +1,6 @@ import type { CollectionConfig } from '../../src/collections/config/types'; import { devUser } from '../credentials'; import { buildConfig } from '../buildConfig'; -import type { Post } from './payload-types'; export interface Relation { id: string; @@ -135,13 +134,13 @@ export default buildConfig({ }, }); - const rel1 = await payload.create({ + const rel1 = await payload.create({ collection: relationSlug, data: { name: 'name', }, }); - const rel2 = await payload.create({ + const rel2 = await payload.create({ collection: relationSlug, data: { name: 'name2', @@ -156,14 +155,14 @@ export default buildConfig({ }); // Relation - hasMany - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to hasMany', relationHasManyField: rel1.id, }, }); - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to hasMany 2', @@ -172,7 +171,7 @@ export default buildConfig({ }); // Relation - relationTo multi - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to multi', @@ -184,7 +183,7 @@ export default buildConfig({ }); // Relation - relationTo multi hasMany - await payload.create({ + await payload.create({ collection: slug, data: { title: 'rel to multi hasMany', diff --git a/test/fields/int.spec.ts b/test/fields/int.spec.ts index 1b07b458a2..4dac81557a 100644 --- a/test/fields/int.spec.ts +++ b/test/fields/int.spec.ts @@ -1,10 +1,9 @@ import type { IndexDirection, IndexOptions } from 'mongoose'; import { initPayloadTest } from '../helpers/configHelpers'; import { RESTClient } from '../helpers/rest'; -import config from '../uploads/config'; +import configPromise from '../uploads/config'; import payload from '../../src'; import { pointDoc } from './collections/Point'; -import type { ArrayField, GroupField, TabsField } from './payload-types'; import { arrayFieldsSlug, arrayDefaultValue, arrayDoc } from './collections/Array'; import { groupFieldsSlug, groupDefaultChild, groupDefaultValue, groupDoc } from './collections/Group'; import { defaultText } from './collections/Text'; @@ -17,6 +16,7 @@ let client; describe('Fields', () => { beforeAll(async () => { const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }); + const config = await configPromise; client = new RESTClient(config, { serverURL, defaultSlug: 'point-fields' }); await client.login(); }); @@ -268,14 +268,14 @@ describe('Fields', () => { const collection = arrayFieldsSlug; beforeAll(async () => { - doc = await payload.create({ + doc = await payload.create({ collection, data: {}, }); }); it('should return undefined arrays when no data present', async () => { - const document = await payload.create({ + const document = await payload.create({ collection: arrayFieldsSlug, data: arrayDoc, }); @@ -284,7 +284,7 @@ describe('Fields', () => { }); it('should create with ids and nested ids', async () => { - const docWithIDs = await payload.create({ + const docWithIDs = await payload.create({ collection: groupFieldsSlug, data: groupDoc, }); @@ -300,14 +300,14 @@ describe('Fields', () => { const localized = [{ text: 'unique' }]; const enText = 'english'; const esText = 'spanish'; - const { id } = await payload.create({ + const { id } = await payload.create({ collection, data: { localized, }, }); - const enDoc = await payload.update({ + const enDoc = await payload.update({ collection, id, locale: 'en', @@ -316,7 +316,7 @@ describe('Fields', () => { }, }); - const esDoc = await payload.update({ + const esDoc = await payload.update({ collection, id, locale: 'es', @@ -342,7 +342,7 @@ describe('Fields', () => { let document; beforeAll(async () => { - document = await payload.create({ + document = await payload.create({ collection: groupFieldsSlug, data: {}, }); @@ -358,7 +358,7 @@ describe('Fields', () => { let document; beforeAll(async () => { - document = await payload.create({ + document = await payload.create({ collection: tabsSlug, data: tabsDoc, }); @@ -395,7 +395,7 @@ describe('Fields', () => { }); it('should allow hooks on a named tab', async () => { - const newDocument = await payload.create({ + const newDocument = await payload.create({ collection: tabsSlug, data: tabsDoc, }); @@ -406,7 +406,7 @@ describe('Fields', () => { }); it('should return empty object for groups when no data present', async () => { - const doc = await payload.create({ + const doc = await payload.create({ collection: groupFieldsSlug, data: groupDoc, }); diff --git a/test/globals/int.spec.ts b/test/globals/int.spec.ts index 2a43ffefee..682dbae773 100644 --- a/test/globals/int.spec.ts +++ b/test/globals/int.spec.ts @@ -1,6 +1,6 @@ import { GraphQLClient } from 'graphql-request'; import { initPayloadTest } from '../helpers/configHelpers'; -import config, { arraySlug, englishLocale, slug, spanishLocale } from './config'; +import configPromise, { arraySlug, englishLocale, slug, spanishLocale } from './config'; import payload from '../../src'; import { RESTClient } from '../helpers/rest'; @@ -13,6 +13,7 @@ describe('globals', () => { describe('REST', () => { let client: RESTClient; beforeAll(async () => { + const config = await configPromise; client = new RESTClient(config, { serverURL, defaultSlug: slug }); }); it('should create', async () => { @@ -135,6 +136,7 @@ describe('globals', () => { describe('graphql', () => { let client: GraphQLClient; beforeAll(async () => { + const config = await configPromise; const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`; client = new GraphQLClient(url); }); diff --git a/test/helpers/rest.ts b/test/helpers/rest.ts index 15b146c8b6..9b936c12bb 100644 --- a/test/helpers/rest.ts +++ b/test/helpers/rest.ts @@ -258,7 +258,7 @@ export class RESTClient { return { status, doc: result }; } - async endpoint(path: string, method = 'get', params = undefined): Promise<{ status: number, data: T }> { + async endpoint(path: string, method = 'get', params: any = undefined): Promise<{ status: number, data: T }> { const response = await fetch(`${this.serverURL}${path}`, { headers: { 'Content-Type': 'application/json', diff --git a/test/localization/int.spec.ts b/test/localization/int.spec.ts index a97422ffce..508c25302a 100644 --- a/test/localization/int.spec.ts +++ b/test/localization/int.spec.ts @@ -10,7 +10,7 @@ import type { GlobalArray, } from './payload-types'; import type { LocalizedPostAllLocale } from './config'; -import config, { relationshipLocalizedSlug, slug, withLocalizedRelSlug, withRequiredLocalizedFields } from './config'; +import configPromise, { relationshipLocalizedSlug, slug, withLocalizedRelSlug, withRequiredLocalizedFields } from './config'; import { defaultLocale, englishTitle, @@ -23,8 +23,10 @@ import { } from './shared'; import type { Where } from '../../src/types'; import { arrayCollectionSlug } from './collections/Array'; +import type { Config } from '../../src/config/types'; const collection = slug; +let config: Config; let serverURL; @@ -34,6 +36,7 @@ describe('Localization', () => { beforeAll(async () => { ({ serverURL } = await initPayloadTest({ __dirname, init: { local: false } })); + config = await configPromise; post1 = await payload.create({ collection, @@ -49,7 +52,7 @@ describe('Localization', () => { }, }); - await payload.update({ + await payload.update({ collection, id: postWithLocalizedData.id, locale: spanishLocale, @@ -67,7 +70,7 @@ describe('Localization', () => { describe('localized text', () => { it('create english', async () => { - const allDocs = await payload.find({ + const allDocs = await payload.find({ collection, where: { title: { equals: post1.title }, @@ -77,7 +80,7 @@ describe('Localization', () => { }); it('add spanish translation', async () => { - const updated = await payload.update({ + const updated = await payload.update({ collection, id: post1.id, locale: spanishLocale, @@ -88,7 +91,7 @@ describe('Localization', () => { expect(updated.title).toEqual(spanishTitle); - const localized = await payload.findByID({ + const localized = await payload.findByID({ collection, id: post1.id, locale: 'all', @@ -99,7 +102,7 @@ describe('Localization', () => { }); it('should fallback to english translation when empty', async () => { - const updated = await payload.update({ + const updated = await payload.update({ collection, id: post1.id, locale: spanishLocale, @@ -110,7 +113,7 @@ describe('Localization', () => { expect(updated.title).toEqual(englishTitle); - const localizedFallback = await payload.findByID({ + const localizedFallback = await payload.findByID({ collection, id: post1.id, locale: 'all', @@ -123,14 +126,14 @@ describe('Localization', () => { describe('querying', () => { let localizedPost: LocalizedPost; beforeEach(async () => { - const { id } = await payload.create({ + const { id } = await payload.create({ collection, data: { title: englishTitle, }, }); - localizedPost = await payload.update({ + localizedPost = await payload.update({ collection, id, locale: spanishLocale, @@ -170,7 +173,7 @@ describe('Localization', () => { }); it('all locales', async () => { - const localized = await payload.findByID({ + const localized = await payload.findByID({ collection, locale: 'all', id: localizedPost.id, @@ -181,7 +184,7 @@ describe('Localization', () => { }); it('by localized field value - default locale', async () => { - const result = await payload.find({ + const result = await payload.find({ collection, where: { title: { @@ -194,7 +197,7 @@ describe('Localization', () => { }); it('by localized field value - alternate locale', async () => { - const result = await payload.find({ + const result = await payload.find({ collection, locale: spanishLocale, where: { @@ -208,7 +211,7 @@ describe('Localization', () => { }); it('by localized field value - opposite locale???', async () => { - const result = await payload.find({ + const result = await payload.find({ collection, locale: 'all', where: { @@ -258,7 +261,7 @@ describe('Localization', () => { describe('regular relationship', () => { it('can query localized relationship', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationship.title': { @@ -271,7 +274,7 @@ describe('Localization', () => { }); it('specific locale', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, locale: spanishLocale, where: { @@ -285,7 +288,7 @@ describe('Localization', () => { }); it('all locales', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, locale: 'all', where: { @@ -300,7 +303,7 @@ describe('Localization', () => { it('populates relationships with all locales', async () => { // the relationship fields themselves are localized on this collection - const result = await payload.find({ + const result = await payload.find({ collection: relationshipLocalizedSlug, locale: 'all', depth: 1, @@ -314,7 +317,7 @@ describe('Localization', () => { describe('relationship - hasMany', () => { it('default locale', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationHasManyField.title': { @@ -326,7 +329,7 @@ describe('Localization', () => { expect(result.docs[0].id).toEqual(withRelationship.id); // Second relationship - const result2 = await payload.find({ + const result2 = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationHasManyField.title': { @@ -339,7 +342,7 @@ describe('Localization', () => { }); it('specific locale', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, locale: spanishLocale, where: { @@ -352,7 +355,7 @@ describe('Localization', () => { expect(result.docs[0].id).toEqual(withRelationship.id); // Second relationship - const result2 = await payload.find({ + const result2 = await payload.find({ collection: withLocalizedRelSlug, locale: spanishLocale, where: { @@ -366,7 +369,7 @@ describe('Localization', () => { }); it('relationship population uses locale', async () => { - const result = await payload.findByID({ + const result = await payload.findByID({ collection: withLocalizedRelSlug, depth: 1, id: withRelationship.id, @@ -377,7 +380,7 @@ describe('Localization', () => { it('all locales', async () => { const queryRelation = (where: Where) => { - return payload.find({ + return payload.find({ collection: withLocalizedRelSlug, locale: 'all', where, @@ -423,7 +426,7 @@ describe('Localization', () => { describe('relationTo multi', () => { it('by id', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationMultiRelationTo.value': { @@ -435,7 +438,7 @@ describe('Localization', () => { expect(result.docs[0].id).toEqual(withRelationship.id); // Second relationship - const result2 = await payload.find({ + const result2 = await payload.find({ collection: withLocalizedRelSlug, locale: spanishLocale, where: { @@ -451,7 +454,7 @@ describe('Localization', () => { describe('relationTo multi hasMany', () => { it('by id', async () => { - const result = await payload.find({ + const result = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationMultiRelationToHasMany.value': { @@ -463,7 +466,7 @@ describe('Localization', () => { expect(result.docs[0].id).toEqual(withRelationship.id); // First relationship - spanish locale - const result2 = await payload.find({ + const result2 = await payload.find({ collection: withLocalizedRelSlug, locale: spanishLocale, where: { @@ -476,7 +479,7 @@ describe('Localization', () => { expect(result2.docs[0].id).toEqual(withRelationship.id); // Second relationship - const result3 = await payload.find({ + const result3 = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationMultiRelationToHasMany.value': { @@ -488,7 +491,7 @@ describe('Localization', () => { expect(result3.docs[0].id).toEqual(withRelationship.id); // Second relationship - spanish locale - const result4 = await payload.find({ + const result4 = await payload.find({ collection: withLocalizedRelSlug, where: { 'localizedRelationMultiRelationToHasMany.value': { @@ -504,7 +507,7 @@ describe('Localization', () => { describe('Localized - arrays with nested localized fields', () => { it('should allow moving rows and retain existing row locale data', async () => { - const globalArray = await payload.findGlobal({ + const globalArray = await payload.findGlobal({ slug: 'global-array', }); @@ -553,7 +556,7 @@ describe('Localization', () => { }, }); - const updatedDoc = await payload.update({ + const updatedDoc = await payload.update({ collection: withRequiredLocalizedFields, id: newDoc.id, data: { @@ -577,7 +580,7 @@ describe('Localization', () => { let token; it('should allow user to login and retrieve populated localized field', async () => { - const url = `${serverURL}${config.routes.api}${config.routes.graphQL}?locale=en`; + const url = `${serverURL}${config?.routes?.api}${config?.routes?.graphQL}?locale=en`; const client = new GraphQLClient(url); const query = `mutation { @@ -602,7 +605,7 @@ describe('Localization', () => { it('should allow retrieval of populated localized fields within meUser', async () => { // Defining locale=en in graphQL string should not break JWT strategy - const url = `${serverURL}${config.routes.api}${config.routes.graphQL}?locale=en`; + const url = `${serverURL}${config?.routes?.api}${config?.routes?.graphQL}?locale=en`; const client = new GraphQLClient(url); const query = `query { @@ -626,7 +629,7 @@ describe('Localization', () => { }); it('should create and update collections', async () => { - const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`; + const url = `${serverURL}${config?.routes?.api}${config?.routes?.graphQL}`; const client = new GraphQLClient(url); const create = `mutation { @@ -749,14 +752,14 @@ async function createLocalizedPost(data: { [spanishLocale]: string; }; }): Promise { - const localizedRelation = await payload.create({ + const localizedRelation = await payload.create({ collection, data: { title: data.title.en, }, }); - await payload.update({ + await payload.update({ collection, id: localizedRelation.id, locale: spanishLocale, diff --git a/test/plugins/.gitignore b/test/plugins/.gitignore new file mode 100644 index 0000000000..3454702ee9 --- /dev/null +++ b/test/plugins/.gitignore @@ -0,0 +1 @@ +media diff --git a/test/plugins/config.ts b/test/plugins/config.ts new file mode 100644 index 0000000000..7f0f417d08 --- /dev/null +++ b/test/plugins/config.ts @@ -0,0 +1,40 @@ +import { buildConfig } from '../buildConfig'; +import { devUser } from '../credentials'; + +export const pagesSlug = 'pages'; + +export default buildConfig({ + collections: [ + { + slug: 'users', + auth: true, + fields: [], + }, + ], + plugins: [ + async (config) => ({ + ...config, + collections: [ + ...config.collections || [], + { + slug: pagesSlug, + fields: [ + { + name: 'title', + type: 'text', + }, + ], + }, + ], + }), + ], + onInit: async (payload) => { + await payload.create({ + collection: 'users', + data: { + email: devUser.email, + password: devUser.password, + }, + }); + }, +}); diff --git a/test/plugins/e2e.todo-spec.ts b/test/plugins/e2e.todo-spec.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/plugins/int.spec.ts b/test/plugins/int.spec.ts new file mode 100644 index 0000000000..561b3cdef8 --- /dev/null +++ b/test/plugins/int.spec.ts @@ -0,0 +1,28 @@ +import { initPayloadTest } from '../helpers/configHelpers'; +import { RESTClient } from '../helpers/rest'; +import configPromise, { pagesSlug } from './config'; +import payload from '../../src'; + +require('isomorphic-fetch'); + +let client; + +describe('Collections - Plugins', () => { + beforeAll(async () => { + const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }); + const config = await configPromise; + client = new RESTClient(config, { serverURL, defaultSlug: pagesSlug }); + await client.login(); + }); + + it('created pages collection', async () => { + const { id } = await payload.create({ + collection: pagesSlug, + data: { + title: 'Test Page', + }, + }); + + expect(id).toEqual(expect.any(String)); + }); +}); diff --git a/test/versions/int.spec.ts b/test/versions/int.spec.ts index be6cc57fd1..3eb3878b1d 100644 --- a/test/versions/int.spec.ts +++ b/test/versions/int.spec.ts @@ -1,7 +1,7 @@ import { request, GraphQLClient } from 'graphql-request'; import { initPayloadTest } from '../helpers/configHelpers'; import payload from '../../src'; -import config from './config'; +import configPromise from './config'; import AutosavePosts from './collections/Autosave'; import AutosaveGlobal from './globals/Autosave'; import { devUser } from '../credentials'; @@ -27,6 +27,8 @@ const globalGraphQLOriginalTitle = 'updated global title'; describe('Versions', () => { beforeAll(async () => { + const config = await configPromise; + const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }); graphQLURL = `${serverURL}${config.routes.api}${config.routes.graphQL}`;