Skip to content

Commit

Permalink
fix(options): remove PactOptions redefinition, introduce JestPactOptions
Browse files Browse the repository at this point in the history
BREAKING CHANGE

To avoid needing to modify PactOptions every time it changes, we now union it with the extra options in a type called JestPactOptions
  • Loading branch information
TimothyJones committed May 5, 2020
1 parent 9df21f7 commit b622f01
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 76 deletions.
57 changes: 28 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- [x] instantiates the PactOptions for you
- [x] Setups Pact mock service before and after hooks so you don’t have to
- [x] Assign random ports and pass port back to user so we can run in parallel without port clashes
- [x] Set Jasmine's timeout to 30 seconds preventing brittle tests in slow environments
- [x] Set Jasmine's timeout to 30 seconds preventing brittle tests in slow environments

## `Jest-Pact` Roadmap

Expand Down Expand Up @@ -67,7 +67,7 @@ import api from 'yourCode';

pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
let client;

beforeEach(() => {
client = api(provider.mockService.baseUrl)
});
Expand All @@ -76,8 +76,8 @@ pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
// Here we set up the interaction that the Pact
// mock provider will expect.
//
// jest-pact takes care of validating and tearing
// down the provider for you.
// jest-pact takes care of validating and tearing
// down the provider for you.
beforeEach(() =>
provider.addInteraction({
state: "Server is healthy",
Expand All @@ -94,16 +94,16 @@ pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
},
})
);
// You also test that the API returns the correct
// response to the data layer.

