Skip to content

Commit

Permalink
chore(examples/testing): builds testing example (#3443)
Browse files Browse the repository at this point in the history
  • Loading branch information
JarrodMFlesch authored Oct 5, 2023
1 parent 9880880 commit deb5be5
Show file tree
Hide file tree
Showing 14 changed files with 9,058 additions and 1 deletion.
5 changes: 5 additions & 0 deletions examples/testing/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PORT=3000
MONGO_URL=mongodb://127.0.0.1/your-project-name
PAYLOAD_SECRET_KEY=alwifhjoq284jgo5w34jgo43f3
PAYLOAD_CONFIG_PATH=src/payload.config.ts
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
142 changes: 142 additions & 0 deletions examples/testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Payload Testing Example

This example demonstrates how to get started with testing Payload using [Jest](https://jestjs.io/). You can clone this down and use it as a starting point for your own Payload projects, or you can follow the steps below to add testing to your existing Payload project.

## Add testing to your existing Payload project

1. Initial setup:

```bash
# install dependencies
yarn add --dev jest mongodb-memory-server @swc/jest @swc/core isomorphic-fetch @types/jest
```

```bash
# create a .env file
cp .env.example .env
```

2. This example uses the following folder structure:
```
root
└─ /src
└─ payload.config.ts
└─ /tests
```

3. Add test credentials to your project. Create a file at `src/tests/credentials.ts` with the following contents:

```ts
export default {
email: '[email protected]',
password: 'test',
};
```

4. Add the global setup file to your project. This file will be run before any tests are run. It will start a MongoDB server and create a Payload instance for you to use in your tests. Create a file at `src/tests/globalSetup.ts` with the following contents:

```ts
import { resolve } from 'path';
import payload from 'payload';
import express from 'express';
import testCredentials from './credentials';

require('dotenv').config({
path: resolve(__dirname, '../../.env'),
});

const app = express();

const globalSetup = async () => {
await payload.init({
secret: process.env.PAYLOAD_SECRET_KEY,
mongoURL: process.env.MONGO_URL,
express: app,
});

app.listen(process.env.PORT, async () => {
console.log(`Express is now listening for incoming connections on port ${process.env.PORT}.`);
});

const response = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/users/first-register`, {
body: JSON.stringify({
email: testCredentials.email,
password: testCredentials.password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});

const data = await response.json();

if (!data.user || !data.user.token) {
throw new Error('Failed to register first user');
}
};

export default globalSetup;
```

5. Add a `jest.config.ts` file to the root of your project:

```ts
module.exports = {
verbose: true,
globalSetup: '<rootDir>/src/tests/globalSetup.ts',
roots: ['<rootDir>/src/'],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
transform: {
'^.+\\.(t|j)sx?$': [
'@swc/jest',
{
jsc: {
target: 'es2021',
},
},
],
},
};
```

6. Write your first test. Create a file at `src/tests/login.spec.ts` with the following contents:

```ts
import { User } from '../payload-types';
import testCredentials from './credentials';

describe('Users', () => {
it('should allow a user to log in', async () => {
const result: {
token: string
user: User
} = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/users/login`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: testCredentials.email,
password: testCredentials.password,
}),
}).then((res) => res.json());

expect(result.token).toBeDefined();
});
});
```

7. Add a script to run tests via the command line. Add the following to your `package.json` scripts:

```json
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest --forceExit --detectOpenHandles"
}
```

8. Run your tests:

```bash
yarn test
```
16 changes: 16 additions & 0 deletions examples/testing/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
verbose: true,
globalSetup: '<rootDir>/src/tests/globalSetup.ts',
roots: ['<rootDir>/src/'],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
transform: {
'^.+\\.(t|j)sx?$': [
'@swc/jest',
{
jsc: {
target: 'es2021',
},
},
],
},
}
6 changes: 6 additions & 0 deletions examples/testing/nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"watch": [
"./src/**/*.ts"
],
"exec": "ts-node ./src/server.ts"
}
32 changes: 32 additions & 0 deletions examples/testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "jest-payload",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"dotenv": "^16.3.1",
"express": "^4.18.2",
"get-tsconfig": "^4.7.0",
"payload": "^1.15.6"
},
"devDependencies": {
"@swc/core": "^1.3.84",
"@swc/jest": "^0.2.29",
"@types/jest": "^29.5.4",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.7.0",
"mongodb-memory-server": "^8.15.1",
"nodemon": "^3.0.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
},
"scripts": {
"generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
"dev": "nodemon",
"build:payload": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
"build:server": "tsc",
"build": "yarn build:server && yarn build:payload",
"serve": "PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --forceExit --detectOpenHandles"
}
}
35 changes: 35 additions & 0 deletions examples/testing/src/payload-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* 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: {
posts: Post
users: User
}
globals: {}
}
export interface Post {
id: string
title?: string
author?: string | User
updatedAt: string
createdAt: string
}
export interface User {
id: string
updatedAt: string
createdAt: string
email: string
resetPasswordToken?: string
resetPasswordExpiration?: string
salt?: string
hash?: string
loginAttempts?: number
lockUntil?: string
password?: string
}
33 changes: 33 additions & 0 deletions examples/testing/src/payload.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import path from 'path'
import dotenv from 'dotenv'
import { buildConfig } from 'payload/config'

dotenv.config({
path: path.resolve(__dirname, '../.env'),
})

export default buildConfig({
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
collections: [
{
slug: 'posts',
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'author',
type: 'relationship',
relationTo: 'users',
},
],
},
],
})
24 changes: 24 additions & 0 deletions examples/testing/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import path from 'path'
import express from 'express'
import payload from 'payload'

// Use `dotenv` to import your `.env` file automatically
require('dotenv').config({
path: path.resolve(__dirname, '../.env'),
})

const app = express()

async function start() {
await payload.init({
secret: process.env.PAYLOAD_SECRET_KEY,
mongoURL: process.env.MONGO_URL,
express: app,
})

app.listen(process.env.PORT, async () => {
console.log(`Express is now listening for incoming connections on port ${process.env.PORT}.`)
})
}

start()
4 changes: 4 additions & 0 deletions examples/testing/src/tests/credentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
email: '[email protected]',
password: 'test',
}
44 changes: 44 additions & 0 deletions examples/testing/src/tests/globalSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { resolve } from 'path'
import payload from 'payload'
import express from 'express'
import testCredentials from './credentials'

require('dotenv').config({
path: resolve(__dirname, '../../.env'),
})

const app = express()

const globalSetup = async () => {
await payload.init({
secret: process.env.PAYLOAD_SECRET_KEY,
mongoURL: process.env.MONGO_URL,
express: app,
})

app.listen(process.env.PORT, async () => {
console.log(`Express is now listening for incoming connections on port ${process.env.PORT}.`)
})

const response = await fetch(
`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/users/first-register`,
{
body: JSON.stringify({
email: testCredentials.email,
password: testCredentials.password,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
},
)

const data = await response.json()

if (!data.user || !data.user.token) {
throw new Error('Failed to register first user')
}
}

export default globalSetup
22 changes: 22 additions & 0 deletions examples/testing/src/tests/login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { User } from '../payload-types'
import testCredentials from './credentials'

describe('Users', () => {
it('should allow a user to log in', async () => {
const result: {
token: string
user: User
} = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/users/login`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: testCredentials.email,
password: testCredentials.password,
}),
}).then((res) => res.json())

expect(result.token).toBeDefined()
})
})
Loading

0 comments on commit deb5be5

Please sign in to comment.