Skip to content

Commit

Permalink
Move testing package into main package (#6361)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown authored Aug 18, 2021
1 parent a95773f commit 595922b
Show file tree
Hide file tree
Showing 76 changed files with 181 additions and 173 deletions.
6 changes: 6 additions & 0 deletions .changeset/itchy-eggs-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/testing': major
'@keystone-next/keystone': minor
---

Moved exports of `@keystone-next/testing` to `@keystone-next/keystone/testing`
10 changes: 5 additions & 5 deletions docs/pages/docs/guides/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Markdown } from '../../../components/Markdown';
# Testing

When building a web application it's important to be able to test the behaviour of your system to ensure it does what you expect.
In this guide we'll show you how to use the `@keystone-next/testing` package and [Jest](https://jestjs.io/) to write tests to check the behaviour of your GraphQL API.
In this guide we'll show you how to use `@keystone-next/keystone/testing` and [Jest](https://jestjs.io/) to write tests to check the behaviour of your GraphQL API.

## Running tests

In order to run tests using the `@keystone-next/testing` package, we recommend adding the following script to your `package.json` file.
In order to run tests using `@keystone-next/keystone/testing`, we recommend adding the following script to your `package.json` file.

```json
"scripts": {
Expand All @@ -30,7 +30,7 @@ The first step to writing a test for your Keystone system is to setup a test run
You can then use this runner to wrap your test functions.

```typescript
import { setupTestRunner } from '@keystone-next/testing';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import { config } from './keystone';

const runner = setupTestRunner({ config });
Expand Down Expand Up @@ -204,7 +204,7 @@ The context API can be used after calling `connect()` in the `beforeAll()` block
x> Be careful of sharing database state across tests. Avoid relying on changes of state from one test in subsequent tests.

```
import { setupTestEnv, TestEnv } from '@keystone-next/testing';
import { setupTestEnv, TestEnv } from '@keystone-next/keystone/testing';
import { KeystoneContext } from '@keystone-next/types';
describe('Example tests using test environment', () => {
Expand Down Expand Up @@ -234,4 +234,4 @@ describe('Example tests using test environment', () => {
});
```

export default ({ children }) => <Markdown description="Learn how to use the `@keystone-next/testing` package and Jest to write tests that check the behaviour of your GraphQL API.">{children}</Markdown>;
export default ({ children }) => <Markdown description="Learn how to use `@keystone-next/keystone/testing` and Jest to write tests that check the behaviour of your GraphQL API.">{children}</Markdown>;
1 change: 0 additions & 1 deletion examples-staging/ecommerce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"stripe": "^8.169.0"
},
"devDependencies": {
"@keystone-next/testing": "^1.1.1",
"typescript": "^4.3.5"
},
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/ecommerce/tests/mutations.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KeystoneContext } from '@keystone-next/types';
import { setupTestRunner } from '@keystone-next/testing';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import config from '../keystone';

const FAKE_ID = 'cinjfgbkjnfg';
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Each project below demonstrates a Keystone feature you can learn about and exper
- [`extendGraphqlSchema`](./extend-graphql-schema): Extends the GraphQL API of the Task Manager base.
- [Virtual field](./virtual-field): Adds virtual fields to the Blog base.
- [Document field](./document-field): Adds document fields to the Blog base.
- [Testing](./testing): Adds tests with `@keystone-next/testing` to the `withAuth()` example.
- [Testing](./testing): Adds tests with `@keystone-next/keystone/testing` to the `withAuth()` example.
- [Custom field](./custom-field): Adds a custom `stars` field to the Blog base.
- [Custom field view](./custom-field-view): Adds a custom Admin UI view to a `json` field to the Task Manager base.
- [Custom Admin UI logo](./custom-admin-ui-logo): Adds a custom logo in the Admin UI to the Task Manager base.
Expand Down
6 changes: 3 additions & 3 deletions examples/testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://

## Features

