chore: merge master

This commit is contained in:
James
2023-07-29 11:18:39 -04:00
523 changed files with 55727 additions and 20120 deletions

View File

@@ -1,10 +1,10 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { PostsCollection, postsSlug } from './collections/Posts';
import { MenuGlobal } from './globals/Menu';
import { devUser } from '../credentials';
import { MediaCollection } from './collections/Media';
export default buildConfig({
export default buildConfigWithDefaults({
// ...extend config here
collections: [
PostsCollection,

View File

@@ -1,5 +1,5 @@
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { FieldAccess } from '../../src/fields/config/types';
import { firstArrayText, secondArrayText } from './shared';
@@ -36,7 +36,7 @@ const UseRequestHeadersAccess: FieldAccess = ({ req: { headers } }) => {
return !!headers && headers.authorization === requestHeaders.authorization;
};
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
user: 'users',
},

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { mapAsync } from '../../src/utilities/mapAsync';
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import AfterDashboard from './components/AfterDashboard';
import CustomMinimalRoute from './components/views/CustomMinimal';
import CustomDefaultRoute from './components/views/CustomDefault';
@@ -20,7 +20,7 @@ export interface Post {
updatedAt: Date;
}
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
css: path.resolve(__dirname, 'styles.scss'),
components: {

View File

@@ -1,6 +1,7 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug: 'arrays',
@@ -41,4 +42,13 @@ export default buildConfig({
],
},
],
onInit: async (payload) => {
await payload.create({
collection: 'users',
data: {
email: devUser.email,
password: devUser.password,
},
});
},
});

View File

@@ -1,12 +1,12 @@
import { v4 as uuid } from 'uuid';
import { mapAsync } from '../../src/utilities/mapAsync';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import { AuthDebug } from './AuthDebug';
export const slug = 'users';
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
user: 'users',
autoLogin: false,

View File

@@ -1,7 +1,7 @@
import { Request } from 'express';
import { Strategy } from 'passport-strategy';
import { Payload } from '../../../src/payload';
import { buildConfig } from '../../buildConfig';
import { buildConfigWithDefaults } from '../../buildConfigWithDefaults';
export const slug = 'users';
export const strategyName = 'test-local';
@@ -41,7 +41,7 @@ export class CustomStrategy extends Strategy {
}
}
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
user: 'users',
},

View File

