From 8ba6e6643821bac85c03d317ccf7d205bfa4649c Mon Sep 17 00:00:00 2001 From: Timothy Jones Date: Tue, 6 Oct 2020 23:11:16 +1100 Subject: [PATCH] feat(v3): Add draft withPact interface for V3 --- package.json | 4 +- src/v3/index.ts | 126 +++++++++++++++++++++++++++++++++++++ src/v3/pactwith.v3.test.ts | 57 +++++++++++++++++ 3 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 src/v3/index.ts create mode 100644 src/v3/pactwith.v3.test.ts diff --git a/package.json b/package.json index 731be51..487accb 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@commitlint/cli": "^10.0.0", "@commitlint/config-conventional": "^10.0.0", - "@pact-foundation/pact": "9.8.0", + "@pact-foundation/pact": "v10.0.0-beta.16", "@types/supertest": "2.0.8", "coveralls": "3.0.9", "cross-env": "7.0.2", @@ -57,7 +57,7 @@ "typescript": "3.8.3" }, "peerDependencies": { - "@pact-foundation/pact": "^8.2.4 || ^9.0.1", + "@pact-foundation/pact": "^v10.0.0-beta.16", "jest": "^24.0.0 || ^25.0.0 || ^26.0.0" }, "dependencies": { diff --git a/src/v3/index.ts b/src/v3/index.ts new file mode 100644 index 0000000..e8db151 --- /dev/null +++ b/src/v3/index.ts @@ -0,0 +1,126 @@ +import * as pactV3 from '@pact-foundation/pact/v3'; +import * as path from 'path'; + +export type JestPactOptionsV3 = Omit & { + dir?: string; + timeout?: number; +}; + +type ExecuteTestFn = (mockServer: pactV3.V3MockServer) => Promise; + +interface DescibeArg { + provider: pactV3.PactV3; + execute: JestPactExecuteTestFn; +} + +type JestPactExecuteTestFn = (description: string, fn: ExecuteTestFn) => void; + +export type JestProvidedPactFnV3 = (D: DescibeArg) => void; +export type JestDescribePactFnV3 = ( + description: string, + fn: JestProvidedPactFnV3, +) => void; + +export type JestProvidedDescribeFnV3 = ( + pactDescribe: JestDescribePactFnV3, +) => void; + +const applyDefaults = (options: JestPactOptionsV3): pactV3.PactV3Options => ({ + dir: path.resolve(process.cwd(), 'pact/pacts'), + ...options, +}); + +const setupProvider = (options: pactV3.PactV3Options) => { + const pactDescribe: JestDescribePactFnV3 = ( + describeDescription: string, + fn: JestProvidedPactFnV3, + ) => { + describe(describeDescription, () => { + const provider = new pactV3.PactV3(options); + const execute = (testDescription: string, executeTest: ExecuteTestFn) => { + it(testDescription, () => provider.executeTest(executeTest)); + }; + fn({ provider, execute }); + }); + }; + return pactDescribe; +}; + +const jestPactWrapper = ( + options: JestPactOptionsV3, + tests: JestProvidedDescribeFnV3, +): void => { + const pactTestTimeout = options.timeout || 30000; + + describe(`with ${pactTestTimeout} ms timeout for Pact`, () => { + let originalTimeout: number; + + beforeAll(() => { + // Jest's default timeout is 5000, and jest doesn't provide a way of + // asking what the current timeout is. In Jest 24 and 25, Jasmine was probably + // the test runner, so we can ask Jasmine if it is there. In later versions of + // Jest (eg 26 and up), Jasmine may not be defined. + // See https://github.com/pact-foundation/jest-pact/issues/197 for discussion + // + // For now, we just assume that 5000 was the original timeout. + // The impact is likely to be small, as `jest.setTimeout()` only works for the + // current test file + originalTimeout = global.jasmine + ? global.jasmine.DEFAULT_TIMEOUT_INTERVAL + : 5000; + jest.setTimeout(pactTestTimeout); + }); + + afterAll(() => { + jest.setTimeout(originalTimeout); + }); + + tests(setupProvider(applyDefaults(options))); + }); +}; + +const describeString = (options: JestPactOptionsV3) => + `Pact between ${options.consumer} and ${options.provider}`; + +export const pactWith = ( + options: JestPactOptionsV3, + tests: JestProvidedDescribeFnV3, +) => describe(describeString(options), () => jestPactWrapper(options, tests)); + +export const xpactWith = ( + options: JestPactOptionsV3, + tests: JestProvidedDescribeFnV3, +) => xdescribe(describeString(options), () => jestPactWrapper(options, tests)); + +export const fpactWith = ( + options: JestPactOptionsV3, + tests: JestProvidedDescribeFnV3, +) => fdescribe(describeString(options), () => jestPactWrapper(options, tests)); + +/* + +example: + +const options = { dir: '', consumer: '', provider: '' }; + +const api = (url: string) => ({ getWhatever: () => Promise.resolve(url) }); + +pactWith(options, (pactDescribe) => { + pactDescribe('some interaction', (provider) => { + beforeEach(() => + provider + .given('Some state') + .uponReceiving('Some request') + .withRequest({ path: '/whatever/path', method: 'GET' }), + ); + + it('works', () => + provider + .executeTest((mockserver) => api(mockserver.url).getWhatever()) + .then((response) => { + expect(response).toEqual({ whatever: 'it-should-be' }); + })); + }); +}); + +*/ diff --git a/src/v3/pactwith.v3.test.ts b/src/v3/pactwith.v3.test.ts new file mode 100644 index 0000000..682307b --- /dev/null +++ b/src/v3/pactwith.v3.test.ts @@ -0,0 +1,57 @@ +import { V3MockServer } from '@pact-foundation/pact/v3/pact'; +import * as supertest from 'supertest'; +import { pactWith } from './index'; + +const getClient = (mock: V3MockServer) => supertest(mock.url); + +pactWith({ consumer: 'MyConsumer', provider: 'pactWith v3' }, (interaction) => { + interaction('pact integration', ({ provider, execute }) => { + beforeEach(() => + provider + .given('A pet 1845563262948980200 exists') + .uponReceiving('A get request to get a pet 1845563262948980200') + .withRequest({ + method: 'GET', + path: '/v2/pet/1845563262948980200', + headers: { api_key: '[]' }, + }) + .willRespondWith({ + status: 200, + }), + ); + + execute( + 'should be be able to hide the pact stuff behind the scenes with a port of the users choosing', + (mock) => + getClient(mock) + .get('/v2/pet/1845563262948980200') + .set('api_key', '[]') + .expect(200), + ); + }); + + interaction('another pact integration', ({ provider, execute }) => { + beforeEach(() => + provider + .given('No pets exist') + .uponReceiving('A get request to get a pet 1845563262948980200') + .withRequest({ + method: 'GET', + path: '/v2/pet/1845563262948980200', + headers: { api_key: '[]' }, + }) + .willRespondWith({ + status: 404, + }), + ); + + execute( + 'should be be able to hide the pact stuff behind the scenes with a port of the users choosing', + (mock) => + getClient(mock) + .get('/v2/pet/1845563262948980200') + .set('api_key', '[]') + .expect(404), + ); + }); +});