Keystone provides a testing library in the package [`@keystone-next/testing`](https://keystonejs.com/guides/testing) which helps you write tests using [Jest](https://jestjs.io/).
Keystone provides a testing library in [`@keystone-next/keystone/testing`](https://keystonejs.com/guides/testing) which helps you write tests using [Jest](https://jestjs.io/).
This example project uses this library to add tests to the [`withAuth()`](../with-auth) example project. The tests can be found in [example.test.ts](./example.test.ts)

### Running tests
Expand Down Expand Up @@ -63,7 +63,7 @@ Ran all test suites.
The function `setupTestRunner` takes the project's `KeystoneConfig` object and creates a `runner` function. This test runner is then used to wrap a test function.

```typescript
import { setupTestRunner } from '@keystone-next/testing';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import config from './keystone';

const runner = setupTestRunner({ config });
Expand Down Expand Up @@ -128,7 +128,7 @@ The function `setupTestEnv` is used to set up a test environment which can be us

```typescript
import { KeystoneContext } from '@keystone-next/types';
import { setupTestEnv, TestEnv } from '@keystone-next/testing';
import { setupTestEnv, TestEnv } from '@keystone-next/keystone/testing';
import config from './keystone';

describe('Example tests using test environment', () => {
Expand Down
2 changes: 1 addition & 1 deletion examples/testing/example.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KeystoneContext } from '@keystone-next/types';
import { setupTestEnv, setupTestRunner, TestEnv } from '@keystone-next/testing';
import { setupTestEnv, setupTestRunner, TestEnv } from '@keystone-next/keystone/testing';
import config from './keystone';

// Setup a test runner which will provide a clean test environment
Expand Down
1 change: 0 additions & 1 deletion examples/testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"@keystone-next/auth": "^31.0.0",
"@keystone-next/fields": "^14.0.0",
"@keystone-next/keystone": "^24.0.0",
"@keystone-next/testing": "^1.1.1",
"@keystone-next/types": "^24.0.0"
},
"devDependencies": {
Expand Down
7 changes: 6 additions & 1 deletion packages/keystone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"session",
"dist",
"static",
"admin-ui"
"admin-ui",
"testing"
],
"bin": {
"keystone-next": "bin/cli.js"
Expand Down Expand Up @@ -62,6 +63,7 @@
"@types/prettier": "^2.3.2",
"@types/prompts": "^2.0.14",
"@types/source-map-support": "^0.5.4",
"@types/supertest": "^2.0.11",
"@types/uid-safe": "^2.1.2",
"@types/uuid": "^8.3.1",
"apollo-server-errors": "^2.5.0",
Expand All @@ -86,6 +88,7 @@
"graphql-upload": "^12.0.0",
"image-size": "^1.0.0",
"image-type": "^4.1.0",
"memoize-one": "^5.2.1",
"meow": "^9.0.0",
"next": "^10.2.3",
"node-fetch": "^2.6.1",
Expand All @@ -100,6 +103,7 @@
"react-dom": "^17.0.2",
"resolve": "^1.20.0",
"source-map-support": "^0.5.19",
"supertest": "^6.1.5",
"typescript": "^4.3.5",
"uid-safe": "^2.1.5",
"uuid": "^8.3.2"
Expand All @@ -125,6 +129,7 @@
"___internal-do-not-use-will-break-in-patch/admin-ui/{next-config.ts,id-field-view.tsx}",
"artifacts.ts",
"migrations.ts",
"testing.ts",
"schema/index.ts",
"session/index.ts",
"scripts/index.ts",
Expand Down
91 changes: 91 additions & 0 deletions packages/keystone/src/testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import path from 'path';
import crypto from 'crypto';
import fs from 'fs';
import express from 'express';
import supertest, { Test } from 'supertest';
import memoizeOne from 'memoize-one';
import type { KeystoneConfig, KeystoneContext } from '@keystone-next/types';
import {
getCommittedArtifacts,
writeCommittedArtifacts,
requirePrismaClient,
generateNodeModulesArtifacts,
} from './artifacts';
import { pushPrismaSchemaToDatabase } from './migrations';
import { initConfig, createSystem, createExpressServer } from '.';

export type GraphQLRequest = (arg: {
query: string;
variables?: Record<string, any>;
operationName?: string;
}) => Test;

export type TestArgs = {
context: KeystoneContext;
graphQLRequest: GraphQLRequest;
app: express.Express;
};

export type TestEnv = {
connect: () => Promise<void>;
disconnect: () => Promise<void>;
testArgs: TestArgs;
};

const _hashPrismaSchema = memoizeOne(prismaSchema =>
crypto.createHash('md5').update(prismaSchema).digest('hex')
);
const _alreadyGeneratedProjects = new Set<string>();
export async function setupTestEnv({
config: _config,
}: {
config: KeystoneConfig;
}): Promise<TestEnv> {
// Force the UI to always be disabled.
const config = initConfig({ ..._config, ui: { isDisabled: true } });
const { graphQLSchema, getKeystone } = createSystem(config);

const artifacts = await getCommittedArtifacts(graphQLSchema, config);
const hash = _hashPrismaSchema(artifacts.prisma);

const artifactPath = path.resolve('.keystone', 'tests', hash);

if (!_alreadyGeneratedProjects.has(hash)) {
_alreadyGeneratedProjects.add(hash);
fs.mkdirSync(artifactPath, { recursive: true });
await writeCommittedArtifacts(artifacts, artifactPath);
await generateNodeModulesArtifacts(graphQLSchema, config, artifactPath);
}
await pushPrismaSchemaToDatabase(
config.db.url,
artifacts.prisma,
path.join(artifactPath, 'schema.prisma'),
true // shouldDropDatabase
);

const { connect, disconnect, createContext } = getKeystone(requirePrismaClient(artifactPath));

// (config, graphQLSchema, createContext, dev, projectAdminPath, isVerbose)
const app = await createExpressServer(config, graphQLSchema, createContext, true, '', false);

const graphQLRequest: GraphQLRequest = ({ query, variables = undefined, operationName }) =>
supertest(app)
.post('/api/graphql')
.send({ query, variables, operationName })
.set('Accept', 'application/json');

return { connect, disconnect, testArgs: { context: createContext(), graphQLRequest, app } };
}

export function setupTestRunner({ config }: { config: KeystoneConfig }) {
return (testFn: (testArgs: TestArgs) => Promise<void>) => async () => {
// Reset the database to be empty for every test.
const { connect, disconnect, testArgs } = await setupTestEnv({ config });
await connect();
try {
return await testFn(testArgs);
} finally {
await disconnect();
}
};
}
4 changes: 4 additions & 0 deletions packages/keystone/testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "dist/keystone.cjs.js",
"module": "dist/keystone.esm.js"
}
2 changes: 1 addition & 1 deletion packages/testing/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Testing

This package provides tools to help you test your Keystone system.
This exports of this package have been moved to `@keystone-next/keystone`
7 changes: 0 additions & 7 deletions packages/testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,5 @@
"engines": {
"node": "^12.20 || >= 14.13"
},
"dependencies": {
"@keystone-next/keystone": "^24.0.0",
"@types/supertest": "^2.0.11",
"express": "^4.17.1",
"memoize-one": "^5.2.1",
"supertest": "^6.1.5"
},
"repository": "https://github.com/keystonejs/keystone/tree/master/packages/testing"
}
92 changes: 3 additions & 89 deletions packages/testing/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,91 +1,5 @@
import path from 'path';
import crypto from 'crypto';
import fs from 'fs';
import express from 'express';
import supertest, { Test } from 'supertest';
import memoizeOne from 'memoize-one';
import { initConfig, createSystem, createExpressServer } from '@keystone-next/keystone';
import { pushPrismaSchemaToDatabase } from '@keystone-next/keystone/migrations';
import {
getCommittedArtifacts,
writeCommittedArtifacts,
requirePrismaClient,
generateNodeModulesArtifacts,
} from '@keystone-next/keystone/artifacts';
import type { KeystoneConfig, KeystoneContext } from '@keystone-next/types';

export type GraphQLRequest = (arg: {
query: string;
variables?: Record<string, any>;
operationName?: string;
}) => Test;

export type TestArgs = {
context: KeystoneContext;
graphQLRequest: GraphQLRequest;
app: express.Express;
};

export type TestEnv = {
connect: () => Promise<void>;
disconnect: () => Promise<void>;
testArgs: TestArgs;
};

const _hashPrismaSchema = memoizeOne(prismaSchema =>
crypto.createHash('md5').update(prismaSchema).digest('hex')
throw new Error(
"Keystone's testing utilities have moved to `@keystone-next/keystone/testing`, please import them from there instead."
);
const _alreadyGeneratedProjects = new Set<string>();
export async function setupTestEnv({
config: _config,
}: {
config: KeystoneConfig;
}): Promise<TestEnv> {
// Force the UI to always be disabled.
const config = initConfig({ ..._config, ui: { isDisabled: true } });
const { graphQLSchema, getKeystone } = createSystem(config);

const artifacts = await getCommittedArtifacts(graphQLSchema, config);
const hash = _hashPrismaSchema(artifacts.prisma);

const artifactPath = path.resolve('.keystone', 'tests', hash);

if (!_alreadyGeneratedProjects.has(hash)) {
_alreadyGeneratedProjects.add(hash);
fs.mkdirSync(artifactPath, { recursive: true });
await writeCommittedArtifacts(artifacts, artifactPath);
await generateNodeModulesArtifacts(graphQLSchema, config, artifactPath);
}
await pushPrismaSchemaToDatabase(
config.db.url,
artifacts.prisma,
path.join(artifactPath, 'schema.prisma'),
true // shouldDropDatabase
);

const { connect, disconnect, createContext } = getKeystone(requirePrismaClient(artifactPath));

// (config, graphQLSchema, createContext, dev, projectAdminPath, isVerbose)
const app = await createExpressServer(config, graphQLSchema, createContext, true, '', false);

const graphQLRequest: GraphQLRequest = ({ query, variables = undefined, operationName }) =>
supertest(app)
.post('/api/graphql')
.send({ query, variables, operationName })
.set('Accept', 'application/json');

return { connect, disconnect, testArgs: { context: createContext(), graphQLRequest, app } };
}

export function setupTestRunner({ config }: { config: KeystoneConfig }) {
return (testFn: (testArgs: TestArgs) => Promise<void>) => async () => {
// Reset the database to be empty for every test.
const { connect, disconnect, testArgs } = await setupTestEnv({ config });
await connect();
try {
return await testFn(testArgs);
} finally {
await disconnect();
}
};
}
export {};
1 change: 0 additions & 1 deletion tests/admin-ui-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"build": "keystone-next build"
},
"dependencies": {
"@keystone-next/testing": "^1.1.1",
"@keystone-next/types": "^24.0.0",
"@keystone-next/utils": "^1.0.4",
"@manypkg/find-root": "^1.1.0",
Expand Down
2 changes: 1 addition & 1 deletion tests/api-tests/access-control/authed.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GraphQLError } from 'graphql';
import { DatabaseProvider, KeystoneContext } from '@keystone-next/types';
import { setupTestEnv, TestEnv } from '@keystone-next/testing';
import { setupTestEnv, TestEnv } from '@keystone-next/keystone/testing';
import { expectAccessDenied } from '../utils';
import {
FAKE_ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { text } from '@keystone-next/fields';
import { createSchema, list } from '@keystone-next/keystone/schema';
import { setupTestRunner } from '@keystone-next/testing';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import { apiTestConfig, expectAccessDenied } from '../utils';

const runner = setupTestRunner({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { text } from '@keystone-next/fields';
import { createSchema, list } from '@keystone-next/keystone/schema';
import { setupTestRunner } from '@keystone-next/testing';
import { setupTestRunner } from '@keystone-next/keystone/testing';
import { apiTestConfig, expectAccessDenied } from '../utils';

const runner = setupTestRunner({
Expand Down
Loading

0 comments on commit 595922b

Please sign in to comment.