@@ -1,40 +1,41 @@
import { Config, SanitizedConfig } from '../src/config/types';
import { buildConfig as buildPayloadConfig } from '../src/config/build';
export function buildConfig(config?: Partial<Config>): Promise<SanitizedConfig> {
export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<SanitizedConfig> {
const [name] = process.argv.slice(2);
const baseConfig: Config = {
const config: Config = {
telemetry: false,
rateLimit: {
window: 15 * 60 * 100, // 15min default,
max: 9999999999,
},
...config,
...testConfig,
};
baseConfig.admin = {
config.admin = {
autoLogin: process.env.PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN === 'true' ? false : {
email: 'dev@payloadcms.com',
password: 'test',
},
...(baseConfig.admin || {}),
...(config.admin || {}),
webpack: (webpackConfig) => {
const existingConfig = typeof config?.admin?.webpack === 'function'
? config.admin.webpack(webpackConfig)
const existingConfig = typeof testConfig?.admin?.webpack === 'function'
? testConfig.admin.webpack(webpackConfig)
: webpackConfig;
return {
...existingConfig,
name,
cache: process.env.NODE_ENV === 'test' ? {
type: 'memory',
} : existingConfig.cache,
cache: process.env.NODE_ENV === 'test'
? { type: 'memory' }
: existingConfig.cache,
};
},
};
if (process.env.PAYLOAD_DISABLE_ADMIN === 'true') {
if (typeof baseConfig.admin !== 'object') baseConfig.admin = {};
baseConfig.admin.disable = true;
if (typeof config.admin !== 'object') config.admin = {};
config.admin.disable = true;
}
return buildPayloadConfig(baseConfig);
return buildPayloadConfig(config);
}

View File

@@ -1,7 +1,7 @@
import path from 'path';
import type { CollectionConfig } from '../../src/collections/config/types';
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
export interface Relation {
id: string;
@@ -30,7 +30,7 @@ const collectionWithName = (collectionSlug: string): CollectionConfig => {
export const slug = 'posts';
export const relationSlug = 'relation';
export default buildConfig({
export default buildConfigWithDefaults({
graphQL: {
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
},

View File

@@ -1,6 +1,6 @@
import type { CollectionConfig } from '../../src/collections/config/types';
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
export interface Relation {
id: string;
@@ -34,7 +34,7 @@ export const customIdSlug = 'custom-id';
export const customIdNumberSlug = 'custom-id-number';
export const errorOnHookSlug = 'error-on-hooks';
export default buildConfig({
export default buildConfigWithDefaults({
endpoints: [
{
path: '/send-test-email',

View File

@@ -1,4 +1,4 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { openAccess } from '../helpers/configHelpers';
import { Config } from '../../src/config/types';
@@ -62,4 +62,4 @@ const config: Config = {
custom: { name: 'Customer portal' },
};
export default buildConfig(config);
export default buildConfigWithDefaults(config);

View File

@@ -1,7 +1,7 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug: 'posts',

View File

@@ -1,6 +1,6 @@
import express, { Response } from 'express';
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { openAccess } from '../helpers/configHelpers';
import { PayloadRequest } from '../../src/express/types';
import { Config } from '../../src/config/types';
@@ -148,4 +148,4 @@ const MyConfig: Config = {
},
};
export default buildConfig(MyConfig);
export default buildConfigWithDefaults(MyConfig);

View File

@@ -1,9 +1,9 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { ErrorFieldsCollection } from './collections/ErrorFields';
import { devUser } from '../credentials';
import Uploads from './collections/Upload';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
ErrorFieldsCollection,
Uploads,

View File

@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug: 'blocks-collection',

View File

@@ -1,5 +1,5 @@
import type { CollectionConfig } from '../../src/collections/config/types';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import { mapAsync } from '../../src/utilities/mapAsync';
import { FilterOptionsProps } from '../../src/fields/config/types';
@@ -33,7 +33,7 @@ const baseRelationshipFields: CollectionConfig['fields'] = [
},
];
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug,

View File

@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import path from 'path';
import fs from 'fs';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import ArrayFields, { arrayDoc } from './collections/Array';
import BlockFields, { blocksDoc } from './collections/Blocks';
@@ -26,7 +26,7 @@ import Uploads2 from './collections/Upload2';
import Uploads3 from './collections/Uploads3';
import RowFields from './collections/Row';
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
webpack: (config) => ({
...config,

View File

@@ -411,6 +411,88 @@ describe('fields', () => {
const customRowLabel = await page.locator('#rowLabelAsComponent-row-0 >> .row-label :text("custom row label")');
await expect(customRowLabel).toHaveCSS('text-transform', 'uppercase');
});
describe('row manipulation', () => {
test('should add 2 new rows', async () => {
await page.goto(url.create);
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1');
await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2');
await saveDocAndAssert(page);
});
test('should remove 2 new rows', async () => {
await page.goto(url.create);
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1');
await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2');
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click();
await page.locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__remove').click();
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click();
await page.locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__remove').click();
const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows');
const directChildDivCount = await rowsContainer.evaluate((element) => {
const childDivCount = element.querySelectorAll(':scope > div');
return childDivCount.length;
});
expect(directChildDivCount).toBe(0);
});
test('should remove existing row', async () => {
await page.goto(url.create);
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1');
await saveDocAndAssert(page);
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click();
await page.locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__action.array-actions__remove').click();
const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows');
const directChildDivCount = await rowsContainer.evaluate((element) => {
const childDivCount = element.querySelectorAll(':scope > div');
return childDivCount.length;
});
expect(directChildDivCount).toBe(0);
});
test('should add row after removing existing row', async () => {
await page.goto(url.create);
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1');
await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2');
await saveDocAndAssert(page);
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click();
await page.locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__action.array-actions__remove').click();
await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click();
await page.locator('#field-potentiallyEmptyArray__1__text').fill('updated array row 2');
await saveDocAndAssert(page);
const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows');
const directChildDivCount = await rowsContainer.evaluate((element) => {
const childDivCount = element.querySelectorAll(':scope > div');
return childDivCount.length;
});
expect(directChildDivCount).toBe(2);
});
});
});
describe('tabs', () => {
@@ -494,7 +576,7 @@ describe('fields', () => {
await wait(200);
await editLinkModal.locator('button[type="submit"]').click();
const errorField = await page.locator('[id^=drawer_1_rich-text-link-] .render-fields > :nth-child(3)');
const hasErrorClass = await errorField.evaluate(el => el.classList.contains('error'));
const hasErrorClass = await errorField.evaluate((el) => el.classList.contains('error'));
expect(hasErrorClass).toBe(true);
});

View File

@@ -1,5 +1,5 @@
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
export const slug = 'global';
export const arraySlug = 'array';
@@ -16,7 +16,7 @@ const access = {
update: () => true,
};
export default buildConfig({
export default buildConfigWithDefaults({
localization: {
locales: [englishLocale, spanishLocale],
defaultLocale: englishLocale,

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
export default buildConfig({
export default buildConfigWithDefaults({
graphQL: {
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
},

View File

@@ -0,0 +1,68 @@
/* eslint-disable no-param-reassign */
import payload from '../../../../src';
import { CollectionConfig } from '../../../../src/collections/config/types';
import type { PayloadRequest } from '../../../../src/types';
export const contextHooksSlug = 'context-hooks';
const ContextHooks: CollectionConfig = {
slug: contextHooksSlug,
access: {
read: () => true,
create: () => true,
delete: () => true,
update: () => true,
},
hooks: {
beforeOperation: [async ({ context, args }) => {
// eslint-disable-next-line prefer-destructuring
const req: PayloadRequest = args.req;
if (!req.query || !Object.keys(req.query).length) {
return args;
}
Object.keys(req.query).forEach((key) => {
if (key.startsWith('context_')) {
// Strip 'context_' from key, add it to context object and remove it from query params
const newKey = key.substring('context_'.length);
context[newKey] = req.query[key];
delete req.query[key];
}
});
return args;
}],
beforeChange: [({ context, data, req }) => {
if (!context.secretValue) {
context.secretValue = 'secret';
}
if (req.context !== context) {
throw new Error('req.context !== context');
}
return data;
}],
afterChange: [async ({ context, doc }) => {
if (context.triggerAfterChange === false) { // Make sure we don't trigger afterChange again and again in an infinite loop
return;
}
await payload.update({
collection: contextHooksSlug,
id: doc.id,
data: {
value: context.secretValue ?? '',
},
context: {
triggerAfterChange: false, // Make sure we don't trigger afterChange again and again in an infinite loop. This should be done via context and not a potential disableHooks property, as we want to specifically test the context functionality here
},
});
}],
},
fields: [
{
name: 'value',
type: 'text',
},
],
};
export default ContextHooks;

View File

@@ -1,13 +1,15 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import TransformHooks from './collections/Transform';
import Hooks, { hooksSlug } from './collections/Hook';
import NestedAfterReadHooks from './collections/NestedAfterReadHooks';
import ChainingHooks from './collections/ChainingHooks';
import Relations from './collections/Relations';
import Users, { seedHooksUsers } from './collections/Users';
import ContextHooks from './collections/ContextHooks';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
ContextHooks,
TransformHooks,
Hooks,
NestedAfterReadHooks,

View File

@@ -11,13 +11,16 @@ import type { NestedAfterReadHook } from './payload-types';
import { hooksUsersSlug } from './collections/Users';
import { devUser, regularUser } from '../credentials';
import { AuthenticationError } from '../../src/errors';
import { contextHooksSlug } from './collections/ContextHooks';
let client: RESTClient;
let apiUrl;
describe('Hooks', () => {
beforeAll(async () => {
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } });
client = new RESTClient(config, { serverURL, defaultSlug: transformSlug });
apiUrl = `${serverURL}/api`;
});
afterAll(async () => {
@@ -148,6 +151,63 @@ describe('Hooks', () => {
expect(retrievedDocs[0].text).toEqual('ok!!');
});
it('should pass context from beforeChange to afterChange', async () => {
const document = await payload.create({
collection: contextHooksSlug,
data: {
value: 'wrongvalue',
},
});
const retrievedDoc = await payload.findByID({
collection: contextHooksSlug,
id: document.id,
});
expect(retrievedDoc.value).toEqual('secret');
});
it('should pass context from local API to hooks', async () => {
const document = await payload.create({
collection: contextHooksSlug,
data: {
value: 'wrongvalue',
},
context: {
secretValue: 'data from local API',
},
});
const retrievedDoc = await payload.findByID({
collection: contextHooksSlug,
id: document.id,
});
expect(retrievedDoc.value).toEqual('data from local API');
});
it('should pass context from rest API to hooks', async () => {
const params = new URLSearchParams({
context_secretValue: 'data from rest API',
});
// send context as query params. It will be parsed by the beforeOperation hook
const response = await fetch(`${apiUrl}/${contextHooksSlug}?${params.toString()}`, {
body: JSON.stringify({
value: 'wrongvalue',
}),
method: 'post',
});
const document = (await response.json()).doc;
const retrievedDoc = await payload.findByID({
collection: contextHooksSlug,
id: document.id,
});
expect(retrievedDoc.value).toEqual('data from rest API');
});
});
describe('auth collection hooks', () => {

View File

@@ -1,4 +1,4 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import { ArrayCollection } from './collections/Array';
import { LocalizedPost, RelationshipLocalized } from './payload-types';
@@ -32,7 +32,7 @@ const openAccess = {
update: () => true,
};
export default buildConfig({
export default buildConfigWithDefaults({
localization: {
locales: [defaultLocale, spanishLocale],
defaultLocale,

View File

@@ -1,9 +1,9 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
export const pagesSlug = 'pages';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug: 'users',

View File

@@ -1,10 +1,10 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import GlobalViewWithRefresh from './GlobalViewWithRefresh';
export const pagesSlug = 'pages';
export default buildConfig({
export default buildConfigWithDefaults({
globals: [
{
slug: 'settings',

View File

@@ -1,6 +1,6 @@
import type { CollectionConfig } from '../../src/collections/config/types';
import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
const openAccess = {
create: () => true,
@@ -41,7 +41,7 @@ export const defaultAccessRelSlug = 'strict-access';
export const chainedRelSlug = 'chained-relation';
export const customIdSlug = 'custom-id-relation';
export const customIdNumberSlug = 'custom-id-number-relation';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
{
slug,

View File

@@ -0,0 +1,36 @@
import path from 'path';
import { CollectionConfig } from '../../../../src/collections/config/types';
type TypeWithFile = {
filename: string;
mimeType: string;
filesize: number;
} & Record<string, unknown>
function docHasFilename(doc: Record<string, unknown>): doc is TypeWithFile {
if (typeof doc === 'object' && 'filename' in doc) {
return true;
}
return false;
}
export const adminThumbnailSrc = '/media/image-640x480.png';
export const AdminThumbnailCol: CollectionConfig = {
slug: 'admin-thumbnail',
upload: {
staticDir: path.resolve(__dirname, '../../media'),
adminThumbnail: ({ doc }) => {
if (docHasFilename(doc)) {
if (doc.mimeType.startsWith('image/')) {
return null; // Fallback to default admin thumbnail if image
}
return adminThumbnailSrc; // Use custom thumbnail if not image
}
return null;
},
},
fields: [],
};
export default AdminThumbnailCol;

View File

@@ -1,10 +1,11 @@
import path from 'path';
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import { devUser } from '../credentials';
import getFileByPath from '../../src/uploads/getFileByPath';
import removeFiles from '../helpers/removeFiles';
import { Uploads1 } from './collections/Upload1';
import Uploads2 from './collections/Upload2';
import AdminThumbnailCol from './collections/admin-thumbnail';
export const mediaSlug = 'media';
@@ -12,9 +13,11 @@ export const relationSlug = 'relation';
export const audioSlug = 'audio';
export const adminThumbnailSlug = 'admin-thumbnail';
const mockModulePath = path.resolve(__dirname, './mocks/mockFSModule.js');
export default buildConfig({
export default buildConfigWithDefaults({
admin: {
webpack: (config) => ({
...config,
@@ -198,6 +201,7 @@ export default buildConfig({
},
Uploads1,
Uploads2,
AdminThumbnailCol,
],
onInit: async (payload) => {
const uploadsDir = path.resolve(__dirname, './media');
@@ -244,5 +248,24 @@ export default buildConfig({
audio: file.id,
},
});
// Create admin thumbnail media
await payload.create({
collection: AdminThumbnailCol.slug,
data: {},
file: {
...audioFile,
name: 'audio-thumbnail.mp3', // Override to avoid conflicts
},
});
await payload.create({
collection: AdminThumbnailCol.slug,
data: {},
file: {
...imageFile,
name: `thumb-${imageFile.name}`,
},
});
},
});

View File

@@ -1,19 +1,21 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import path from 'path';
import { relationSlug, mediaSlug, audioSlug } from './config';
import { relationSlug, mediaSlug, audioSlug, adminThumbnailSlug } from './config';
import type { Media } from './payload-types';
import payload from '../../src';
import { AdminUrlUtil } from '../helpers/adminUrlUtil';
import { initPayloadE2E } from '../helpers/configHelpers';
import { saveDocAndAssert } from '../helpers';
import wait from '../../src/utilities/wait';
import { adminThumbnailSrc } from './collections/admin-thumbnail';
const { beforeAll, describe } = test;
let mediaURL: AdminUrlUtil;
let audioURL: AdminUrlUtil;
let relationURL: AdminUrlUtil;
let adminThumbnailURL: AdminUrlUtil;
describe('uploads', () => {
let page: Page;
@@ -26,6 +28,7 @@ describe('uploads', () => {
mediaURL = new AdminUrlUtil(serverURL, mediaSlug);
audioURL = new AdminUrlUtil(serverURL, audioSlug);
relationURL = new AdminUrlUtil(serverURL, relationSlug);
adminThumbnailURL = new AdminUrlUtil(serverURL, adminThumbnailSlug);
const context = await browser.newContext();
page = await context.newPage();
@@ -144,4 +147,17 @@ describe('uploads', () => {
await wait(200);
await expect(page.locator('.Toastify')).toContainText('The following field is invalid: audio');
});
test('Should execute adminThumbnail and provide thumbnail when set', async () => {
await page.goto(adminThumbnailURL.list);
await wait(200);
// Ensure sure false or null shows generic file svg
const genericUploadImage = page.locator('tr.row-1 .thumbnail svg');
await expect(genericUploadImage).toBeVisible();
// Ensure adminThumbnail fn returns correct value based on audio/mp3 mime
const audioUploadImage = page.locator('tr.row-2 .thumbnail img');
expect(await audioUploadImage.getAttribute('src')).toContain(adminThumbnailSrc);
});
});

View File

@@ -1,35 +1,44 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation".
*/
export interface Config {
collections: {
relation: Relation;
audio: Audio;
'gif-resize': GifResize;
media: Media;
'media-trim': MediaTrim;
'unstored-media': UnstoredMedia;
'externally-served-media': ExternallyServedMedia;
'uploads-1': Uploads1;
'uploads-2': Uploads2;
users: User;
};
globals: {};
}
export interface Relation {
id: string;
image?: string | Media;
createdAt: string;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes: {
maintainedAspectRatio: {
sizes?: {
maintainedAspectRatio?: {
url?: string;
width?: number;
height?: number;
@@ -37,7 +46,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
differentFormatFromMainImage: {
differentFormatFromMainImage?: {
url?: string;
width?: number;
height?: number;
@@ -45,7 +54,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
maintainedImageSize: {
maintainedImageSize?: {
url?: string;
width?: number;
height?: number;
@@ -53,7 +62,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
maintainedImageSizeWithNewFormat: {
maintainedImageSizeWithNewFormat?: {
url?: string;
width?: number;
height?: number;
@@ -61,7 +70,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
tablet: {
tablet?: {
url?: string;
width?: number;
height?: number;
@@ -69,7 +78,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
mobile: {
mobile?: {
url?: string;
width?: number;
height?: number;
@@ -77,7 +86,7 @@ export interface Media {
filesize?: number;
filename?: string;
};
icon: {
icon?: {
url?: string;
width?: number;
height?: number;
@@ -86,35 +95,138 @@ export interface Media {
filename?: string;
};
};
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "unstored-media".
*/
export interface UnstoredMedia {
export interface Audio {
id: string;
audio?: string | Media;
updatedAt: string;
createdAt: string;
}
export interface GifResize {
id: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
small?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
large?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
}
export interface MediaTrim {
id: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
trimNumber?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
trimString?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
trimOptions?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
}
export interface UnstoredMedia {
id: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
}
export interface ExternallyServedMedia {
id: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
}
export interface Uploads1 {
id: string;
media?: string | Uploads2;
richText?: {
[k: string]: unknown;
}[];
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
}
export interface Uploads2 {
id: string;
title?: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
email?: string;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string;
resetPasswordExpiration?: string;
salt?: string;
hash?: string;
loginAttempts?: number;
lockUntil?: string;
createdAt: string;
updatedAt: string;
password?: string;
}

View File

@@ -1,4 +1,4 @@
import { buildConfig } from '../buildConfig';
import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import AutosavePosts from './collections/Autosave';
import DraftPosts from './collections/Drafts';
import AutosaveGlobal from './globals/Autosave';
@@ -7,7 +7,7 @@ import DraftGlobal from './globals/Draft';
import VersionPosts from './collections/Versions';
import { draftSlug } from './shared';
export default buildConfig({
export default buildConfigWithDefaults({
collections: [
AutosavePosts,
DraftPosts,