// You also test that the API returns the correct
// response to the data layer.
//
// Although Pact will ensure that the provider
// returned the expected object, you need to test that
// your code recieves the right object.
//
// This is often the same as the object that was
// in the network response, but (as illustrated
// This is often the same as the object that was
// in the network response, but (as illustrated
// here) not always.
it('returns server health', () =>
client.health().then(health => {
Expand Down Expand Up @@ -133,7 +133,7 @@ export const healthyResponse = {
body: {
status: Matchers.like('up'),
},
}
}
```
Expand All @@ -145,7 +145,7 @@ import api from 'yourCode';

pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
let client;

beforeEach(() => {
client = api(provider.mockService.baseUrl)
});
Expand All @@ -159,7 +159,7 @@ pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
willRespondWith: healthyResponse
})
);

it('returns server health', () =>
client.health().then(health => {
expect(health).toEqual('up');
Expand All @@ -170,37 +170,36 @@ pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
## Configuration
```ts
You can use all the usual `PactOptions` from pact-js, plus a timeout for
telling jest to wait a bit longer for pact to start and run.
pactWith(PactOptions, provider => {
```ts
pactWith(JestPactOptions, provider => {
// regular pact tests go here
}

interface PactOptions {
provider: string;
consumer: string;
port?: number; // defaults to a random port if not provided
pactfileWriteMode?: PactFileWriteMode;
dir?: string // defaults to pact/pacts if not provided
timeout?: number // Timeout for pact service start/teardown. Defaults to 30 seconds.
interface JestPactOptions = PactOptions & {
timeout?: number; // Timeout for pact service start/teardown, expressed in milliseconds
// Default is 30000 milliseconds (30 seconds).
}

type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
type PactFileWriteMode = "overwrite" | "update" | "merge";

```
### Defaults
## Defaults
Jest-Pact sets some helpful defaults for you. You can override any of these by explicitly setting corresponding option.
- Log files are written to /pact/logs
- Pact files are written to /pact/pacts
- `log` is set so that log files are written to /pact/logs, and named <consumer>-<provider>-mockserver-interaction.log
- `dir` is set so that pact files are written to /pact/pacts
- `logLevel` is set to error
- `timeout` is 30,000 milliseconds (30 seconds)
- `pactfileWriteMode` is set to "update"
Most of the time you won't need to change these.
### Jest Watch Mode
By default Jest will watch all your files for changes, which means it will run in an infinite loop as your pact tests will generate json pact files and log files.
You can get round this by using the following `watchPathIgnorePatterns: ["pact/logs/*","pact/pacts/*"]` in your `jest.config.js`
You can get around this by using the following `watchPathIgnorePatterns: ["pact/logs/*","pact/pacts/*"]` in your `jest.config.js`
Example
Expand Down
27 changes: 6 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
import * as pact from '@pact-foundation/pact';
import { PactOptions } from '@pact-foundation/pact/dsl/options';
import * as path from 'path';

export interface PactOptions {
provider: string;
consumer: string;
port?: number;
logLevel?: LogLevel;
pactfileWriteMode?: PactFileWriteMode;
dir?: string;
export type JestPactOptions = PactOptions & {
timeout?: number;
}

export declare type LogLevel =
| 'trace'
| 'debug'
| 'info'
| 'warn'
| 'error'
| 'fatal';

export declare type PactFileWriteMode = 'overwrite' | 'update' | 'merge';
};

const applyDefaults = (options: PactOptions) => ({
const applyDefaults = (options: JestPactOptions) => ({
port: options.port,
log: path.resolve(
process.cwd(),
Expand All @@ -35,7 +20,7 @@ const applyDefaults = (options: PactOptions) => ({
...options,
});

const setupProvider = (options: PactOptions) => {
const setupProvider = (options: JestPactOptions) => {
const pactMock: pact.Pact = new pact.Pact(options);

beforeAll(() => pactMock.setup());
Expand All @@ -51,7 +36,7 @@ export const getProviderBaseUrl = (provider: pact.Pact) =>
? provider.mockService.baseUrl
: `http://${provider.opts.host}:${provider.opts.port}`;

export const pactWith = (options: PactOptions, tests: any) =>
export const pactWith = (options: JestPactOptions, tests: any) =>
describe(`Pact between ${options.consumer} and ${options.provider}`, () => {
const pactTestTimeout = options.timeout || 30000;

Expand Down
63 changes: 37 additions & 26 deletions src/test/pactwith.test.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,71 @@
import { InteractionObject } from "@pact-foundation/pact";
import * as supertest from "supertest";
import { pactWith } from "../index";
import { InteractionObject } from '@pact-foundation/pact';
import * as supertest from 'supertest';
import { getProviderBaseUrl, pactWith } from '../index';

const getClient = (provider: any) => supertest(provider.mockService.baseUrl);
const pactPort: number = 5001;

const postValidRequest: InteractionObject = {
state: "A pet 1845563262948980200 exists",
uponReceiving: "A get request to get a pet 1845563262948980200",
state: 'A pet 1845563262948980200 exists',
uponReceiving: 'A get request to get a pet 1845563262948980200',
willRespondWith: {
status: 200
status: 200,
},
withRequest: {
method: "GET",
path: "/v2/pet/1845563262948980200",
headers: { api_key: "[]" }
}
method: 'GET',
path: '/v2/pet/1845563262948980200',
headers: { api_key: '[]' },
},
};

pactWith(
{ consumer: "MyConsumer", provider: "pactWith", port: pactPort },
{ consumer: 'MyConsumer', provider: 'pactWith', port: pactPort },
(provider: any) => {
describe("pact integration", () => {
describe('pact integration', () => {
beforeEach(() => provider.addInteraction(postValidRequest));

test("should be be able to hide the pact stuff behind the scenes with a port of the users choosing", () =>
test('should be be able to hide the pact stuff behind the scenes with a port of the users choosing', () =>
getClient(provider)
.get("/v2/pet/1845563262948980200")
.set("api_key", "[]")
.get('/v2/pet/1845563262948980200')
.set('api_key', '[]')
.expect(200));
});

describe("provider object", () => {
test("should show the specified port in the URL", () => {
describe('provider object', () => {
test('should show the specified port in the URL', () => {
expect(provider.mockService.baseUrl).toMatch(
new RegExp(`${pactPort}$`)
new RegExp(`${pactPort}$`),
);
});
test('should return the port on getProviderBaseUrl', () => {
expect(getProviderBaseUrl(provider)).toEqual(
`http://127.0.0.1:${pactPort}`,
);
});
});
}
},
);

pactWith({ consumer: "MyConsumer", provider: "pactWith2" }, (provider: any) => {
describe("pact integration", () => {
pactWith({ consumer: 'MyConsumer', provider: 'pactWith2' }, (provider: any) => {
describe('pact integration', () => {
beforeEach(() => provider.addInteraction(postValidRequest));

test("should be ok if i dont provide a port", () =>
test('should be ok if i dont provide a port', () =>
getClient(provider)
.get("/v2/pet/1845563262948980200")
.set("api_key", "[]")
.get('/v2/pet/1845563262948980200')
.set('api_key', '[]')
.expect(200));
});

describe("provider object", () => {
test("should show the randomly assigned port in the URL", () => {
describe('provider object', () => {
test('should show the randomly assigned port in the URL', () => {
expect(provider.mockService.baseUrl).toMatch(new RegExp(`\\d{4,5}$`));
});

test('should return the host on getProviderBaseUrl', () => {
expect(getProviderBaseUrl(provider)).toMatch(
new RegExp('^http://127.0.0.1:\\d{4,5}$'),
);
});
});
});

0 comments on commit b622f01

Please sign in to comment.