chore(plugin-form-builder): scaffolds tests (#4500)

This commit is contained in:
Jacob Fletcher
2023-12-13 15:13:38 -05:00
committed by GitHub
parent eb6572e9e5
commit 9e7a8c7206
14 changed files with 484 additions and 371 deletions

View File

@@ -1,2 +0,0 @@
MONGODB_URI=mongodb://localhost/payload-plugin-form-builder
PAYLOAD_SECRET=kajsnfkjhabndsfgseaniluanbsrkdgbhyasfg

View File

@@ -1,4 +0,0 @@
{
"exec": "ts-node src/server.ts",
"ext": "ts"
}

View File

@@ -1,29 +0,0 @@
{
"name": "payload-starter-typescript",
"description": "Blank template - no collections",
"version": "1.0.0",
"main": "dist/server.js",
"license": "MIT",
"scripts": {
"dev": "cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
"build:server": "tsc",
"build": "yarn build:payload && yarn build:server",
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
},
"dependencies": {
"deepmerge": "^4.2.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"payload": "^1.8.2"
},
"devDependencies": {
"@types/express": "^4.17.9",
"@types/react": "^18.2.6",
"cross-env": "^7.0.3",
"nodemon": "^2.0.6",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
}
}

View File

@@ -1,205 +0,0 @@
/* tslint:disable */
/**
* This file was automatically generated by Payload CMS.
* 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 {
collections: {
users: User
pages: Page
forms: Form
'form-submissions': FormSubmission
}
globals: {}
}
export interface User {
id: string
updatedAt: string
createdAt: string
email?: string
resetPasswordToken?: string
resetPasswordExpiration?: string
loginAttempts?: number
lockUntil?: string
password?: string
}
export interface Page {
id: string
title: string
form?: string | Form
updatedAt: string
createdAt: string
}
export interface Form {
id: string
title: string
fields?: (
| {
name: string
label?: string
width?: number
defaultValue?: string
required?: boolean
id?: string
blockName?: string
blockType: 'text'
}
| {
name: string
label?: string
width?: number
defaultValue?: string
required?: boolean
id?: string
blockName?: string
blockType: 'textarea'
}
| {
name: string
label?: string
width?: number
defaultValue?: string
options: {
label: string
value: string
id?: string
}[]
required?: boolean
id?: string
blockName?: string
blockType: 'select'
}
| {
name: string
label?: string
width?: number
required?: boolean
id?: string
blockName?: string
blockType: 'email'
}
| {
name: string
label?: string
width?: number
required?: boolean
id?: string
blockName?: string
blockType: 'state'
}
| {
name: string
label?: string
width?: number
required?: boolean
id?: string
blockName?: string
blockType: 'country'
}
| {
name: string
label?: string
width?: number
defaultValue?: number
required?: boolean
id?: string
blockName?: string
blockType: 'number'
}
| {
name: string
label?: string
width?: number
required?: boolean
defaultValue?: boolean
id?: string
blockName?: string
blockType: 'checkbox'
}
| {
message?: {
[k: string]: unknown
}[]
id?: string
blockName?: string
blockType: 'message'
}
| {
name: string
label?: string
width?: number
basePrice?: number
priceConditions?: {
fieldToUse?: string
condition?: 'hasValue' | 'equals' | 'notEquals'
valueForCondition?: string
operator?: 'add' | 'subtract' | 'multiply' | 'divide'
valueType?: 'static' | 'valueOfField'
valueForOperator?: string
id?: string
}[]
required?: boolean
id?: string
blockName?: string
blockType: 'payment'
}
| {
value?: string
id?: string
blockName?: string
blockType: 'color'
}
)[]
submitButtonLabel?: string
confirmationType?: 'message' | 'redirect'
confirmationMessage: {
[k: string]: unknown
}[]
redirect?: {
type?: 'reference' | 'custom'
reference: {
value: string | Page
relationTo: 'pages'
}
url: string
}
emails: {
emailTo?: string
cc?: string
bcc?: string
replyTo?: string
emailFrom?: string
subject: string
message?: {
[k: string]: unknown
}[]
id?: string
}[]
name?: string
updatedAt: string
createdAt: string
}
export interface FormSubmission {
id: string
form: string | Form
submissionData: {
field: string
value: string
id?: string
}[]
payment?: {
field?: string
status?: string
amount?: number
paymentProcessor?: string
creditCard?: {
token?: string
brand?: string
number?: string
}
}
updatedAt: string
createdAt: string
}

View File

@@ -1,48 +0,0 @@
import type { Payload } from 'payload'
export const seed = async (payload: Payload): Promise<any> => {
payload.logger.info('Seeding data...')
await payload.create({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
password: 'test',
},
})
const { id: formID } = await payload.create({
collection: 'forms',
data: {
title: 'Contact Form',
confirmationMessage: [
{
type: 'paragraph',
text: 'Confirmed',
},
],
fields: [
{
blockType: 'text',
label: 'Name',
name: 'name',
required: true,
},
{
blockType: 'email',
label: 'Email',
name: 'email',
required: true,
},
],
},
})
await payload.create({
collection: 'pages',
data: {
title: 'Contact',
form: formID,
},
})
}

View File

@@ -1,33 +0,0 @@
import dotenv from 'dotenv'
import express from 'express'
import payload from 'payload'
import { seed } from './seed'
dotenv.config()
const app = express()
// Redirect root to Admin panel
app.get('/', (_, res) => {
res.redirect('/admin')
})
// Initialize Payload
const start = async (): Promise<any> => {
await payload.init({
secret: process.env.PAYLOAD_SECRET,
mongoURL: process.env.MONGODB_URI,
express: app,
onInit: () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
},
})
if (process.env.PAYLOAD_SEED === 'true') {
await seed(payload)
}
app.listen(3000)
}
start()

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "../",
"jsx": "react"
},
"ts-node": {
"transpileOnly": true
}
}

View File

@@ -1,4 +1,5 @@
import type { CollectionConfig } from 'payload/types' // const payload = require('payload');
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
export const Pages: CollectionConfig = { export const Pages: CollectionConfig = {
slug: 'pages', slug: 'pages',
@@ -9,6 +10,9 @@ export const Pages: CollectionConfig = {
admin: { admin: {
useAsTitle: 'title', useAsTitle: 'title',
}, },
access: {
read: () => true,
},
fields: [ fields: [
{ {
name: 'title', name: 'title',
@@ -16,6 +20,12 @@ export const Pages: CollectionConfig = {
type: 'text', type: 'text',
required: true, required: true,
}, },
{
name: 'slug',
label: 'Slug',
type: 'text',
required: true,
},
{ {
name: 'form', name: 'form',
label: 'Form', label: 'Form',

View File

@@ -1,4 +1,4 @@
import type { CollectionConfig } from 'payload/types' import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
export const Users: CollectionConfig = { export const Users: CollectionConfig = {
slug: 'users', slug: 'users',

View File

@@ -1,12 +1,11 @@
import path from 'path' import type { Block } from '../../packages/payload/src/fields/config/types'
import { buildConfig } from 'payload/config'
import type { Block } from 'payload/types'
// import formBuilderPlugin from '../../dist'; import formBuilder, { fields as formFields } from '../../packages/plugin-form-builder/src'
// eslint-disable-next-line import/no-relative-packages import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
import formBuilderPlugin, { fields } from '../../src' import { devUser } from '../credentials'
import { Pages } from './collections/Pages' import { Pages } from './collections/Pages'
import { Users } from './collections/Users' import { Users } from './collections/Users'
import { seed } from './seed'
const colorField: Block = { const colorField: Block = {
slug: 'color', slug: 'color',
@@ -22,34 +21,26 @@ const colorField: Block = {
], ],
} }
export default buildConfig({ export default buildConfigWithDefaults({
serverURL: 'http://localhost:3000', collections: [Pages, Users],
localization: { localization: {
locales: ['en', 'it'],
defaultLocale: 'en', defaultLocale: 'en',
fallback: true,
locales: ['en', 'es', 'de'],
}, },
admin: { onInit: async (payload) => {
user: Users.slug, await payload.create({
webpack: (config) => { collection: 'users',
const newConfig = { data: {
...config, email: devUser.email,
resolve: { password: devUser.password,
...config.resolve,
alias: {
...config.resolve.alias,
react: path.join(__dirname, '../node_modules/react'),
'react-dom': path.join(__dirname, '../node_modules/react-dom'),
payload: path.join(__dirname, '../node_modules/payload'),
}, },
}, })
}
return newConfig await seed(payload)
}, },
},
collections: [Users, Pages],
plugins: [ plugins: [
formBuilderPlugin({ formBuilder({
// handlePayment: handleFormPayments, // handlePayment: handleFormPayments,
// beforeEmail: prepareFormEmails, // beforeEmail: prepareFormEmails,
redirectRelationships: ['pages'], redirectRelationships: ['pages'],
@@ -60,7 +51,7 @@ export default buildConfig({
// }, // },
fields: [ fields: [
{ {
name: 'name', name: 'custom',
type: 'text', type: 'text',
}, },
], ],
@@ -69,7 +60,7 @@ export default buildConfig({
payment: true, payment: true,
colorField, colorField,
text: { text: {
...fields.text, ...formFields.text,
labels: { labels: {
singular: 'Custom Text Field', singular: 'Custom Text Field',
plural: 'Custom Text Fields', plural: 'Custom Text Fields',
@@ -89,7 +80,4 @@ export default buildConfig({
}, },
}), }),
], ],
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
}) })

View File

@@ -0,0 +1,119 @@
import type { Form } from './payload-types'
import payload from '../../packages/payload/src'
import { initPayloadTest } from '../helpers/configHelpers'
import { formSubmissionsSlug, formsSlug } from './shared'
describe('Form Builder Plugin', () => {
let form: Form
beforeAll(async () => {
await initPayloadTest({ __dirname, init: { local: true } })
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
title: 'Test Form',
fields: [
{
name: 'name',
blockType: 'text',
},
],
confirmationMessage: [
{
type: 'text',
text: 'Confirmed.',
},
],
}
form = (await payload.create({
collection: formsSlug,
data: formConfig,
})) as unknown as Form
})
describe('plugin collections', () => {
it('adds forms collection', async () => {
const { docs: forms } = await payload.find({ collection: formsSlug })
expect(forms.length).toBeGreaterThan(0)
})
it('adds form submissions collection', async () => {
const { docs: formSubmissions } = await payload.find({ collection: formSubmissionsSlug })
expect(formSubmissions).toHaveLength(0)
})
})
describe('form building', () => {
it('can create a simple form', async () => {
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
title: 'Test Form',
fields: [
{
name: 'name',
blockType: 'text',
},
],
confirmationMessage: [
{
type: 'text',
text: 'Confirmed.',
},
],
}
const testForm = await payload.create({
collection: formsSlug,
data: formConfig,
})
expect(testForm).toHaveProperty('fields')
expect(testForm.fields).toHaveLength(1)
expect(testForm.fields[0]).toHaveProperty('name', 'name')
})
it('can use form overrides', async () => {
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
custom: 'custom',
title: 'Test Form',
confirmationMessage: [
{
type: 'text',
text: 'Confirmed.',
},
],
}
const testForm = await payload.create({
collection: formsSlug,
data: formConfig,
})
expect(testForm).toHaveProperty('custom', 'custom')
})
})
describe('form submissions and validations', () => {
it('can create a form submission', async () => {
const formSubmission = await payload.create({
collection: formSubmissionsSlug,
data: {
form: form.id,
submissionData: [
{
field: 'name',
value: 'Test Submission',
},
],
},
depth: 0,
})
expect(formSubmission).toHaveProperty('form', form.id)
expect(formSubmission).toHaveProperty('submissionData')
expect(formSubmission.submissionData).toHaveLength(1)
expect(formSubmission.submissionData[0]).toHaveProperty('field', 'name')
expect(formSubmission.submissionData[0]).toHaveProperty('value', 'Test Submission')
})
})
})

View File

@@ -0,0 +1,257 @@
/* 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 {
collections: {
pages: Page
users: User
forms: Form
'form-submissions': FormSubmission
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
globals: {}
}
export interface Page {
id: string
title: string
slug: string
form?: (string | null) | Form
updatedAt: string
createdAt: string
}
export interface Form {
id: string
title: string
fields?:
| (
| {
name: string
label?: string | null
width?: number | null
required?: boolean | null
defaultValue?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'checkbox'
}
| {
name: string
label?: string | null
width?: number | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'country'
}
| {
name: string
label?: string | null
width?: number | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'email'
}
| {
message?:
| {
[k: string]: unknown
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'message'
}
| {
name: string
label?: string | null
width?: number | null
defaultValue?: number | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'number'
}
| {
name: string
label?: string | null
width?: number | null
basePrice?: number | null
priceConditions?:
| {
fieldToUse?: string | null
condition?: ('hasValue' | 'equals' | 'notEquals') | null
valueForCondition?: string | null
operator?: ('add' | 'subtract' | 'multiply' | 'divide') | null
valueType?: ('static' | 'valueOfField') | null
valueForOperator?: string | null
id?: string | null
}[]
| null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'payment'
}
| {
name: string
label?: string | null
width?: number | null
defaultValue?: string | null
options?:
| {
label: string
value: string
id?: string | null
}[]
| null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'select'
}
| {
name: string
label?: string | null
width?: number | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'state'
}
| {
name: string
label?: string | null
width?: number | null
defaultValue?: string | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'text'
}
| {
name: string
label?: string | null
width?: number | null
defaultValue?: string | null
required?: boolean | null
id?: string | null
blockName?: string | null
blockType: 'textarea'
}
| {
value?: string | null
id?: string | null
blockName?: string | null
blockType: 'color'
}
)[]
| null
submitButtonLabel?: string | null
confirmationType?: ('message' | 'redirect') | null
confirmationMessage?:
| {
[k: string]: unknown
}[]
| null
redirect?: {
type?: ('reference' | 'custom') | null
reference?: {
relationTo: 'pages'
value: string | Page
} | null
url?: string | null
}
emails?:
| {
emailTo?: string | null
cc?: string | null
bcc?: string | null
replyTo?: string | null
emailFrom?: string | null
subject: string
message?:
| {
[k: string]: unknown
}[]
| null
id?: string | null
}[]
| null
custom?: string | null
updatedAt: string
createdAt: string
}
export interface User {
id: string
updatedAt: string
createdAt: string
email: string
resetPasswordToken?: string | null
resetPasswordExpiration?: string | null
salt?: string | null
hash?: string | null
loginAttempts?: number | null
lockUntil?: string | null
password: string | null
}
export interface FormSubmission {
id: string
form: string | Form
submissionData?:
| {
field: string
value: string
id?: string | null
}[]
| null
payment?: {
field?: string | null
status?: string | null
amount?: number | null
paymentProcessor?: string | null
creditCard?: {
token?: string | null
brand?: string | null
number?: string | null
}
}
updatedAt: string
createdAt: string
}
export interface PayloadPreference {
id: string
user: {
relationTo: 'users'
value: string | User
}
key?: string | null
value?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
updatedAt: string
createdAt: string
}
export interface PayloadMigration {
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}

View File

@@ -0,0 +1,70 @@
import type { Payload } from '../../../packages/payload/src'
import type { PayloadRequest } from '../../../packages/payload/src/express/types'
import { formsSlug, pagesSlug } from '../shared'
export const seed = async (payload: Payload): Promise<boolean> => {
payload.logger.info('Seeding data...')
const req = {} as PayloadRequest
try {
await payload.create({
collection: 'users',
data: {
email: 'demo@payloadcms.com',
password: 'demo',
},
req,
})
await payload.create({
collection: pagesSlug,
data: {
slug: 'home',
title: 'Home page',
},
req,
})
const { id: formID } = await payload.create({
collection: formsSlug,
data: {
title: 'Contact Form',
confirmationMessage: [
{
type: 'paragraph',
text: 'Confirmed',
},
],
fields: [
{
blockType: 'text',
label: 'Name',
name: 'name',
required: true,
},
{
blockType: 'email',
label: 'Email',
name: 'email',
required: true,
},
],
},
})
await payload.create({
collection: pagesSlug,
data: {
title: 'Contact',
slug: 'contact',
form: formID,
},
})
return true
} catch (err) {
console.error(err)
return false
}
}

View File

@@ -0,0 +1,5 @@
export const pagesSlug = 'pages'
export const formsSlug = 'forms'
export const formSubmissionsSlug = 'form-submissions'