diff --git a/examples/e2e/.eslintrc.yml b/.eslintrc.yml similarity index 50% rename from examples/e2e/.eslintrc.yml rename to .eslintrc.yml index 572a1f367..a5b488c88 100644 --- a/examples/e2e/.eslintrc.yml +++ b/.eslintrc.yml @@ -2,4 +2,5 @@ env: es6: true node: true mocha: true -extends: 'eslint:recommended' +extends: + - "plugin:prettier/recommended" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6dc5a3a14..a26d65588 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,9 +7,9 @@ There may also be an answer to your question on [stackoverflow]. Please provide the following information with your issue to enable us to respond as quickly as possible. -* The relevant versions of the packages you are using. -* The steps to recreate your issue. -* An executable code example where possible. You can fork this repository and use one of the [examples] to quickly recreate your issue. +- The relevant versions of the packages you are using. +- The steps to recreate your issue. +- An executable code example where possible. You can fork this repository and use one of the [examples] to quickly recreate your issue. ### Commit messages @@ -27,12 +27,18 @@ npm i -g cz-conventional-changelog `git cz` to commit and commitizen will guide you. +## Code style and formatting + +We use [Prettier](https://prettier.io/) for formatting, and for linting we use [TSLint](https://palantir.github.io/tslint/) (for TypeScript) and [Standard](https://standardjs.com) (for JS). + +Please update your editor to enable Prettier, and things should be easy 👌. If not, our lint step will catch it. + ## Pull requests -* Write tests for any changes -* Follow existing code style and conventions -* Separate unrelated changes into multiple pull requests -* For bigger changes, make sure you start a discussion first by creating an issue and explaining the intended change +- Write tests for any changes +- Follow existing code style and conventions +- Separate unrelated changes into multiple pull requests +- For bigger changes, make sure you start a discussion first by creating an issue and explaining the intended change [stackoverflow]: https://stackoverflow.com/questions/tagged/pact [examples]: https://github.com/pact-foundation/pact-js/tree/master/examples diff --git a/LICENSE b/LICENSE index 856021e04..edc428595 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ The MIT License (MIT) -Original work Copyright (c) 2014 ThoughtWorks -Modified work Copyright (c) 2014 DiUS +Original work Copyright (c) 2014 DiUS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/examples/ava/index.js b/examples/ava/index.js index 974efc2dc..e0e02b3de 100644 --- a/examples/ava/index.js +++ b/examples/ava/index.js @@ -1,31 +1,31 @@ -'use strict' +"use strict"; -const axios = require('axios') +const axios = require("axios"); -exports.getMeDogs = (endpoint) => { - const url = endpoint.url - const port = endpoint.port +exports.getMeDogs = endpoint => { + const url = endpoint.url; + const port = endpoint.port; return axios.request({ - method: 'GET', + method: "GET", baseURL: `${url}:${port}`, - url: '/dogs', + url: "/dogs", headers: { - 'Accept': 'application/json' + Accept: "application/json" } - }) -} + }); +}; -exports.getMeDog = (endpoint) => { - const url = endpoint.url - const port = endpoint.port +exports.getMeDog = endpoint => { + const url = endpoint.url; + const port = endpoint.port; return axios.request({ - method: 'GET', + method: "GET", baseURL: `${url}:${port}`, - url: '/dogs/1', + url: "/dogs/1", headers: { - 'Accept': 'application/json' + Accept: "application/json" } - }) -} + }); +}; diff --git a/examples/ava/test/get-dog_1.spec.js b/examples/ava/test/get-dog_1.spec.js index c8cea1322..53c4e3c4c 100644 --- a/examples/ava/test/get-dog_1.spec.js +++ b/examples/ava/test/get-dog_1.spec.js @@ -1,79 +1,83 @@ -'use strict' +"use strict"; -const path = require('path') -const test = require('ava') -const pact = require('../../../dist/pact') +const path = require("path"); +const test = require("ava"); +const pact = require("../../../dist/pact"); const Pact = pact.Pact; -const getMeDog = require('../index').getMeDog +const getMeDog = require("../index").getMeDog; -const url = 'http://localhost' -const port = 8990 +const url = "http://localhost"; +const port = 8990; const provider = new Pact({ port: port, - log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'), - dir: path.resolve(process.cwd(), 'pacts'), + log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), + dir: path.resolve(process.cwd(), "pacts"), spec: 2, - consumer: 'MyConsumer', - provider: 'MyProvider', - pactfileWriteMode: 'merge' -}) + consumer: "MyConsumer", + provider: "MyProvider", + pactfileWriteMode: "merge" +}); -test.before('setting up Dog API expectations', async() => { - await provider.setup() -}) +test.before("setting up Dog API expectations", async () => { + await provider.setup(); +}); -test('Dog API GET /dogs/1', async t => { - t.plan(1) +test("Dog API GET /dogs/1", async t => { + t.plan(1); // BEGIN - // Setup interactions for expected API response from provider // This is done due to similar reasons of tear-up/down of database // data in tests. const interaction = { - state: 'dog 1 exists', - uponReceiving: 'a request for dog 1', + state: "dog 1 exists", + uponReceiving: "a request for dog 1", withRequest: { - method: 'GET', - path: '/dogs/1', + method: "GET", + path: "/dogs/1", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, - body: [{ - dog: pact.Matchers.somethingLike(1), - name: pact.Matchers.term({ - matcher: '\(\\S+\)', - generate: 'rocky' - }) - }] + body: [ + { + dog: pact.Matchers.somethingLike(1), + name: pact.Matchers.term({ + matcher: "(\\S+)", + generate: "rocky" + }) + } + ] } - } + }; - await provider.addInteraction(interaction) + await provider.addInteraction(interaction); // END const urlAndPort = { url: url, port: port - } - const response = await getMeDog(urlAndPort) - t.deepEqual(response.data, [{ - dog: 1, - name: "rocky" - }]) -}) + }; + const response = await getMeDog(urlAndPort); + t.deepEqual(response.data, [ + { + dog: 1, + name: "rocky" + } + ]); +}); test.afterEach(async t => { // verify with Pact, and reset expectations - await t.notThrows(provider.verify()) -}) + await t.notThrows(provider.verify()); +}); -test.always.after('pact.js mock server graceful shutdown', async() => { - await provider.finalize() -}) +test.always.after("pact.js mock server graceful shutdown", async () => { + await provider.finalize(); +}); diff --git a/examples/ava/test/get-dogs.spec.js b/examples/ava/test/get-dogs.spec.js index b53c84317..3cc6e6416 100644 --- a/examples/ava/test/get-dogs.spec.js +++ b/examples/ava/test/get-dogs.spec.js @@ -1,79 +1,83 @@ -'use strict' +"use strict"; -const path = require('path') -const test = require('ava') -const pact = require('../../../dist/pact') +const path = require("path"); +const test = require("ava"); +const pact = require("../../../dist/pact"); const Pact = pact.Pact; -const getMeDogs = require('../index').getMeDogs +const getMeDogs = require("../index").getMeDogs; -const url = 'http://localhost' -const port = 8989 +const url = "http://localhost"; +const port = 8989; const provider = new Pact({ port: port, - log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'), - dir: path.resolve(process.cwd(), 'pacts'), + log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), + dir: path.resolve(process.cwd(), "pacts"), spec: 2, - consumer: 'MyConsumer', - provider: 'MyProvider', - pactfileWriteMode: 'merge' -}) + consumer: "MyConsumer", + provider: "MyProvider", + pactfileWriteMode: "merge" +}); -test.before('setting up Dog API expectations', async() => { - await provider.setup() -}) +test.before("setting up Dog API expectations", async () => { + await provider.setup(); +}); -test('Dog API GET /dogs', async t => { - t.plan(1) +test("Dog API GET /dogs", async t => { + t.plan(1); // BEGIN - // Setup interactions for expected API response from provider // This is done due to similar reasons of tear-up/down of database // data in tests. const interaction = { - state: 'i have a list of dogs', - uponReceiving: 'a request for all dogs', + state: "i have a list of dogs", + uponReceiving: "a request for all dogs", withRequest: { - method: 'GET', - path: '/dogs', + method: "GET", + path: "/dogs", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, - body: [{ - dog: pact.Matchers.somethingLike(1), - name: pact.Matchers.term({ - matcher: '\(\\S+\)', - generate: 'rocky' - }) - }] + body: [ + { + dog: pact.Matchers.somethingLike(1), + name: pact.Matchers.term({ + matcher: "(\\S+)", + generate: "rocky" + }) + } + ] } - } + }; - await provider.addInteraction(interaction) + await provider.addInteraction(interaction); // END const urlAndPort = { url: url, port: port - } - const response = await getMeDogs(urlAndPort) - t.deepEqual(response.data, [{ - dog: 1, - name: "rocky" - }]) -}) + }; + const response = await getMeDogs(urlAndPort); + t.deepEqual(response.data, [ + { + dog: 1, + name: "rocky" + } + ]); +}); test.afterEach(async t => { // verify with Pact, and reset expectations - await t.notThrows(provider.verify()) -}) + await t.notThrows(provider.verify()); +}); -test.always.after('pact.js mock server graceful shutdown', async() => { - await provider.finalize() -}) +test.always.after("pact.js mock server graceful shutdown", async () => { + await provider.finalize(); +}); diff --git a/examples/e2e/consumer.js b/examples/e2e/consumer.js index 657006319..622cbd1f1 100644 --- a/examples/e2e/consumer.js +++ b/examples/e2e/consumer.js @@ -1,86 +1,84 @@ -const express = require('express') -const request = require('superagent') -const server = express() -const API_HOST = process.env.API_HOST || 'http://localhost:8081' +const express = require("express"); +const request = require("superagent"); +const server = express(); +const API_HOST = process.env.API_HOST || "http://localhost:8081"; // Fetch animals who are currently 'available' from the // Animal Service const availableAnimals = () => { return request .get(`${API_HOST}/animals/available`) - .then(res => res.body, - () => []) -} + .then(res => res.body, () => []); +}; // Find animals by their ID from the Animal Service -const getAnimalById = (id) => { +const getAnimalById = id => { return request .get(`${API_HOST}/animals/${id}`) - .then(res => res.body, - () => null) -} + .then(res => res.body, () => null); +}; // Suggestions function: // Given availability and sex etc. find available suitors, // and give them a 'score' const suggestion = mate => { const predicates = [ - ((candidate, animal) => candidate.id !== animal.id), - ((candidate, animal) => candidate.gender !== animal.gender), - ((candidate, animal) => candidate.animal === animal.animal) - ] + (candidate, animal) => candidate.id !== animal.id, + (candidate, animal) => candidate.gender !== animal.gender, + (candidate, animal) => candidate.animal === animal.animal + ]; - const weights = [ - ((candidate, animal) => Math.abs(candidate.age - animal.age)) - ] + const weights = [(candidate, animal) => Math.abs(candidate.age - animal.age)]; return availableAnimals().then(available => { - const eligible = available.filter(a => !predicates.map(p => p(a, mate)).includes(false)) + const eligible = available.filter( + a => !predicates.map(p => p(a, mate)).includes(false) + ); return { suggestions: eligible.map(candidate => { const score = weights.reduce((acc, weight) => { - return acc - weight(candidate, mate) - }, 100) + return acc - weight(candidate, mate); + }, 100); return { score, - 'animal': candidate - } + animal: candidate + }; }) - } - }) -} + }; + }); +}; // Creates a mate for suggestions -const createMateForDates = (mate) => { +const createMateForDates = mate => { return request .post(`${API_HOST}/animals`) .send(mate) - .set('Content-Type', 'application/json; charset=utf-8') -} + .set("Content-Type", "application/json; charset=utf-8"); +}; // Suggestions API -server.get('/suggestions/:animalId', (req, res) => { +server.get("/suggestions/:animalId", (req, res) => { if (!req.params.animalId) { - res.writeHead(400) - res.end() + res.writeHead(400); + res.end(); } request(`${API_HOST}/animals/${req.params.animalId}`, (err, r) => { if (!err && r.statusCode === 200) { suggestion(r.body).then(suggestions => { - res.json(suggestions) - }) + res.json(suggestions); + }); } else if (r && r.statusCode === 404) { - res.writeHead(404) - res.end() + res.writeHead(404); + res.end(); } else { - res.writeHead(500) - res.end() + res.writeHead(500); + res.end(); } - }) -}) + }); +}); module.exports = { server, @@ -88,4 +86,4 @@ module.exports = { createMateForDates, suggestion, getAnimalById -} +}; diff --git a/examples/e2e/consumerService.js b/examples/e2e/consumerService.js index b80ae9906..d52a6de3e 100644 --- a/examples/e2e/consumerService.js +++ b/examples/e2e/consumerService.js @@ -1,5 +1,5 @@ -const { server } = require('./consumer.js') +const { server } = require("./consumer.js"); server.listen(8080, () => { - console.log('Animal Matching Service listening on http://localhots:8080') -}) + console.log("Animal Matching Service listening on http://localhots:8080"); +}); diff --git a/examples/e2e/provider.js b/examples/e2e/provider.js index e4e7f4454..3e2ec7f82 100644 --- a/examples/e2e/provider.js +++ b/examples/e2e/provider.js @@ -1,79 +1,81 @@ -const express = require('express') -const cors = require('cors') -const bodyParser = require('body-parser') -const Repository = require('./repository') +const express = require("express"); +const cors = require("cors"); +const bodyParser = require("body-parser"); +const Repository = require("./repository"); -const server = express() -server.use(cors()) -server.use(bodyParser.json()) -server.use(bodyParser.urlencoded({ - extended: true -})) +const server = express(); +server.use(cors()); +server.use(bodyParser.json()); +server.use( + bodyParser.urlencoded({ + extended: true + }) +); server.use((req, res, next) => { - res.header('Content-Type', 'application/json; charset=utf-8') - next() -}) + res.header("Content-Type", "application/json; charset=utf-8"); + next(); +}); -const animalRepository = new Repository() +const animalRepository = new Repository(); // Load default data into a repository const importData = () => { - const data = require('./data/animalData.json') + const data = require("./data/animalData.json"); data.reduce((a, v) => { - v.id = a + 1 - animalRepository.insert(v) - return a + 1 - }, 0) -} + v.id = a + 1; + animalRepository.insert(v); + return a + 1; + }, 0); +}; // List all animals with 'available' eligibility const availableAnimals = () => { return animalRepository.fetchAll().filter(a => { - return a.eligibility.available - }) -} + return a.eligibility.available; + }); +}; // Get all animals -server.get('/animals', (req, res) => { - res.json(animalRepository.fetchAll()) -}) +server.get("/animals", (req, res) => { + res.json(animalRepository.fetchAll()); +}); // Get all available animals -server.get('/animals/available', (req, res) => { - res.json(availableAnimals()) -}) +server.get("/animals/available", (req, res) => { + res.json(availableAnimals()); +}); // Find an animal by ID -server.get('/animals/:id', (req, res) => { - const response = animalRepository.getById(req.params.id) +server.get("/animals/:id", (req, res) => { + const response = animalRepository.getById(req.params.id); if (response) { - res.end(JSON.stringify(response)) + res.end(JSON.stringify(response)); } else { - res.writeHead(404) - res.end() + res.writeHead(404); + res.end(); } -}) +}); // Register a new Animal for the service -server.post('/animals', (req, res) => { - const animal = req.body +server.post("/animals", (req, res) => { + const animal = req.body; // Really basic validation if (!animal || !animal.first_name) { - res.writeHead(400) - res.end() + res.writeHead(400); + res.end(); - return + return; } - animal.id = animalRepository.fetchAll().length - animalRepository.insert(animal) + animal.id = animalRepository.fetchAll().length; + animalRepository.insert(animal); - res.json(animal) -}) + res.json(animal); +}); module.exports = { server, importData, animalRepository -} +}; diff --git a/examples/e2e/providerService.js b/examples/e2e/providerService.js index 0e1c82dc0..5c0971d38 100644 --- a/examples/e2e/providerService.js +++ b/examples/e2e/providerService.js @@ -1,6 +1,6 @@ -const { server, importData } = require('./provider.js') -importData() +const { server, importData } = require("./provider.js"); +importData(); server.listen(8081, () => { - console.log('Animal Profile Service listening on http://localhost:8081') -}) + console.log("Animal Profile Service listening on http://localhost:8081"); +}); diff --git a/examples/e2e/repository.js b/examples/e2e/repository.js index 04fe65b8e..cc9c0fb68 100644 --- a/examples/e2e/repository.js +++ b/examples/e2e/repository.js @@ -1,24 +1,24 @@ // Simple object repository class Repository { constructor() { - this.entities = [] + this.entities = []; } fetchAll() { - return this.entities + return this.entities; } getById(id) { - return this.entities.find((entity) => id == entity.id) + return this.entities.find(entity => id == entity.id); } insert(entity) { - this.entities.push(entity) + this.entities.push(entity); } clear() { - this.entities = [] + this.entities = []; } } -module.exports = Repository +module.exports = Repository; diff --git a/examples/e2e/test/consumer.spec.js b/examples/e2e/test/consumer.spec.js index 4d2acbbc7..91bc48b2d 100644 --- a/examples/e2e/test/consumer.spec.js +++ b/examples/e2e/test/consumer.spec.js @@ -1,52 +1,49 @@ -const path = require('path') -const chai = require('chai') -const chaiAsPromised = require('chai-as-promised') -const expect = chai.expect -const { Pact, Matchers } = require('../../../dist/pact'); -const MOCK_SERVER_PORT = 1234 -const LOG_LEVEL = process.env.LOG_LEVEL || 'WARN' +const path = require("path"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +const expect = chai.expect; +const { Pact, Matchers } = require("../../../dist/pact"); +const MOCK_SERVER_PORT = 1234; +const LOG_LEVEL = process.env.LOG_LEVEL || "WARN"; -chai.use(chaiAsPromised) +chai.use(chaiAsPromised); -describe('Pact', () => { +describe("Pact", () => { const provider = new Pact({ - consumer: 'Matching Service', - provider: 'Animal Profile Service', + consumer: "Matching Service", + provider: "Animal Profile Service", port: MOCK_SERVER_PORT, - log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'), - dir: path.resolve(process.cwd(), 'pacts'), + log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), + dir: path.resolve(process.cwd(), "pacts"), logLevel: LOG_LEVEL, spec: 2 - }) + }); // Alias flexible matchers for simplicity const { eachLike, like, term, iso8601DateTimeWithMillis } = Matchers; // Animal we want to match :) const suitor = { - 'id': 2, - 'available_from': '2017-12-04T14:47:18.582Z', - 'first_name': 'Nanny', - 'animal': 'goat', - 'last_name': 'Doe', - 'age': 27, - 'gender': 'F', - 'location': { - 'description': 'Werribee Zoo', - 'country': 'Australia', - 'post_code': 3000 + id: 2, + available_from: "2017-12-04T14:47:18.582Z", + first_name: "Nanny", + animal: "goat", + last_name: "Doe", + age: 27, + gender: "F", + location: { + description: "Werribee Zoo", + country: "Australia", + post_code: 3000 }, - 'eligibility': { - 'available': true, - 'previously_married': true + eligibility: { + available: true, + previously_married: true }, - 'interests': [ - 'walks in the garden/meadow', - 'parkour' - ] - } + interests: ["walks in the garden/meadow", "parkour"] + }; - const MIN_ANIMALS = 2 + const MIN_ANIMALS = 2; // Define animal payload, with flexible matchers // @@ -55,32 +52,32 @@ describe('Pact', () => { // It is also import here to not put in expectations for parts of the // API we don't care about const animalBodyExpectation = { - 'id': like(1), - 'available_from': iso8601DateTimeWithMillis(), - 'first_name': like('Billy'), - 'last_name': like('Goat'), - 'animal': like('goat'), - 'age': like(21), - 'gender': term({ - matcher: 'F|M', - generate: 'M' + id: like(1), + available_from: iso8601DateTimeWithMillis(), + first_name: like("Billy"), + last_name: like("Goat"), + animal: like("goat"), + age: like(21), + gender: term({ + matcher: "F|M", + generate: "M" }), - 'location': { - 'description': like('Melbourne Zoo'), - 'country': like('Australia'), - 'post_code': like(3000) + location: { + description: like("Melbourne Zoo"), + country: like("Australia"), + post_code: like(3000) }, - 'eligibility': { - 'available': like(true), - 'previously_married': like(false) + eligibility: { + available: like(true), + previously_married: like(false) }, - 'interests': eachLike('walks in the garden/meadow') - } + interests: eachLike("walks in the garden/meadow") + }; // Define animal list payload, reusing existing object matcher const animalListExpectation = eachLike(animalBodyExpectation, { min: MIN_ANIMALS - }) + }); // Setup a Mock Server before unit tests run. // This server acts as a Test Double for the real Provider API. @@ -88,131 +85,150 @@ describe('Pact', () => { // to act like the Provider // It also sets up expectations for what requests are to come, and will fail // if the calls are not seen. - before(() => provider.setup()) + before(() => provider.setup()); // After each individual test (one or more interactions) // we validate that the correct request came through. // This ensures what we _expect_ from the provider, is actually // what we've asked for (and is what gets captured in the contract) - afterEach(() => provider.verify()) + afterEach(() => provider.verify()); // Configure and import consumer API // Note that we update the API endpoint to point at the Mock Service - process.env.API_HOST = `http://localhost:${MOCK_SERVER_PORT}` + process.env.API_HOST = `http://localhost:${MOCK_SERVER_PORT}`; const { createMateForDates, suggestion, getAnimalById - } = require('../consumer') + } = require("../consumer"); // Verify service client works as expected. // // Note that we don't call the consumer API endpoints directly, but // use unit-style tests that test the collaborating function behaviour - // we want to test the function that is calling the external service. - describe('when a call to list all animals from the Animal Service is made', () => { - describe('and there are animals in the database', () => { + describe("when a call to list all animals from the Animal Service is made", () => { + describe("and there are animals in the database", () => { before(() => provider.addInteraction({ - state: 'Has some animals', - uponReceiving: 'a request for all animals', + state: "Has some animals", + uponReceiving: "a request for all animals", withRequest: { - method: 'GET', - path: '/animals/available' + method: "GET", + path: "/animals/available" }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json; charset=utf-8' + "Content-Type": "application/json; charset=utf-8" }, body: animalListExpectation } - })) + }) + ); + + it("returns a list of animals", done => { + const suggestedMates = suggestion(suitor); + + expect(suggestedMates).to.eventually.have.deep.property( + "suggestions[0].score", + 94 + ); + expect(suggestedMates) + .to.eventually.have.property("suggestions") + .with.lengthOf(MIN_ANIMALS) + .notify(done); + }); + }); + }); + + describe("when a call to the Animal Service is made to retreive a single animal by ID", () => { + describe("and there is an animal in the DB with ID 1", () => { + before(() => + provider.addInteraction({ + state: "Has an animal with ID 1", + uponReceiving: "a request for an animal with ID 1", + withRequest: { + method: "GET", + path: term({ generate: "/animals/1", matcher: "/animals/[0-9]+" }) + }, + willRespondWith: { + status: 200, + headers: { + "Content-Type": "application/json; charset=utf-8" + }, + body: animalBodyExpectation + } + }) + ); - it('returns a list of animals', done => { - const suggestedMates = suggestion(suitor) + it("returns the animal", done => { + const suggestedMates = getAnimalById(11); - expect(suggestedMates).to.eventually.have.deep.property('suggestions[0].score', 94) - expect(suggestedMates).to.eventually.have.property('suggestions').with.lengthOf(MIN_ANIMALS).notify(done) - }) - }) - }) - - describe('when a call to the Animal Service is made to retreive a single animal by ID', () => { - describe('and there is an animal in the DB with ID 1', () => { - before(() => provider.addInteraction({ - state: 'Has an animal with ID 1', - uponReceiving: 'a request for an animal with ID 1', + expect(suggestedMates) + .to.eventually.have.deep.property("id", 1) + .notify(done); + }); + }); + + describe("and there no animals in the database", () => { + before(() => + provider.addInteraction({ + state: "Has no animals", + uponReceiving: "a request for an animal with ID 100", + withRequest: { + method: "GET", + path: "/animals/100" + }, + willRespondWith: { + status: 404 + } + }) + ); + + it("returns a 404", done => { + // uncomment below to test a failed verify + // const suggestedMates = getAnimalById(123) + const suggestedMates = getAnimalById(100); + + expect(suggestedMates) + .to.eventually.be.a("null") + .notify(done); + }); + }); + }); + + describe("when a call to the Animal Service is made to create a new mate", () => { + before(() => + provider.addInteraction({ + uponReceiving: "a request to create a new mate", withRequest: { - method: 'GET', - path: term({ generate: '/animals/1', matcher: '/animals/[0-9]+' }) + method: "POST", + path: "/animals", + body: like(suitor), + headers: { + "Content-Type": "application/json; charset=utf-8" + } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json; charset=utf-8' + "Content-Type": "application/json; charset=utf-8" }, - body: animalBodyExpectation - } - })) - - it('returns the animal', done => { - const suggestedMates = getAnimalById(11) - - expect(suggestedMates).to.eventually.have.deep.property('id', 1).notify(done) - }) - }) - - describe('and there no animals in the database', () => { - before(() => provider.addInteraction({ - state: 'Has no animals', - uponReceiving: 'a request for an animal with ID 100', - withRequest: { - method: 'GET', - path: '/animals/100' - }, - willRespondWith: { - status: 404 + body: like(suitor) } - })) - - it('returns a 404', done => { - // uncomment below to test a failed verify - // const suggestedMates = getAnimalById(123) - const suggestedMates = getAnimalById(100) - - expect(suggestedMates).to.eventually.be.a('null').notify(done) }) - }) - }) - - describe('when a call to the Animal Service is made to create a new mate', () => { - before(() => provider.addInteraction({ - uponReceiving: 'a request to create a new mate', - withRequest: { - method: 'POST', - path: '/animals', - body: like(suitor), - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - }, - willRespondWith: { - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: like(suitor) - } - })) + ); - it('should create a new mate', (done) => { - expect(createMateForDates(suitor)).to.eventually.be.fulfilled.notify(done) - }) - }) + it("should create a new mate", done => { + expect(createMateForDates(suitor)).to.eventually.be.fulfilled.notify( + done + ); + }); + }); // Write pact files after(() => { - return provider.finalize() - }) -}) + return provider.finalize(); + }); +}); diff --git a/examples/e2e/test/provider.spec.js b/examples/e2e/test/provider.spec.js index a6f35e1c0..2b96d6d13 100644 --- a/examples/e2e/test/provider.spec.js +++ b/examples/e2e/test/provider.spec.js @@ -1,67 +1,59 @@ -const { - Verifier -} = require('../../../dist/pact') -const path = require('path') -const chai = require('chai') -const chaiAsPromised = require('chai-as-promised') -const expect = chai.expect -const { - VerifierOptions -} = require('@pact-foundation/pact-node'); -chai.use(chaiAsPromised) -const { - server, - importData, - animalRepository -} = require('../provider.js') +const { Verifier } = require("../../../dist/pact"); +const path = require("path"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +const expect = chai.expect; +const { VerifierOptions } = require("@pact-foundation/pact-node"); +chai.use(chaiAsPromised); +const { server, importData, animalRepository } = require("../provider.js"); -server.post('/setup', (req, res) => { - const state = req.body.state +server.post("/setup", (req, res) => { + const state = req.body.state; - animalRepository.clear() + animalRepository.clear(); switch (state) { - case 'Has no animals': + case "Has no animals": // do nothing - break + break; default: - importData() + importData(); } - res.end() -}) + res.end(); +}); server.listen(8081, () => { - console.log('Animal Profile Service listening on http://localhost:8081') -}) + console.log("Animal Profile Service listening on http://localhost:8081"); +}); // Verify that the provider meets all consumer expectations -describe('Pact Verification', () => { - it('should validate the expectations of Matching Service', function () { // lexical binding required here - this.timeout(10000) +describe("Pact Verification", () => { + it("should validate the expectations of Matching Service", function() { + // lexical binding required here + this.timeout(10000); let opts = { - provider: 'Animal Profile Service', - providerBaseUrl: 'http://localhost:8081', - providerStatesSetupUrl: 'http://localhost:8081/setup', + provider: "Animal Profile Service", + providerBaseUrl: "http://localhost:8081", + providerStatesSetupUrl: "http://localhost:8081/setup", // Fetch pacts from broker - pactBrokerUrl: 'https://test.pact.dius.com.au/', + pactBrokerUrl: "https://test.pact.dius.com.au/", // Fetch from broker with given tags - tags: ['prod', 'sit5'], + tags: ["prod", "sit5"], // Specific Remote pacts (doesn't need to be a broker) // pactFilesOrDirs: ['https://test.pact.dius.com.au/pacts/provider/Animal%20Profile%20Service/consumer/Matching%20Service/latest'], // Local pacts // pactFilesOrDirs: [path.resolve(process.cwd(), './pacts/matching_service-animal_profile_service.json')], - pactBrokerUsername: 'dXfltyFMgNOFZAxr8io9wJ37iUpY42M', - pactBrokerPassword: 'O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1', + pactBrokerUsername: "dXfltyFMgNOFZAxr8io9wJ37iUpY42M", + pactBrokerPassword: "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1", publishVerificationResult: true, providerVersion: "1.0.0", - customProviderHeaders: ['Authorization: basic e5e5e5e5e5e5e5'] - } + customProviderHeaders: ["Authorization: basic e5e5e5e5e5e5e5"] + }; - return new Verifier().verifyProvider(opts) - .then(output => { - console.log('Pact Verification Complete!') - console.log(output) - }) - }) -}) + return new Verifier().verifyProvider(opts).then(output => { + console.log("Pact Verification Complete!"); + console.log(output); + }); + }); +}); diff --git a/examples/e2e/test/publish.js b/examples/e2e/test/publish.js index 55c5edd24..8f1bf94e9 100644 --- a/examples/e2e/test/publish.js +++ b/examples/e2e/test/publish.js @@ -1,23 +1,33 @@ -const pact = require('@pact-foundation/pact-node') -const path = require('path') +const pact = require("@pact-foundation/pact-node"); +const path = require("path"); const opts = { - pactFilesOrDirs: [path.resolve(__dirname, '../pacts/matching_service-animal_profile_service.json')], - pactBroker: 'https://test.pact.dius.com.au', - pactBrokerUsername: 'dXfltyFMgNOFZAxr8io9wJ37iUpY42M', - pactBrokerPassword: 'O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1', - tags: ['prod', 'test'], - consumerVersion: '1.0.' + ((process.env.TRAVIS_BUILD_NUMBER) ? process.env.TRAVIS_BUILD_NUMBER : Math.floor(new Date() / 1000)) -} + pactFilesOrDirs: [ + path.resolve( + __dirname, + "../pacts/matching_service-animal_profile_service.json" + ) + ], + pactBroker: "https://test.pact.dius.com.au", + pactBrokerUsername: "dXfltyFMgNOFZAxr8io9wJ37iUpY42M", + pactBrokerPassword: "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1", + tags: ["prod", "test"], + consumerVersion: + "1.0." + + (process.env.TRAVIS_BUILD_NUMBER + ? process.env.TRAVIS_BUILD_NUMBER + : Math.floor(new Date() / 1000)) +}; -pact.publishPacts(opts) +pact + .publishPacts(opts) .then(() => { - console.log('Pact contract publishing complete!') - console.log('') - console.log('Head over to https://test.pact.dius.com.au/ and login with') - console.log('=> Username: dXfltyFMgNOFZAxr8io9wJ37iUpY42M') - console.log('=> Password: O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1') - console.log('to see your published contracts.') + console.log("Pact contract publishing complete!"); + console.log(""); + console.log("Head over to https://test.pact.dius.com.au/ and login with"); + console.log("=> Username: dXfltyFMgNOFZAxr8io9wJ37iUpY42M"); + console.log("=> Password: O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1"); + console.log("to see your published contracts."); }) .catch(e => { - console.log('Pact contract publishing failed: ', e) - }) + console.log("Pact contract publishing failed: ", e); + }); diff --git a/examples/graphql/README.md b/examples/graphql/README.md index 1665d5bd2..03a1ee985 100644 --- a/examples/graphql/README.md +++ b/examples/graphql/README.md @@ -5,6 +5,7 @@ PactJS could provider a simple helper function that wraps the GraphQL request in suitable for Pact (after all, GraphQL is simply an interface over HTTP) Test it out here: + ``` npm run test:consumer npm run test:provider diff --git a/examples/graphql/consumer.spec.ts b/examples/graphql/consumer.spec.ts index 16a3a08df..f8417198f 100644 --- a/examples/graphql/consumer.spec.ts +++ b/examples/graphql/consumer.spec.ts @@ -16,7 +16,7 @@ describe("GraphQL example", () => { log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), dir: path.resolve(process.cwd(), "pacts"), consumer: "GraphQLConsumer", - provider: "GraphQLProvider", + provider: "GraphQLProvider" }); before(() => provider.setup()); @@ -29,21 +29,21 @@ describe("GraphQL example", () => { .withQuery(`{ hello }`) .withRequest({ path: "/graphql", - method: "POST", + method: "POST" }) .withVariables({ - foo: "bar", + foo: "bar" }) .willRespondWith({ status: 200, headers: { - "Content-Type": "application/json; charset=utf-8", + "Content-Type": "application/json; charset=utf-8" }, body: { data: { - hello: like("Hello world!"), - }, - }, + hello: like("Hello world!") + } + } }); return provider.addInteraction(graphqlQuery); }); diff --git a/examples/graphql/consumer.ts b/examples/graphql/consumer.ts index a7f999859..de8d10cbb 100644 --- a/examples/graphql/consumer.ts +++ b/examples/graphql/consumer.ts @@ -1,4 +1,4 @@ -import { ApolloClient, HttpLink } from "apollo-boost"; +import { ApolloClient } from "apollo-boost"; import { InMemoryCache } from "apollo-cache-inmemory"; import gql from "graphql-tag"; import { createHttpLink } from "apollo-link-http"; diff --git a/examples/graphql/provider.spec.ts b/examples/graphql/provider.spec.ts index 3b4bdee9b..565cf7124 100644 --- a/examples/graphql/provider.spec.ts +++ b/examples/graphql/provider.spec.ts @@ -9,11 +9,14 @@ const expect = chai.expect; const path = require("path"); chai.use(chaiAsPromised); -const server = app.listen(4000, () => console.log("Now browse to localhost:4000/graphql")); +const server = app.listen(4000, () => + console.log("Now browse to localhost:4000/graphql") +); // Verify that the provider meets all consumer expectations describe("Pact Verification", () => { - it("should validate the expectations of Matching Service", () => { // lexical binding required here + it("should validate the expectations of Matching Service", () => { + // lexical binding required here const opts = { provider: "GraphQLProvider", providerBaseUrl: "http://localhost:4000/graphql", @@ -24,14 +27,13 @@ describe("Pact Verification", () => { pactBrokerPassword: "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1", publishVerificationResult: true, providerVersion: "1.0.0", - tags: ["prod"], + tags: ["prod"] }; - return new Verifier().verifyProvider(opts) - .then((output) => { - console.log("Pact Verification Complete!"); - console.log(output); - server.close(); - }); + return new Verifier().verifyProvider(opts).then(output => { + console.log("Pact Verification Complete!"); + console.log(output); + server.close(); + }); }); }); diff --git a/examples/graphql/provider.ts b/examples/graphql/provider.ts index 2f5e2c8fb..0bbeff943 100644 --- a/examples/graphql/provider.ts +++ b/examples/graphql/provider.ts @@ -9,17 +9,20 @@ const schema = buildSchema(` `); const root = { - hello: () => "Hello world!", + hello: () => "Hello world!" }; const app = express(); export default app; -app.use("/graphql", graphqlHTTP({ - graphiql: true, - rootValue: root, - schema, -})); +app.use( + "/graphql", + graphqlHTTP({ + graphiql: true, + rootValue: root, + schema + }) +); export function start(): any { app.listen(4000, () => console.log("Now browse to localhost:4000/graphql")); diff --git a/examples/graphql/publish.js b/examples/graphql/publish.js index becbdf7fc..1dec2f2a1 100644 --- a/examples/graphql/publish.js +++ b/examples/graphql/publish.js @@ -1,23 +1,30 @@ -const pact = require('@pact-foundation/pact-node') -const path = require('path') +const pact = require("@pact-foundation/pact-node"); +const path = require("path"); const opts = { - pactFilesOrDirs: [path.resolve(__dirname, 'pacts/graphqlconsumer-graphqlprovider.json')], - pactBroker: 'https://test.pact.dius.com.au', - pactBrokerUsername: 'dXfltyFMgNOFZAxr8io9wJ37iUpY42M', - pactBrokerPassword: 'O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1', - tags: ['prod', 'test'], - consumerVersion: '1.0.' + ((process.env.TRAVIS_BUILD_NUMBER) ? process.env.TRAVIS_BUILD_NUMBER : Math.floor(new Date() / 1000)) -} + pactFilesOrDirs: [ + path.resolve(__dirname, "pacts/graphqlconsumer-graphqlprovider.json") + ], + pactBroker: "https://test.pact.dius.com.au", + pactBrokerUsername: "dXfltyFMgNOFZAxr8io9wJ37iUpY42M", + pactBrokerPassword: "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1", + tags: ["prod", "test"], + consumerVersion: + "1.0." + + (process.env.TRAVIS_BUILD_NUMBER + ? process.env.TRAVIS_BUILD_NUMBER + : Math.floor(new Date() / 1000)) +}; -pact.publishPacts(opts) +pact + .publishPacts(opts) .then(() => { - console.log('Pact contract publishing complete!') - console.log('') - console.log('Head over to https://test.pact.dius.com.au/ and login with') - console.log('=> Username: dXfltyFMgNOFZAxr8io9wJ37iUpY42M') - console.log('=> Password: O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1') - console.log('to see your published contracts.') + console.log("Pact contract publishing complete!"); + console.log(""); + console.log("Head over to https://test.pact.dius.com.au/ and login with"); + console.log("=> Username: dXfltyFMgNOFZAxr8io9wJ37iUpY42M"); + console.log("=> Password: O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1"); + console.log("to see your published contracts."); }) .catch(e => { - console.log('Pact contract publishing failed: ', e) - }) + console.log("Pact contract publishing failed: ", e); + }); diff --git a/examples/jest/__tests__/another.spec.js b/examples/jest/__tests__/another.spec.js index 41d337592..e35cf4a03 100644 --- a/examples/jest/__tests__/another.spec.js +++ b/examples/jest/__tests__/another.spec.js @@ -1,55 +1,57 @@ -'use strict' +"use strict"; -const path = require('path') -const Pact = require('../../../dist/pact').Pact -const getMeCats = require('../index').getMeCats +const path = require("path"); +const Pact = require("../../../dist/pact").Pact; +const getMeCats = require("../index").getMeCats; describe("Cat's API", () => { - let url = 'http://localhost' + let url = "http://localhost"; - const EXPECTED_BODY = [{ - cat: 2 - }] + const EXPECTED_BODY = [ + { + cat: 2 + } + ]; describe("another works", () => { beforeEach(() => { const interaction = { - state: 'i have a list of games', - uponReceiving: 'a request for games', + state: "i have a list of games", + uponReceiving: "a request for games", withRequest: { - method: 'GET', - path: '/cats', + method: "GET", + path: "/cats", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, body: EXPECTED_BODY } - } - return provider.addInteraction(interaction) - }) + }; + return provider.addInteraction(interaction); + }); // add expectations - it('returns a sucessful body', done => { + it("returns a sucessful body", done => { return getMeCats({ - url, - port - }) + url, + port + }) .then(response => { - expect(response.headers['content-type']).toEqual('application/json') - expect(response.data).toEqual(EXPECTED_BODY) - expect(response.status).toEqual(200) - done() + expect(response.headers["content-type"]).toEqual("application/json"); + expect(response.data).toEqual(EXPECTED_BODY); + expect(response.status).toEqual(200); + done(); }) .then(() => provider.verify()); - }) + }); // verify with Pact, and reset expectations //afterEach(() => provider.verify()) - }) -}) + }); +}); diff --git a/examples/jest/__tests__/index.spec.js b/examples/jest/__tests__/index.spec.js index 0763673ca..45770e4ed 100644 --- a/examples/jest/__tests__/index.spec.js +++ b/examples/jest/__tests__/index.spec.js @@ -1,50 +1,52 @@ -'use strict' +"use strict"; -const getMeDogs = require('../index').getMeDogs +const getMeDogs = require("../index").getMeDogs; describe("Dog's API", () => { - let url = 'http://localhost' + let url = "http://localhost"; - const EXPECTED_BODY = [{ - dog: 1 - }] + const EXPECTED_BODY = [ + { + dog: 1 + } + ]; describe("works", () => { beforeEach(() => { const interaction = { - state: 'i have a list of projects', - uponReceiving: 'a request for projects', + state: "i have a list of projects", + uponReceiving: "a request for projects", withRequest: { - method: 'GET', - path: '/dogs', + method: "GET", + path: "/dogs", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, body: EXPECTED_BODY } - } - return provider.addInteraction(interaction) - }) + }; + return provider.addInteraction(interaction); + }); // add expectations - it('returns a sucessful body', done => { + it("returns a sucessful body", done => { return getMeDogs({ - url, - port - }) + url, + port + }) .then(response => { - expect(response.headers['content-type']).toEqual('application/json') - expect(response.data).toEqual(EXPECTED_BODY) - expect(response.status).toEqual(200) - done() + expect(response.headers["content-type"]).toEqual("application/json"); + expect(response.data).toEqual(EXPECTED_BODY); + expect(response.status).toEqual(200); + done(); }) .then(() => provider.verify()); - }) - }) -}) + }); + }); +}); diff --git a/examples/jest/__tests__/multipleSpecs.spec.js b/examples/jest/__tests__/multipleSpecs.spec.js index a502000a2..e3fbc4ea8 100644 --- a/examples/jest/__tests__/multipleSpecs.spec.js +++ b/examples/jest/__tests__/multipleSpecs.spec.js @@ -1,91 +1,91 @@ -'use strict' +"use strict"; -const getMeDogs = require('../index').getMeDogs +const getMeDogs = require("../index").getMeDogs; describe("Dog's API", () => { - let url = 'http://localhost' + let url = "http://localhost"; - const EXPECTED_BODY = [{ - dog: 1 - }] + const EXPECTED_BODY = [ + { + dog: 1 + } + ]; afterEach(() => { - return provider.verify() - }) + return provider.verify(); + }); - describe('works', () => { + describe("works", () => { beforeEach(() => { const interaction = { - state: 'i have a list of projects', - uponReceiving: 'a request for projects', + state: "i have a list of projects", + uponReceiving: "a request for projects", withRequest: { - method: 'GET', - path: '/dogs', + method: "GET", + path: "/dogs", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, body: EXPECTED_BODY } - } - return provider.addInteraction(interaction) - }) + }; + return provider.addInteraction(interaction); + }); // add expectations - it('returns a sucessful body', done => { + it("returns a sucessful body", done => { return getMeDogs({ url, port - }) - .then(response => { - expect(response.headers['content-type']).toEqual('application/json') - expect(response.data).toEqual(EXPECTED_BODY) - expect(response.status).toEqual(200) - done() - }) - }) - }) + }).then(response => { + expect(response.headers["content-type"]).toEqual("application/json"); + expect(response.data).toEqual(EXPECTED_BODY); + expect(response.status).toEqual(200); + done(); + }); + }); + }); - describe('works again', () => { + describe("works again", () => { beforeEach(() => { const interaction = { - state: 'i have a list of projects again', - uponReceiving: 'a request for projects again', + state: "i have a list of projects again", + uponReceiving: "a request for projects again", withRequest: { - method: 'GET', - path: '/dogs', + method: "GET", + path: "/dogs", headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { status: 200, headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, body: EXPECTED_BODY } - } - return provider.addInteraction(interaction) - }) + }; + return provider.addInteraction(interaction); + }); // add expectations - it('returns a sucessful body', done => { + it("returns a sucessful body", done => { return getMeDogs({ url, port - }) - .then(response => { - expect(response.headers['content-type']).toEqual('application/json') - expect(response.data).toEqual(EXPECTED_BODY) - expect(response.status).toEqual(200) - done() - }) - }) - }) -}) + }).then(response => { + expect(response.headers["content-type"]).toEqual("application/json"); + expect(response.data).toEqual(EXPECTED_BODY); + expect(response.status).toEqual(200); + done(); + }); + }); + }); +}); diff --git a/examples/serverless/consumer/index.js b/examples/serverless/consumer/index.js index a4ab2c271..42835f4d3 100644 --- a/examples/serverless/consumer/index.js +++ b/examples/serverless/consumer/index.js @@ -1,7 +1,5 @@ 'use strict'; -const AWS = require('aws-sdk'); - // Consumer handler, responsible for extracting message from SNS // and dealing with lambda-related things. const handler = (event, context, callback) => { diff --git a/karma/jasmine/client-spec.js b/karma/jasmine/client-spec.js index ad7dc5a30..24e319d88 100644 --- a/karma/jasmine/client-spec.js +++ b/karma/jasmine/client-spec.js @@ -1,37 +1,36 @@ /*eslint-disable*/ -(function () { +(function() { + describe("Client", function() { + var client, provider; - describe("Client", function () { - - var client, provider - - beforeAll(function (done) { - client = example.createClient('http://localhost:1234') + beforeAll(function(done) { + client = example.createClient("http://localhost:1234"); provider = new Pact.PactWeb({ - consumer: 'Karma Jasmine', - provider: 'Hello' - }) + consumer: "Karma Jasmine", + provider: "Hello" + }); // required for slower Travis CI environment - setTimeout(function () { - done() - }, 2000) + setTimeout(function() { + done(); + }, 2000); // Required if run with `singleRun: false` - provider.removeInteractions() - }) + provider.removeInteractions(); + }); - afterAll(function (done) { - provider.finalize().then(done, done.fail) - }) + afterAll(function(done) { + provider.finalize().then(done, done.fail); + }); - describe("sayHello", function () { - beforeAll(function (done) { - provider.addInteraction({ - uponReceiving: 'a request for hello', + describe("sayHello", function() { + beforeAll(function(done) { + provider + .addInteraction({ + uponReceiving: "a request for hello", withRequest: { - method: 'GET', - path: '/sayHello' + method: "GET", + path: "/sayHello" }, willRespondWith: { status: 200, @@ -43,39 +42,37 @@ } } }) - .then(done, done.fail) - }) + .then(done, done.fail); + }); - it("should say hello", function (done) { + it("should say hello", function(done) { //Run the tests - client.sayHello() - .then(function (data) { - expect(JSON.parse(data.responseText)).toEqual({ - reply: "Hello" - }) - provider.verify().then(done, done.fail) - }, done.fail) - }) - }) - - describe("findFriendsByAgeAndChildren", function () { - - beforeAll(function (done) { + client.sayHello().then(function(data) { + expect(JSON.parse(data.responseText)).toEqual({ + reply: "Hello" + }); + provider.verify().then(done, done.fail); + }, done.fail); + }); + }); + + describe("findFriendsByAgeAndChildren", function() { + beforeAll(function(done) { provider .addInteraction({ - uponReceiving: 'a request friends', + uponReceiving: "a request friends", withRequest: { - method: 'GET', - path: '/friends', + method: "GET", + path: "/friends", query: { age: Pact.Matchers.term({ - generate: '30', - matcher: '\\d+' + generate: "30", + matcher: "\\d+" }), //remember query params are always strings - children: ['Mary Jane', 'James'] // specify params with multiple values in an array + children: ["Mary Jane", "James"] // specify params with multiple values in an array }, headers: { - 'Accept': 'application/json' + Accept: "application/json" } }, willRespondWith: { @@ -84,47 +81,52 @@ "Content-Type": "application/json" }, body: { - friends: Pact.Matchers.eachLike({ - name: Pact.Matchers.somethingLike('Sue') // Doesn't tie the Provider to a particular friend such as 'Sue' - }, { - min: 1 - }) + friends: Pact.Matchers.eachLike( + { + name: Pact.Matchers.somethingLike("Sue") // Doesn't tie the Provider to a particular friend such as 'Sue' + }, + { + min: 1 + } + ) } } }) - .then(done, done.fail) - }) + .then(done, done.fail); + }); - it("should return some friends", function (done) { + it("should return some friends", function(done) { //Run the tests - client.findFriendsByAgeAndChildren('33', ['Mary Jane', 'James']) - .then(function (res) { + client + .findFriendsByAgeAndChildren("33", ["Mary Jane", "James"]) + .then(function(res) { expect(JSON.parse(res.responseText)).toEqual({ - friends: [{ - name: 'Sue' - }] - }) - provider.verify().then(done, done.fail) - }, done.fail) - }) - }) - - describe("unfriendMe", function () { - - afterEach(function () { - return provider.removeInteractions() - }) - - describe("when I have some friends", function () { - - beforeAll(function (done) { + friends: [ + { + name: "Sue" + } + ] + }); + provider.verify().then(done, done.fail); + }, done.fail); + }); + }); + + describe("unfriendMe", function() { + afterEach(function() { + return provider.removeInteractions(); + }); + + describe("when I have some friends", function() { + beforeAll(function(done) { //Add interaction - provider.addInteraction({ - state: 'I am friends with Fred', - uponReceiving: 'a request to unfriend', + provider + .addInteraction({ + state: "I am friends with Fred", + uponReceiving: "a request to unfriend", withRequest: { - method: 'PUT', - path: '/unfriendMe' + method: "PUT", + path: "/unfriendMe" }, willRespondWith: { status: 200, @@ -136,31 +138,31 @@ } } }) - .then(done, done.fail) - }) + .then(done, done.fail); + }); - it("should unfriend me", function (done) { + it("should unfriend me", function(done) { //Run the tests - client.unfriendMe() - .then(function (res) { - expect(JSON.parse(res.responseText)).toEqual({ - reply: "Bye" - }) - provider.verify().then(done, done.fail) - }, done.fail) - }) - }) + client.unfriendMe().then(function(res) { + expect(JSON.parse(res.responseText)).toEqual({ + reply: "Bye" + }); + provider.verify().then(done, done.fail); + }, done.fail); + }); + }); // verify with Pact, and reset expectations - describe("when there are no friends", function () { - beforeAll(function (done) { + describe("when there are no friends", function() { + beforeAll(function(done) { //Add interaction - provider.addInteraction({ - state: 'I have no friends', - uponReceiving: 'a request to unfriend', + provider + .addInteraction({ + state: "I have no friends", + uponReceiving: "a request to unfriend", withRequest: { - method: 'PUT', - path: '/unfriendMe' + method: "PUT", + path: "/unfriendMe" }, willRespondWith: { status: 404, @@ -169,20 +171,23 @@ } } }) - .then(done, done.fail) - }) + .then(done, done.fail); + }); - it("returns an error message", function (done) { + it("returns an error message", function(done) { //Run the tests - client.unfriendMe().then(function () { - done(new Error('expected request to /unfriend me to fail')) - }, function (e) { - expect(e.status).toEqual(404) - expect(JSON.parse(e.responseText).error).toEqual('No friends :(') - provider.verify().then(done, done.fail) - }) - }) - }) - }) - }) -})() + client.unfriendMe().then( + function() { + done(new Error("expected request to /unfriend me to fail")); + }, + function(e) { + expect(e.status).toEqual(404); + expect(JSON.parse(e.responseText).error).toEqual("No friends :("); + provider.verify().then(done, done.fail); + } + ); + }); + }); + }); + }); +})(); diff --git a/karma/jasmine/client.js b/karma/jasmine/client.js index 126aeab8b..e406cbf27 100644 --- a/karma/jasmine/client.js +++ b/karma/jasmine/client.js @@ -1,57 +1,59 @@ /*eslint-disable*/ var example = example || {}; -(function () { - +(function() { var localBaseUrl; - this.createClient = function (baseUrl) { + this.createClient = function(baseUrl) { localBaseUrl = baseUrl; return this; }; - this.sayHello = function () { + this.sayHello = function() { //Makes a synchronous request var xhr = new XMLHttpRequest(); - xhr.open('GET', localBaseUrl + '/sayHello', false); + xhr.open("GET", localBaseUrl + "/sayHello", false); xhr.send(); return Promise.resolve(xhr); }; - this.findFriendsByAgeAndChildren = function (age, children) { - var url = localBaseUrl + '/friends?age=' + age + children.reduce(function (acc, item) { - return acc.concat("&children=" + item); - }, ''); + this.findFriendsByAgeAndChildren = function(age, children) { + var url = + localBaseUrl + + "/friends?age=" + + age + + children.reduce(function(acc, item) { + return acc.concat("&children=" + item); + }, ""); var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Accept', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Accept", "application/json"); xhr.send(); return Promise.resolve(xhr); }; - this.unfriendMe = function () { + this.unfriendMe = function() { //Makes an asynchronous request - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { var xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function () { + xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState === 4) { if (xmlhttp.status === 200) { - resolve(xmlhttp) + resolve(xmlhttp); } else if (xmlhttp.status === 404) { - xmlhttp.text = "No friends :(" + xmlhttp.text = "No friends :("; reject(xmlhttp); } else { reject(xmlhttp); } } - } + }; - xmlhttp.open('PUT', localBaseUrl + '/unfriendMe', true); + xmlhttp.open("PUT", localBaseUrl + "/unfriendMe", true); xmlhttp.send(); - }) + }); }; - -}).apply(example); +}.apply(example)); diff --git a/karma/jasmine/karma.chrome.conf.js b/karma/jasmine/karma.chrome.conf.js index f6f41ac6e..a5c8f7f4b 100644 --- a/karma/jasmine/karma.chrome.conf.js +++ b/karma/jasmine/karma.chrome.conf.js @@ -3,7 +3,6 @@ module.exports = function (config) { config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '.', @@ -12,11 +11,7 @@ module.exports = function (config) { frameworks: ['jasmine', 'pact'], // list of files / patterns to load in the browser - files: [ - '../../dist/pact.web.js', - 'client.js', - 'client-spec.js' - ], + files: ['../../dist/pact.web.js', 'client.js', 'client-spec.js'], // list of files to exclude exclude: [], diff --git a/karma/jasmine/karma.conf.js b/karma/jasmine/karma.conf.js index 12f669cfc..23bded41b 100644 --- a/karma/jasmine/karma.conf.js +++ b/karma/jasmine/karma.conf.js @@ -4,7 +4,6 @@ var path = require('path') module.exports = function (config) { config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '.', @@ -34,20 +33,19 @@ module.exports = function (config) { reporters: ['progress'], // Pact Providers - pact: [{ - port: 1234, - consumer: 'KarmaJasmineConsumer', - provider: 'KarmaJasmineProvider', - logLevel: 'DEBUG', - log: path.resolve(process.cwd(), 'logs', 'pact.log'), - dir: path.resolve(process.cwd(), 'pacts') - }], - - plugins: [ - 'karma-*', - '@pact-foundation/karma-pact' + pact: [ + { + port: 1234, + consumer: 'KarmaJasmineConsumer', + provider: 'KarmaJasmineProvider', + logLevel: 'DEBUG', + log: path.resolve(process.cwd(), 'logs', 'pact.log'), + dir: path.resolve(process.cwd(), 'pacts') + } ], + plugins: ['karma-*', '@pact-foundation/karma-pact'], + // web server port port: 9876, diff --git a/karma/mocha/client-spec.js b/karma/mocha/client-spec.js index 4a48ff4e7..3bcd54340 100644 --- a/karma/mocha/client-spec.js +++ b/karma/mocha/client-spec.js @@ -1,33 +1,31 @@ /*eslint-disable*/ -(function () { +(function() { + describe("Client", function() { + var client, provider; - describe("Client", function () { - - var client, provider - - before(function (done) { - client = example.createClient('http://localhost:1234') + before(function(done) { + client = example.createClient("http://localhost:1234"); provider = new Pact.PactWeb({ - consumer: 'Karma Mocha', - provider: 'Hello' - }) + consumer: "Karma Mocha", + provider: "Hello" + }); // required for slower Travis CI environment - setTimeout(function () { - done() - }, 1000) - }) + setTimeout(function() { + done(); + }, 1000); + }); - after(function () { - return provider.finalize() - }) + after(function() { + return provider.finalize(); + }); - describe("sayHello", function () { - before(function () { + describe("sayHello", function() { + before(function() { return provider.addInteraction({ - uponReceiving: 'a request for hello', + uponReceiving: "a request for hello", withRequest: { - method: 'GET', - path: '/sayHello' + method: "GET", + path: "/sayHello" }, willRespondWith: { status: 200, @@ -38,87 +36,90 @@ reply: "Hello" } } - }) - }) + }); + }); - it("should say hello", function () { + it("should say hello", function() { //Run the tests - return client.sayHello() - .then(function (data) { - expect(JSON.parse(data.responseText)).to.eql({ - reply: "Hello" - }) - // verify with Pact, and reset expectations - return provider.verify() - }) - }) - }) + return client.sayHello().then(function(data) { + expect(JSON.parse(data.responseText)).to.eql({ + reply: "Hello" + }); + // verify with Pact, and reset expectations + return provider.verify(); + }); + }); + }); - describe("findFriendsByAgeAndChildren", function () { - before(function () { - return provider - .addInteraction({ - uponReceiving: 'a request friends', - withRequest: { - method: 'GET', - path: '/friends', - query: { - age: Pact.Matchers.term({ - generate: '30', - matcher: '\\d+' - }), //remember query params are always strings - children: ['Mary Jane', 'James'] // specify params with multiple values in an array - }, - headers: { - 'Accept': 'application/json' - } + describe("findFriendsByAgeAndChildren", function() { + before(function() { + return provider.addInteraction({ + uponReceiving: "a request friends", + withRequest: { + method: "GET", + path: "/friends", + query: { + age: Pact.Matchers.term({ + generate: "30", + matcher: "\\d+" + }), //remember query params are always strings + children: ["Mary Jane", "James"] // specify params with multiple values in an array }, - willRespondWith: { - status: 200, - headers: { - "Content-Type": "application/json" - }, - body: { - friends: Pact.Matchers.eachLike({ - name: Pact.Matchers.somethingLike('Sue') // Doesn't tie the Provider to a particular friend such as 'Sue' - }, { + headers: { + Accept: "application/json" + } + }, + willRespondWith: { + status: 200, + headers: { + "Content-Type": "application/json" + }, + body: { + friends: Pact.Matchers.eachLike( + { + name: Pact.Matchers.somethingLike("Sue") // Doesn't tie the Provider to a particular friend such as 'Sue' + }, + { min: 1 - }) - } + } + ) } - }) - }) + } + }); + }); - it("should return some friends", function () { + it("should return some friends", function() { //Run the tests - return client.findFriendsByAgeAndChildren('33', ['Mary Jane', 'James']) - .then(function (res) { + return client + .findFriendsByAgeAndChildren("33", ["Mary Jane", "James"]) + .then(function(res) { expect(JSON.parse(res.responseText)).to.eql({ - friends: [{ - name: 'Sue' - }] - }) + friends: [ + { + name: "Sue" + } + ] + }); // verify with Pact, and reset expectations - return provider.verify() - }) - }) - }) + return provider.verify(); + }); + }); + }); - describe("unfriendMe", function () { + describe("unfriendMe", function() { + afterEach(function() { + return provider.removeInteractions(); + }); - afterEach(function () { - return provider.removeInteractions() - }) - - describe("when I have some friends", function () { - before(function () { + describe("when I have some friends", function() { + before(function() { //Add interaction return provider.addInteraction({ - state: 'I am friends with Fred', - uponReceiving: 'a request to unfriend', + state: "I am friends with Fred", + uponReceiving: "a request to unfriend", withRequest: { - method: 'PUT', - path: '/unfriendMe' + method: "PUT", + path: "/unfriendMe" }, willRespondWith: { status: 200, @@ -129,51 +130,53 @@ reply: "Bye" } } - }) - }) + }); + }); - it("should unfriend me", function () { + it("should unfriend me", function() { //Run the tests - return client.unfriendMe() - .then(function (res) { - expect(JSON.parse(res.responseText)).to.eql({ - reply: "Bye" - }) - // verify with Pact, and reset expectations - return provider.verify() - }) - }) - }) + return client.unfriendMe().then(function(res) { + expect(JSON.parse(res.responseText)).to.eql({ + reply: "Bye" + }); + // verify with Pact, and reset expectations + return provider.verify(); + }); + }); + }); // verify with Pact, and reset expectations - describe("when there are no friends", function () { - before(function () { + describe("when there are no friends", function() { + before(function() { //Add interaction return provider.addInteraction({ - state: 'I have no friends', - uponReceiving: 'a request to unfriend', + state: "I have no friends", + uponReceiving: "a request to unfriend", withRequest: { - method: 'PUT', - path: '/unfriendMe' + method: "PUT", + path: "/unfriendMe" }, willRespondWith: { status: 404, body: {} } - }) - }) + }); + }); - it("returns an error message", function () { + it("returns an error message", function() { //Run the tests - return client.unfriendMe().then(function () { - throw new Error('expected request to /unfriend me to fail') - }, function (e) { - expect(e).to.eql('No friends :(') - // verify with Pact, and reset expectations - return provider.verify() - }) - }) - }) - }) - }) -})() + return client.unfriendMe().then( + function() { + throw new Error("expected request to /unfriend me to fail"); + }, + function(e) { + expect(e).to.eql("No friends :("); + // verify with Pact, and reset expectations + return provider.verify(); + } + ); + }); + }); + }); + }); +})(); diff --git a/karma/mocha/client.js b/karma/mocha/client.js index 00436d569..f81d69a2a 100644 --- a/karma/mocha/client.js +++ b/karma/mocha/client.js @@ -1,42 +1,45 @@ /*eslint-disable*/ var example = example || {}; -(function () { - +(function() { var localBaseUrl; - this.createClient = function (baseUrl) { + this.createClient = function(baseUrl) { localBaseUrl = baseUrl; return this; }; - this.sayHello = function () { + this.sayHello = function() { //Makes a synchronous request var xhr = new XMLHttpRequest(); - xhr.open('GET', localBaseUrl + '/sayHello', false); + xhr.open("GET", localBaseUrl + "/sayHello", false); xhr.send(); return Promise.resolve(xhr); }; - this.findFriendsByAgeAndChildren = function (age, children) { - var url = localBaseUrl + '/friends?age=' + age + children.reduce(function (acc, item) { - return acc.concat("&children=" + item); - }, ''); + this.findFriendsByAgeAndChildren = function(age, children) { + var url = + localBaseUrl + + "/friends?age=" + + age + + children.reduce(function(acc, item) { + return acc.concat("&children=" + item); + }, ""); var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.setRequestHeader('Accept', 'application/json'); + xhr.open("GET", url, false); + xhr.setRequestHeader("Accept", "application/json"); xhr.send(); return Promise.resolve(xhr); }; - this.unfriendMe = function () { + this.unfriendMe = function() { //Makes an asynchronous request - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { var xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function () { + xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState === 4) { if (xmlhttp.status === 200) { resolve(xmlhttp); @@ -46,11 +49,10 @@ var example = example || {}; reject(xmlhttp); } } - } + }; - xmlhttp.open('PUT', localBaseUrl + '/unfriendMe', true); + xmlhttp.open("PUT", localBaseUrl + "/unfriendMe", true); xmlhttp.send(); - }) + }); }; - -}).apply(example); +}.apply(example)); diff --git a/karma/mocha/karma.conf.js b/karma/mocha/karma.conf.js index 00858929b..cc90a1dc9 100644 --- a/karma/mocha/karma.conf.js +++ b/karma/mocha/karma.conf.js @@ -4,7 +4,6 @@ var path = require('path') module.exports = function (config) { config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '.', @@ -35,22 +34,21 @@ module.exports = function (config) { reporters: ['progress'], // Pact Providers - pact: [{ - port: 1234, - consumer: 'KarmaMochaConsumer', - provider: 'KarmaMochaProvider', - logLevel: 'DEBUG', - log: path.resolve(process.cwd(), 'logs', 'pact.log'), - dir: path.resolve(process.cwd(), 'pacts') - }], + pact: [ + { + port: 1234, + consumer: 'KarmaMochaConsumer', + provider: 'KarmaMochaProvider', + logLevel: 'DEBUG', + log: path.resolve(process.cwd(), 'logs', 'pact.log'), + dir: path.resolve(process.cwd(), 'pacts') + } + ], // web server port port: 9876, - plugins: [ - 'karma-*', - '@pact-foundation/karma-pact' - ], + plugins: ['karma-*', '@pact-foundation/karma-pact'], // enable / disable colors in the output (reporters and logs) colors: true, diff --git a/package.json b/package.json index 76d48d264..cf74b628f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "deploy:prepare": "./scripts/create_npmrc_file.sh", "dist": "npm run compile && webpack --config ./config/webpack.web.config.js", "jscpd": "jscpd -p src -r json -o jscpd.json", - "lint": "standard && tslint -c tslint.json 'src/**/*.ts'", + "lint": "standard && tslint -c tslint.json -p tsconfig.json 'src/**/*.ts'", + "lint:fix": "standard --fix && prettier --write src/**.ts", "postdist": "npm t", "posttest": "npm run test:karma", "predist": "npm run clean && npm run lint && npm run jscpd", @@ -83,27 +84,29 @@ ] }, "dependencies": { - "@pact-foundation/pact-node": "^6.16.1", + "@pact-foundation/pact-node": "^6.19.11", "@types/express": "^4.11.1", "body-parser": "^1.18.2", "bunyan": "^1.8.12", "cli-color": "^1.1.0", "es6-object-assign": "^1.1.0", "es6-promise": "^4.1.1", + "eslint-plugin-prettier": "^3.0.0", "express": "^4.16.3", "graphql": "^0.13.2", "graphql-tag": "^2.9.1", - "lodash": "^4.17.4", + "lodash": "^4.17.11", "lodash.isfunction": "3.0.8", "lodash.isnil": "4.0.0", "lodash.isundefined": "3.0.1", "lodash.omit": "^4.5.0", "lodash.omitby": "4.6.0", "pkginfo": "^0.4.1", - "popsicle": "^9.2.0" + "popsicle": "^9.2.0", + "prettier": "^1.14.3" }, "devDependencies": { - "@pact-foundation/karma-pact": "~2.1.5", + "@pact-foundation/karma-pact": "~2.1.9", "@types/bluebird": "^3.5.20", "@types/bunyan": "^1.8.3", "@types/chai": "^4.0.3", @@ -131,14 +134,15 @@ "bunyan-prettystream": "^0.1.3", "chai": "~4.1.2", "chai-as-promised": "^7.1.1", - "coveralls": "^2.13.3", + "coveralls": "^3.0.2", "enhanced-resolve": "^3.4.1", + "eslint-config-prettier": "^3.1.0", "imports-loader": "~0.7.1", "istanbul": "~0.4.5", "jasmine-core": "~2.9.1", "jscpd": "0.6.10", "json-loader": "~0.5.7", - "karma": "~2.0.0", + "karma": "^3.0.0", "karma-chai": "~0.1.0", "karma-jasmine": "^1.1.0", "karma-mocha": "~1.3.0", @@ -147,19 +151,20 @@ "mocha": "^5.1.1", "mocha-lcov-reporter": "^1.3.0", "nock": "^9.1.6", - "nyc": "^11.2.0", + "nyc": "^13.1.0", "proxyquire": "^2.0.1", "rimraf": "^2.6.2", - "sinon": "^3.2.1", + "sinon": "^6.3.5", "sinon-chai": "^2.13.0", "source-map-loader": "^0.2.1", "source-map-support": "^0.4.18", "standard": "^11.0.0", - "standard-version": "^4.3.0", + "standard-version": "^4.4.0", "superagent": "^3.8.2", "ts-node": "^3.3.0", "tslint": "^5.8.0", "tslint-config-prettier": "^1.15.0", + "tslint-config-standard": "^8.0.1", "typescript": "^2.7.2", "webpack": "^3.5.5" } diff --git a/src/common/logger.ts b/src/common/logger.ts index cc3d65b8f..a70883582 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -8,11 +8,14 @@ prettyStdOut.pipe(process.stdout); export class Logger extends bunyan { public time(action: string, startTime: number) { const time = Date.now() - startTime; - this.info({ - action, - duration: time, - type: "TIMER", - }, `TIMER: ${action} completed in ${time} milliseconds`); + this.info( + { + action, + duration: time, + type: "TIMER" + }, + `TIMER: ${action} completed in ${time} milliseconds` + ); } public get logLevelName(): string { @@ -22,9 +25,11 @@ export class Logger extends bunyan { export default new Logger({ name: `pact@${pkg.version}`, - streams: [{ - level: (process.env.LOGLEVEL || "info") as bunyan.LogLevel, - stream: prettyStdOut, - type: "raw", - }], + streams: [ + { + level: (process.env.LOGLEVEL || "info") as bunyan.LogLevel, + stream: prettyStdOut, + type: "raw" + } + ] }); diff --git a/src/common/net.ts b/src/common/net.ts index 6cfcc3520..c56b4d3f9 100644 --- a/src/common/net.ts +++ b/src/common/net.ts @@ -8,13 +8,18 @@ import * as net from "net"; const isPortAvailable = (port: number, host: string): Promise => { return new Promise((resolve, reject) => { - const server: any = net.createServer() + const server: any = net + .createServer() .listen({ port, host, exclusive: true }) - .on("error", (e: any) => (e.code === "EADDRINUSE" ? reject(new Error(`Port ${port} is unavailable`)) : reject(e))) + .on( + "error", + (e: any) => + e.code === "EADDRINUSE" + ? reject(new Error(`Port ${port} is unavailable`)) + : reject(e) + ) .on("listening", () => server.once("close", () => resolve()).close()); }); }; -export { - isPortAvailable, -}; +export { isPortAvailable }; diff --git a/src/common/request.spec.ts b/src/common/request.spec.ts index 644e6909d..27de323b3 100644 --- a/src/common/request.spec.ts +++ b/src/common/request.spec.ts @@ -14,47 +14,59 @@ describe("Request", () => { const URL = `http://localhost:${PORT}`; const URLSECURE = `https://localhost:${PORT}`; - beforeEach(() => request = new Request()); + beforeEach(() => (request = new Request())); context("#send", () => { afterEach(() => nock.cleanAll()); describe("Promise", () => { it("Should return a promise", () => { - nock(URL).get("/").reply(200); + nock(URL) + .get("/") + .reply(200); const r = request.send(HTTPMethod.GET, URL); return Promise.all([ expect(r).is.ok, expect(r.then).is.ok, expect(r.then).is.a("function"), - expect(r).to.be.fulfilled, + expect(r).to.be.fulfilled ]); }); it("Should resolve when request succeeds with response body", () => { const BODY = "body"; - nock(URL).get("/").reply(200, BODY); + nock(URL) + .get("/") + .reply(200, BODY); const p = request.send(HTTPMethod.GET, URL); return Promise.all([ expect(p).to.be.fulfilled, - expect(p).to.eventually.be.equal(BODY), + expect(p).to.eventually.be.equal(BODY) ]); }); it("Should reject when request fails with error message", () => { const ERROR = "error"; - nock(URL).get("/").reply(400, ERROR); + nock(URL) + .get("/") + .reply(400, ERROR); const p = request.send(HTTPMethod.GET, URL); return expect(p).to.be.rejectedWith(ERROR); }); }); describe("Headers", () => { it("Should have Pact headers are sent with every request", () => { - nock(URL).matchHeader("X-Pact-Mock-Service", "true").get("/").reply(200); + nock(URL) + .matchHeader("X-Pact-Mock-Service", "true") + .get("/") + .reply(200); return expect(request.send(HTTPMethod.GET, URL)).to.be.fulfilled; }); }); describe("SSL", () => { it("Should ignore self signed certificate errors", () => { - nock(URLSECURE).matchHeader("X-Pact-Mock-Service", "true").get("/").reply(200); + nock(URLSECURE) + .matchHeader("X-Pact-Mock-Service", "true") + .get("/") + .reply(200); return expect(request.send(HTTPMethod.GET, URLSECURE)).to.be.fulfilled; }); }); diff --git a/src/common/request.ts b/src/common/request.ts index d08133fdb..a42e289dc 100644 --- a/src/common/request.ts +++ b/src/common/request.ts @@ -11,36 +11,46 @@ export enum HTTPMethod { PATCH = "PATCH", DELETE = "DELETE", HEAD = "HEAD", - OPTIONS = "OPTIONS", + OPTIONS = "OPTIONS" } -export type methods = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS"; +export type methods = + | "GET" + | "POST" + | "PUT" + | "PATCH" + | "DELETE" + | "HEAD" + | "OPTIONS"; export class Request { private readonly transport = Popsicle.createTransport({ rejectUnauthorized: false, // Need to tell node to ignore bad ssl cert - type: "text", + type: "text" }); - public send(method: HTTPMethod | methods, url: string, body?: string): Promise { + public send( + method: HTTPMethod | methods, + url: string, + body?: string + ): Promise { const opts = { body, headers: { "Content-Type": "application/json", - "X-Pact-Mock-Service": "true", + "X-Pact-Mock-Service": "true" }, method, timeout: 10000, transport: this.transport, - url, + url }; - return Popsicle.request(opts) - .then((res: Response) => { - if (res.status >= 200 && res.status < 400) { - return res.body; - } else { - return Promise.reject(res.body); - } - }); + return Popsicle.request(opts).then((res: Response) => { + if (res.status >= 200 && res.status < 400) { + return res.body; + } else { + return Promise.reject(res.body); + } + }); } } diff --git a/src/common/utils.ts b/src/common/utils.ts index 0b9b13d53..7e1a7eaf4 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -3,7 +3,6 @@ import * as q from "q"; // Convert a q promise to regular Promise export function qToPromise(promise: q.Promise): Promise { return new Promise((resolve, reject) => { - promise - .then((value: T) => resolve(value), (error: any) => reject(error)); + promise.then((value: T) => resolve(value), (error: any) => reject(error)); }); } diff --git a/src/dsl/graphql.spec.ts b/src/dsl/graphql.spec.ts index be7166519..396330148 100644 --- a/src/dsl/graphql.spec.ts +++ b/src/dsl/graphql.spec.ts @@ -48,7 +48,7 @@ describe("GraphQLInteraction", () => { interaction.withOperation("query"); interaction.withQuery("{ hello }"); interaction.withVariables({ - foo: "bar", + foo: "bar" }); const json: any = interaction.json(); @@ -63,13 +63,15 @@ describe("GraphQLInteraction", () => { interaction.withOperation("query"); interaction.withQuery("{ hello }"); interaction.withVariables({ - foo: "bar", + foo: "bar" }); }); describe("when given an invalid query", () => { it("should fail with an error", () => { - expect(() => interaction.withQuery("{ not properly terminated")).to.throw(Error); + expect(() => + interaction.withQuery("{ not properly terminated") + ).to.throw(Error); }); }); @@ -101,7 +103,7 @@ describe("GraphQLInteraction", () => { } }`); interaction.withVariables({ - name: "bar", + name: "bar" }); const json: any = interaction.json(); @@ -110,9 +112,7 @@ describe("GraphQLInteraction", () => { const lotsOfWhitespace = `{ Hello(id: \$id) { name } }`; expect(r.test(lotsOfWhitespace)).to.eq(true); }); - }); }); }); - }); diff --git a/src/dsl/graphql.ts b/src/dsl/graphql.ts index 9cddea3b3..883f90d4f 100644 --- a/src/dsl/graphql.ts +++ b/src/dsl/graphql.ts @@ -3,49 +3,57 @@ * * @module GraphQL */ -import { Interaction, ResponseOptions, RequestOptions, InteractionState, Query } from "../dsl/interaction"; -import { HTTPMethod, methods } from "../common/request"; -import { MatcherResult, regex } from "./matchers"; -import { keys, isNil, extend } from "lodash"; -import gql from "graphql-tag"; +import { Interaction, InteractionState } from '../dsl/interaction' +import { regex } from './matchers' +import { keys, isNil, extend } from 'lodash' +import gql from 'graphql-tag' -export type GraphQLOperation = "query" | "mutation" | null; +export type GraphQLOperation = 'query' | 'mutation' | null enum GraphQLOperations { - query = "query", - mutation = "mutation", + query = 'query', + mutation = 'mutation' } -export interface GraphQLVariables { [name: string]: any; } +export interface GraphQLVariables { + [name: string]: any +} /** * GraphQL interface */ export class GraphQLInteraction extends Interaction { - private operation: GraphQLOperation = null; - private variables: GraphQLVariables = {}; - private query: string; + private operation: GraphQLOperation = null + private variables: GraphQLVariables = {} + private query: string /** * The type of GraphQL operation. Generally not required. */ - public withOperation(operation: GraphQLOperation) { - if (!operation || operation && keys(GraphQLOperations).indexOf(operation.toString()) < 0) { - throw new Error(`You must provide a valid HTTP method: ${keys(GraphQLOperations).join(", ")}.`); + public withOperation (operation: GraphQLOperation) { + if ( + !operation || + (operation && keys(GraphQLOperations).indexOf(operation.toString()) < 0) + ) { + throw new Error( + `You must provide a valid HTTP method: ${keys(GraphQLOperations).join( + ', ' + )}.` + ) } - this.operation = operation; + this.operation = operation - return this; + return this } /** * Any variables used in the Query */ - public withVariables(variables: GraphQLVariables) { - this.variables = variables; + public withVariables (variables: GraphQLVariables) { + this.variables = variables - return this; + return this } /** @@ -66,49 +74,56 @@ export class GraphQLInteraction extends Interaction { * }" * }' */ - public withQuery(query: string) { + public withQuery (query: string) { if (isNil(query)) { - throw new Error("You must provide a GraphQL query."); + throw new Error('You must provide a GraphQL query.') } try { - gql(query); + gql(query) } catch (e) { - throw new Error(`GraphQL Query is invalid: ${e.message}`); + throw new Error(`GraphQL Query is invalid: ${e.message}`) } - this.query = query; + this.query = query - return this; + return this } /** * Returns the interaction object created. */ - public json(): InteractionState { + public json (): InteractionState { if (isNil(this.query)) { - throw new Error("You must provide a GraphQL query."); + throw new Error('You must provide a GraphQL query.') } if (isNil(this.state.description)) { - throw new Error("You must provide a description for the query."); + throw new Error('You must provide a description for the query.') } - this.state.request = extend({ - body: { - operationName: this.operation, - query: regex({ generate: this.query, matcher: escapeGraphQlQuery(this.query) }), - variables: this.variables, + this.state.request = extend( + { + body: { + operationName: this.operation, + query: regex({ + generate: this.query, + matcher: escapeGraphQlQuery(this.query) + }), + variables: this.variables + }, + headers: { 'content-type': 'application/json' }, + method: 'POST' }, - headers: { "content-type": "application/json" }, - method: "POST", - }, this.state.request); + this.state.request + ) - return this.state; + return this.state } } -const escapeGraphQlQuery = (s: string) => escapeSpace(escapeRegexChars(s)); +const escapeGraphQlQuery = (s: string) => escapeSpace(escapeRegexChars(s)) -const escapeRegexChars = (s: string) => s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); +const escapeRegexChars = (s: string) => + s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') -const escapeSpace = (s: string) => s.replace(/\s+/g, "\\s*"); +const escapeSpace = (s: string) => s.replace(/\s+/g, '\\s*') diff --git a/src/dsl/interaction.spec.ts b/src/dsl/interaction.spec.ts index e72010739..67b33805a 100644 --- a/src/dsl/interaction.spec.ts +++ b/src/dsl/interaction.spec.ts @@ -9,13 +9,22 @@ const expect = chai.expect; describe("Interaction", () => { describe("#given", () => { it("creates Interaction with provider state", () => { - const actual = new Interaction().uponReceiving("r").given("provider state").json(); - expect(actual).to.eql({ description: "r", providerState: "provider state" }); + const actual = new Interaction() + .uponReceiving("r") + .given("provider state") + .json(); + expect(actual).to.eql({ + description: "r", + providerState: "provider state" + }); }); describe("without provider state", () => { it("creates Interaction when blank", () => { - const actual = new Interaction().uponReceiving("r").given("").json(); + const actual = new Interaction() + .uponReceiving("r") + .given("") + .json(); expect(actual).to.eql({ description: "r" }); }); it("creates Interaction when nothing is passed", () => { @@ -29,12 +38,17 @@ describe("Interaction", () => { const interaction = new Interaction(); it("throws error when no description provided", () => { - expect(interaction.uponReceiving).to.throw(Error, "You must provide a description for the interaction."); + expect(interaction.uponReceiving).to.throw( + Error, + "You must provide a description for the interaction." + ); }); it("has a state with description", () => { interaction.uponReceiving("an interaction description"); - expect(interaction.json()).to.eql({ description: "an interaction description" }); + expect(interaction.json()).to.eql({ + description: "an interaction description" + }); }); }); @@ -42,22 +56,32 @@ describe("Interaction", () => { const interaction = new Interaction(); it("throws error when method is not provided", () => { - expect(interaction.withRequest.bind(interaction, {})).to.throw(Error, "You must provide an HTTP method."); + expect(interaction.withRequest.bind(interaction, {})).to.throw( + Error, + "You must provide an HTTP method." + ); }); it("throws error when an invalid method is provided", () => { - expect(interaction.withRequest.bind(interaction, { method: "FOO" })) - .to.throw(Error, "You must provide a valid HTTP method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS."); + expect( + interaction.withRequest.bind(interaction, { method: "FOO" }) + ).to.throw( + Error, + "You must provide a valid HTTP method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS." + ); }); it("throws error when method is not provided", () => { - expect(interaction.withRequest.bind(interaction, { ath: "/" })) - .to.throw(Error, "You must provide an HTTP method."); + expect(interaction.withRequest.bind(interaction, { ath: "/" })).to.throw( + Error, + "You must provide an HTTP method." + ); }); it("throws error when path is not provided", () => { - expect(interaction.withRequest.bind(interaction, { method: HTTPMethod.GET })) - .to.throw(Error, "You must provide a path."); + expect( + interaction.withRequest.bind(interaction, { method: HTTPMethod.GET }) + ).to.throw(Error, "You must provide a path."); }); describe("with only mandatory params", () => { @@ -84,12 +108,19 @@ describe("Interaction", () => { headers: { "Content-Type": "application/json" }, method: HTTPMethod.GET, path: "/search", - query: "q=test", - }).json(); + query: "q=test" + }) + .json(); it("has a full state all available keys", () => { expect(actual).to.have.property("request"); - expect(actual.request).to.have.keys("method", "path", "query", "headers", "body"); + expect(actual.request).to.have.keys( + "method", + "path", + "query", + "headers", + "body" + ); }); }); @@ -100,8 +131,9 @@ describe("Interaction", () => { .withRequest({ body: "", method: HTTPMethod.GET, - path: "/path", - }).json(); + path: "/path" + }) + .json(); expect(actual.request).to.have.any.keys("body"); }); @@ -111,8 +143,9 @@ describe("Interaction", () => { .withRequest({ body: undefined, method: HTTPMethod.GET, - path: "/path", - }).json(); + path: "/path" + }) + .json(); expect(actual.request).not.to.have.any.keys("body"); }); }); @@ -122,12 +155,16 @@ describe("Interaction", () => { let interaction = new Interaction(); it("throws error when status is not provided", () => { - expect(interaction.willRespondWith.bind(interaction, {})).to.throw(Error, "You must provide a status code."); + expect(interaction.willRespondWith.bind(interaction, {})).to.throw( + Error, + "You must provide a status code." + ); }); it("throws error when status is blank", () => { - expect(interaction.willRespondWith.bind(interaction, { status: "" })) - .to.throw(Error, "You must provide a status code."); + expect( + interaction.willRespondWith.bind(interaction, { status: "" }) + ).to.throw(Error, "You must provide a status code."); }); describe("with only mandatory params", () => { @@ -152,7 +189,7 @@ describe("Interaction", () => { interaction.willRespondWith({ body: { id: 1, name: "Test", due: "tomorrow" }, headers: { "Content-Type": "application/json" }, - status: 404, + status: 404 }); const actual = interaction.json(); @@ -166,24 +203,20 @@ describe("Interaction", () => { describe("response body", () => { it("is included when an empty string is specified", () => { interaction = new Interaction(); - interaction - .uponReceiving("request") - .willRespondWith({ - body: "", - status: 204, - }); + interaction.uponReceiving("request").willRespondWith({ + body: "", + status: 204 + }); const actual = interaction.json(); expect(actual.response).to.have.any.keys("body"); }); it("is not included when explicitly set to undefined", () => { interaction = new Interaction(); - interaction - .uponReceiving("request") - .willRespondWith({ - body: undefined, - status: 204, - }); + interaction.uponReceiving("request").willRespondWith({ + body: undefined, + status: 204 + }); const actual = interaction.json(); expect(actual.response).not.to.have.any.keys("body"); }); diff --git a/src/dsl/interaction.ts b/src/dsl/interaction.ts index bd3be7aa4..729cb69dc 100644 --- a/src/dsl/interaction.ts +++ b/src/dsl/interaction.ts @@ -83,7 +83,9 @@ export class Interaction { } if (keys(HTTPMethod).indexOf(requestOpts.method.toString()) < 0) { - throw new Error(`You must provide a valid HTTP method: ${keys(HTTPMethod).join(", ")}.`); + throw new Error( + `You must provide a valid HTTP method: ${keys(HTTPMethod).join(", ")}.` + ); } if (isNil(requestOpts.path)) { @@ -103,15 +105,21 @@ export class Interaction { * @param {Object} responseOpts.body */ public willRespondWith(responseOpts: ResponseOptions) { - if (isNil(responseOpts.status) || responseOpts.status.toString().trim().length === 0) { + if ( + isNil(responseOpts.status) || + responseOpts.status.toString().trim().length === 0 + ) { throw new Error("You must provide a status code."); } - this.state.response = omitBy({ - body: responseOpts.body, - headers: responseOpts.headers || undefined, - status: responseOpts.status, - }, isNil) as ResponseOptions; + this.state.response = omitBy( + { + body: responseOpts.body, + headers: responseOpts.headers || undefined, + status: responseOpts.status + }, + isNil + ) as ResponseOptions; return this; } diff --git a/src/dsl/matchers.spec.ts b/src/dsl/matchers.spec.ts index 221ca7890..58d301828 100644 --- a/src/dsl/matchers.spec.ts +++ b/src/dsl/matchers.spec.ts @@ -3,27 +3,43 @@ import * as chai from "chai"; const expect = require("chai").expect; import { Interaction } from "./interaction"; import { - boolean, decimal, eachLike, hexadecimal, - integer, ipv4Address, ipv6Address, ISO8601_DATE_FORMAT, iso8601Date, - iso8601DateTime, iso8601DateTimeWithMillis, iso8601Time, rfc3339Timestamp, somethingLike, term, uuid, + boolean, + decimal, + eachLike, + hexadecimal, + integer, + ipv4Address, + ipv6Address, + ISO8601_DATE_FORMAT, + iso8601Date, + iso8601DateTime, + iso8601DateTimeWithMillis, + iso8601Time, + rfc3339Timestamp, + somethingLike, + term, + uuid, validateExample, extractPayload, - isMatcher, + isMatcher } from "./matchers"; import { json } from "express"; describe("Matcher", () => { - describe("#validateExample", () => { describe("when given a valid regex", () => { describe("and a matching example", () => { it("should return true", () => { - expect(validateExample("2010-01-01", ISO8601_DATE_FORMAT)).to.eql(true); + expect(validateExample("2010-01-01", ISO8601_DATE_FORMAT)).to.eql( + true + ); }); }); describe("and a failing example", () => { it("should return false", () => { - expect(validateExample("not a date", ISO8601_DATE_FORMAT)).to.eql(false); + expect(validateExample("not a date", ISO8601_DATE_FORMAT)).to.eql( + false + ); }); }); }); @@ -45,15 +61,15 @@ describe("Matcher", () => { matcher: { json_class: "Regexp", o: 0, - s: "\\w+", - }, + s: "\\w+" + } }, - json_class: "Pact::Term", + json_class: "Pact::Term" }; const match = term({ generate: "myawesomeword", - matcher: "\\w+", + matcher: "\\w+" }); expect(JSON.stringify(match)).to.deep.include(JSON.stringify(expected)); @@ -88,7 +104,7 @@ describe("Matcher", () => { expect(() => { term({ generate: "abc", - matcher: ISO8601_DATE_FORMAT, + matcher: ISO8601_DATE_FORMAT }); }).to.throw(Error); }); @@ -100,7 +116,7 @@ describe("Matcher", () => { it("should return a serialized Ruby object", () => { const expected = { contents: "myspecialvalue", - json_class: "Pact::SomethingLike", + json_class: "Pact::SomethingLike" }; const match = somethingLike("myspecialvalue"); @@ -124,7 +140,7 @@ describe("Matcher", () => { describe("when an invalid value is provided", () => { it("should throw an Error", () => { expect(createTheValue(undefined)).to.throw(Error); - expect(createTheValue(() => { })).to.throw(Error); + expect(createTheValue(() => {})).to.throw(Error); }); }); }); @@ -136,7 +152,7 @@ describe("Matcher", () => { const expected = { contents: null, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike(null, { min: 1 }); @@ -149,7 +165,7 @@ describe("Matcher", () => { const expected = { contents: { a: 1 }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike({ a: 1 }, { min: 1 }); @@ -170,7 +186,7 @@ describe("Matcher", () => { const expected = { contents: [1, 2, 3], json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike([1, 2, 3], { min: 1 }); @@ -183,7 +199,7 @@ describe("Matcher", () => { const expected = { contents: "test", json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike("test", { min: 1 }); @@ -198,15 +214,17 @@ describe("Matcher", () => { contents: { id: { contents: 10, - json_class: "Pact::SomethingLike", - }, + json_class: "Pact::SomethingLike" + } }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike({ id: somethingLike(10) }, { min: 1 }); - expect(JSON.stringify(match)).to.deep.include(JSON.stringify(expected)); + expect(JSON.stringify(match)).to.deep.include( + JSON.stringify(expected) + ); }); }); @@ -220,24 +238,29 @@ describe("Matcher", () => { matcher: { json_class: "Regexp", o: 0, - s: "red|green", - }, + s: "red|green" + } }, - json_class: "Pact::Term", - }, + json_class: "Pact::Term" + } }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; - const match = eachLike({ - colour: term({ - generate: "red", - matcher: "red|green", - }), - }, { min: 1 }); + const match = eachLike( + { + colour: term({ + generate: "red", + matcher: "red|green" + }) + }, + { min: 1 } + ); - expect(JSON.stringify(match)).to.deep.include(JSON.stringify(expected)); + expect(JSON.stringify(match)).to.deep.include( + JSON.stringify(expected) + ); }); }); @@ -247,14 +270,16 @@ describe("Matcher", () => { contents: { contents: "blue", json_class: "Pact::ArrayLike", - min: 1, + min: 1 }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike(eachLike("blue", { min: 1 }), { min: 1 }); - expect(JSON.stringify(match)).to.deep.include(JSON.stringify(expected)); + expect(JSON.stringify(match)).to.deep.include( + JSON.stringify(expected) + ); }); }); @@ -269,49 +294,55 @@ describe("Matcher", () => { matcher: { json_class: "Regexp", o: 0, - s: "red|green|blue", - }, + s: "red|green|blue" + } }, - json_class: "Pact::Term", + json_class: "Pact::Term" }, size: { contents: 10, - json_class: "Pact::SomethingLike", + json_class: "Pact::SomethingLike" }, tag: { contents: [ { contents: "jumper", - json_class: "Pact::SomethingLike", + json_class: "Pact::SomethingLike" }, { contents: "shirt", - json_class: "Pact::SomethingLike", - }, + json_class: "Pact::SomethingLike" + } ], json_class: "Pact::ArrayLike", - min: 2, - }, + min: 2 + } }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike( - eachLike({ - colour: term({ generate: "red", matcher: "red|green|blue" }), - size: somethingLike(10), - tag: eachLike([ - somethingLike("jumper"), - somethingLike("shirt"), - ], { min: 2 }), - }, { min: 1 }), - { min: 1 }); - - expect(JSON.stringify(match)).to.deep.include(JSON.stringify(expected)); + eachLike( + { + colour: term({ generate: "red", matcher: "red|green|blue" }), + size: somethingLike(10), + tag: eachLike( + [somethingLike("jumper"), somethingLike("shirt")], + { min: 2 } + ) + }, + { min: 1 } + ), + { min: 1 } + ); + + expect(JSON.stringify(match)).to.deep.include( + JSON.stringify(expected) + ); }); }); }); @@ -321,7 +352,7 @@ describe("Matcher", () => { const expected = { contents: { a: 1 }, json_class: "Pact::ArrayLike", - min: 1, + min: 1 }; const match = eachLike({ a: 1 }); @@ -334,7 +365,7 @@ describe("Matcher", () => { const expected = { contents: { a: 1 }, json_class: "Pact::ArrayLike", - min: 3, + min: 3 }; const match = eachLike({ a: 1 }, { min: 3 }); @@ -379,7 +410,9 @@ describe("Matcher", () => { describe("when given a valid ipv6Address", () => { it("should not fail", () => { expect(ipv6Address("::1")).to.be.an("object"); - expect(ipv6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).to.be.an("object"); + expect(ipv6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).to.be.an( + "object" + ); expect(ipv6Address()).to.be.an("object"); }); }); @@ -438,7 +471,9 @@ describe("Matcher", () => { describe("#rfc3339Timestamp", () => { describe("when given a valid rfc3339Timestamp", () => { it("should not fail", () => { - expect(rfc3339Timestamp("Mon, 31 Oct 2016 15:21:41 -0400")).to.be.an("object"); + expect(rfc3339Timestamp("Mon, 31 Oct 2016 15:21:41 -0400")).to.be.an( + "object" + ); expect(rfc3339Timestamp()).to.be.an("object"); }); }); @@ -486,7 +521,9 @@ describe("Matcher", () => { describe("#iso8601DateTime", () => { describe("when given a valid iso8601DateTime", () => { it("should not fail", () => { - expect(iso8601DateTime("2015-08-06T16:53:10+01:00")).to.be.an("object"); + expect(iso8601DateTime("2015-08-06T16:53:10+01:00")).to.be.an( + "object" + ); expect(iso8601DateTime()).to.be.an("object"); }); }); @@ -502,7 +539,9 @@ describe("Matcher", () => { describe("#iso8601DateTimeWithMillis", () => { describe("when given a valid iso8601DateTimeWithMillis", () => { it("should not fail", () => { - expect(iso8601DateTimeWithMillis("2015-08-06T16:53:10.123+01:00")).to.be.an("object"); + expect( + iso8601DateTimeWithMillis("2015-08-06T16:53:10.123+01:00") + ).to.be.an("object"); expect(iso8601DateTimeWithMillis()).to.be.an("object"); }); }); @@ -522,67 +561,68 @@ describe("Matcher", () => { const matcher = term({ generate: "myawesomeword", - matcher: "\\w+", + matcher: "\\w+" }); expect(isMatcher(matcher)).to.eq(true); expect(extractPayload(matcher)).to.eql(expected); - }); }); describe("when given a complex nested object with matchers", () => { it("should remove all matching guff", () => { const o = somethingLike({ stringMatcher: { - awesomeSetting: somethingLike("a string"), + awesomeSetting: somethingLike("a string") }, anotherStringMatcher: { nestedSetting: { - anotherStringMatcherSubSetting: somethingLike(true), + anotherStringMatcherSubSetting: somethingLike(true) }, - anotherSetting: term({ generate: "this", matcher: "this|that" }), + anotherSetting: term({ generate: "this", matcher: "this|that" }) }, arrayMatcher: { - lotsOfValues: eachLike("useful", { min: 3 }), + lotsOfValues: eachLike("useful", { min: 3 }) }, arrayOfMatchers: { lotsOfValues: eachLike( { foo: "bar", - baz: somethingLike("bat"), - }, { min: 3 }), - }, + baz: somethingLike("bat") + }, + { min: 3 } + ) + } }); const expected = { stringMatcher: { - awesomeSetting: "a string", + awesomeSetting: "a string" }, anotherStringMatcher: { nestedSetting: { - anotherStringMatcherSubSetting: true, + anotherStringMatcherSubSetting: true }, - anotherSetting: "this", + anotherSetting: "this" }, arrayMatcher: { - lotsOfValues: ["useful", "useful", "useful"], + lotsOfValues: ["useful", "useful", "useful"] }, arrayOfMatchers: { lotsOfValues: [ { baz: "bat", - foo: "bar", + foo: "bar" }, { baz: "bat", - foo: "bar", + foo: "bar" }, { baz: "bat", - foo: "bar", - }, - ], - }, + foo: "bar" + } + ] + } }; expect(extractPayload(o)).to.deep.eql(expected); diff --git a/src/dsl/matchers.ts b/src/dsl/matchers.ts index 5634a9280..0e75e4b8a 100644 --- a/src/dsl/matchers.ts +++ b/src/dsl/matchers.ts @@ -9,14 +9,20 @@ import { isFunction, isNil, isEmpty, isUndefined } from "lodash"; // Note: The following regexes are Ruby formatted, // so attempting to parse as JS without modification is probably not going to work as intended! /* tslint:disable:max-line-length */ -export const ISO8601_DATE_FORMAT = "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))?)$"; -export const ISO8601_DATETIME_FORMAT = "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z)$"; -export const ISO8601_DATETIME_WITH_MILLIS_FORMAT = "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d{3}([+-][0-2]\\d:[0-5]\\d|Z)$"; -export const ISO8601_TIME_FORMAT = "^(T\\d\\d:\\d\\d(:\\d\\d)?(\\.\\d+)?(([+-]\\d\\d:\\d\\d)|Z)?)?$"; -export const RFC3339_TIMESTAMP_FORMAT = "^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s\\d{2}\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s\\d{4}\\s\\d{2}:\\d{2}:\\d{2}\\s(\\+|-)\\d{4}$"; +export const ISO8601_DATE_FORMAT = + "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))?)$"; +export const ISO8601_DATETIME_FORMAT = + "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z)$"; +export const ISO8601_DATETIME_WITH_MILLIS_FORMAT = + "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d{3}([+-][0-2]\\d:[0-5]\\d|Z)$"; +export const ISO8601_TIME_FORMAT = + "^(T\\d\\d:\\d\\d(:\\d\\d)?(\\.\\d+)?(([+-]\\d\\d:\\d\\d)|Z)?)?$"; +export const RFC3339_TIMESTAMP_FORMAT = + "^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s\\d{2}\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s\\d{4}\\s\\d{2}:\\d{2}:\\d{2}\\s(\\+|-)\\d{4}$"; export const UUID_V4_FORMAT = "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"; export const IPV4_FORMAT = "^(\\d{1,3}\\.)+\\d{1,3}$"; -export const IPV6_FORMAT = "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"; +export const IPV6_FORMAT = + "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"; export const HEX_FORMAT = "^[0-9a-fA-F]+$"; /* tslint:enable */ @@ -37,7 +43,7 @@ export function validateExample(example: string, matcher: string): boolean { * @param {string} opts.generate - a value to represent the matched String * @param {string} opts.matcher - a Regex representing the value */ -export function term(opts: { generate: string, matcher: string }) { +export function term(opts: { generate: string; matcher: string }) { const generate = opts.generate; const matcher = opts.matcher; @@ -47,7 +53,9 @@ export function term(opts: { generate: string, matcher: string }) { } if (!validateExample(generate, matcher)) { - throw new Error(`Example '${generate}' does not match provided regular expression '${matcher}'`); + throw new Error( + `Example '${generate}' does not match provided regular expression '${matcher}'` + ); } return { @@ -56,13 +64,13 @@ export function term(opts: { generate: string, matcher: string }) { matcher: { json_class: "Regexp", o: 0, - s: matcher, - }, + s: matcher + } }, getValue: () => { return generate; }, - json_class: "Pact::Term", + json_class: "Pact::Term" }; } @@ -73,7 +81,7 @@ export function term(opts: { generate: string, matcher: string }) { export function uuid(id?: string) { return term({ generate: id || "ce118b6e-d8e1-11e7-9296-cec278b6b50a", - matcher: UUID_V4_FORMAT, + matcher: UUID_V4_FORMAT }); } @@ -84,7 +92,7 @@ export function uuid(id?: string) { export function ipv4Address(ip?: string) { return term({ generate: ip || "127.0.0.13", - matcher: IPV4_FORMAT, + matcher: IPV4_FORMAT }); } @@ -95,7 +103,7 @@ export function ipv4Address(ip?: string) { export function ipv6Address(ip?: string) { return term({ generate: ip || "::ffff:192.0.2.128", - matcher: IPV6_FORMAT, + matcher: IPV6_FORMAT }); } @@ -107,7 +115,7 @@ export function ipv6Address(ip?: string) { export function iso8601DateTime(date?: string) { return term({ generate: date || "2015-08-06T16:53:10+01:00", - matcher: ISO8601_DATETIME_FORMAT, + matcher: ISO8601_DATETIME_FORMAT }); } @@ -118,7 +126,7 @@ export function iso8601DateTime(date?: string) { export function iso8601DateTimeWithMillis(date?: string) { return term({ generate: date || "2015-08-06T16:53:10.123+01:00", - matcher: ISO8601_DATETIME_WITH_MILLIS_FORMAT, + matcher: ISO8601_DATETIME_WITH_MILLIS_FORMAT }); } @@ -129,7 +137,7 @@ export function iso8601DateTimeWithMillis(date?: string) { export function iso8601Date(date?: string) { return term({ generate: date || "2013-02-01", - matcher: ISO8601_DATE_FORMAT, + matcher: ISO8601_DATE_FORMAT }); } @@ -140,7 +148,7 @@ export function iso8601Date(date?: string) { export function iso8601Time(time?: string) { return term({ generate: time || "T22:44:30.652Z", - matcher: ISO8601_TIME_FORMAT, + matcher: ISO8601_TIME_FORMAT }); } @@ -151,7 +159,7 @@ export function iso8601Time(time?: string) { export function rfc3339Timestamp(timestamp?: string) { return term({ generate: timestamp || "Mon, 31 Oct 2016 15:21:41 -0400", - matcher: RFC3339_TIMESTAMP_FORMAT, + matcher: RFC3339_TIMESTAMP_FORMAT }); } @@ -162,7 +170,7 @@ export function rfc3339Timestamp(timestamp?: string) { export function hexadecimal(hex?: string) { return term({ generate: hex || "3F", - matcher: HEX_FORMAT, + matcher: HEX_FORMAT }); } @@ -197,11 +205,15 @@ export function boolean() { */ export function eachLike(content: T, opts?: { min: number }) { if (isUndefined(content)) { - throw new Error("Error creating a Pact eachLike. Please provide a content argument"); + throw new Error( + "Error creating a Pact eachLike. Please provide a content argument" + ); } if (opts && (isNil(opts.min) || opts.min < 1)) { - throw new Error("Error creating a Pact eachLike. Please provide opts.min that is > 0"); + throw new Error( + "Error creating a Pact eachLike. Please provide opts.min that is > 0" + ); } return { @@ -215,7 +227,7 @@ export function eachLike(content: T, opts?: { min: number }) { return data; }, json_class: "Pact::ArrayLike", - min: isUndefined(opts) ? 1 : opts.min, + min: isUndefined(opts) ? 1 : opts.min }; } @@ -225,7 +237,9 @@ export function eachLike(content: T, opts?: { min: number }) { */ export function somethingLike(value: T) { if (isNil(value) || isFunction(value)) { - throw new Error("Error creating a Pact somethingLike Match. Value cannot be a function or undefined"); + throw new Error( + "Error creating a Pact somethingLike Match. Value cannot be a function or undefined" + ); } return { @@ -233,7 +247,7 @@ export function somethingLike(value: T) { getValue: () => { return value; }, - json_class: "Pact::SomethingLike", + json_class: "Pact::SomethingLike" }; } @@ -262,9 +276,9 @@ export function extractPayload(obj: any, stack: any = {}): any { const value = obj[property]; if (isMatcher(value)) { - extractPayload(value.getValue(), stack[property] = value.getValue()); + extractPayload(value.getValue(), (stack[property] = value.getValue())); } else if (typeof value === "object") { - extractPayload(value, stack[property] = value); + extractPayload(value, (stack[property] = value)); } } } diff --git a/src/dsl/message.ts b/src/dsl/message.ts index 344406058..f8a8097c2 100644 --- a/src/dsl/message.ts +++ b/src/dsl/message.ts @@ -6,7 +6,9 @@ import { MatcherResult } from "./matchers"; * * @module Message */ -export interface Metadata { [name: string]: string | MatcherResult; } +export interface Metadata { + [name: string]: string | MatcherResult; +} /** * A Message is an asynchronous Interaction, sent via a Provider @@ -24,5 +26,9 @@ export interface Message { // Message producer/handlers export type MessageConsumer = (m: Message) => Promise; export type MessageProvider = (m: Message) => Promise; -export interface MessageProviders { [name: string]: MessageProvider; } -export interface StateHandlers { [name: string]: (state: string) => Promise; } +export interface MessageProviders { + [name: string]: MessageProvider; +} +export interface StateHandlers { + [name: string]: (state: string) => Promise; +} diff --git a/src/dsl/mockService.spec.ts b/src/dsl/mockService.spec.ts index ea4236ef3..d26398700 100644 --- a/src/dsl/mockService.spec.ts +++ b/src/dsl/mockService.spec.ts @@ -10,7 +10,6 @@ chai.use(chaiAsPromised); const expect = chai.expect; describe("MockService", () => { - after(() => { nock.restore(); }); @@ -22,7 +21,13 @@ describe("MockService", () => { }); it("creates a MockService when all mandatory parameters are provided", () => { - const mock = new MockService("consumer", "provider", 4443, "127.0.0.2", true); + const mock = new MockService( + "consumer", + "provider", + 4443, + "127.0.0.2", + true + ); expect(mock.baseUrl).to.eql("https://127.0.0.2:4443"); }); @@ -35,15 +40,13 @@ describe("MockService", () => { it("does not create a MockService when consumer is not provided", () => { expect(() => { new MockService("", "provider"); - }) - .not.to.throw(Error); + }).not.to.throw(Error); }); it("does not create a MockService when provider is not provided", () => { expect(() => { new MockService("consumer", ""); - }) - .not.to.throw(Error); + }).not.to.throw(Error); }); }); @@ -56,13 +59,17 @@ describe("MockService", () => { .withRequest({ method: HTTPMethod.GET, path: "/search" }) .willRespondWith({ status: 200 }); - it("when Interaction added successfully", (done) => { - nock(mock.baseUrl).post(/interactions$/).reply(200); + it("when Interaction added successfully", done => { + nock(mock.baseUrl) + .post(/interactions$/) + .reply(200); expect(mock.addInteraction(interaction)).to.eventually.notify(done); }); - it("when Interaction fails to be added", (done) => { - nock(mock.baseUrl).post(/interactions$/).reply(500); + it("when Interaction fails to be added", done => { + nock(mock.baseUrl) + .post(/interactions$/) + .reply(500); expect(mock.addInteraction(interaction)).to.eventually.be.rejected; done(); }); @@ -71,13 +78,17 @@ describe("MockService", () => { describe("#removeInteractions", () => { const mock = new MockService("consumer", "provider", 1234); - it("when interactions are removed successfully", (done) => { - nock(mock.baseUrl).delete(/interactions$/).reply(200); + it("when interactions are removed successfully", done => { + nock(mock.baseUrl) + .delete(/interactions$/) + .reply(200); expect(mock.removeInteractions()).to.eventually.be.fulfilled.notify(done); }); - it("when interactions fail to be removed", (done) => { - nock(mock.baseUrl).delete(/interactions$/).reply(500); + it("when interactions fail to be removed", done => { + nock(mock.baseUrl) + .delete(/interactions$/) + .reply(500); expect(mock.removeInteractions()).to.eventually.be.rejected.notify(done); }); }); @@ -85,13 +96,17 @@ describe("MockService", () => { describe("#verify", () => { const mock = new MockService("consumer", "provider", 1234); - it("when verification is successful", (done) => { - nock(mock.baseUrl).get(/interactions\/verification$/).reply(200); + it("when verification is successful", done => { + nock(mock.baseUrl) + .get(/interactions\/verification$/) + .reply(200); expect(mock.verify()).to.eventually.be.fulfilled.notify(done); }); - it("when verification fails", (done) => { - nock(mock.baseUrl).get(/interactions\/verification$/).reply(500); + it("when verification fails", done => { + nock(mock.baseUrl) + .get(/interactions\/verification$/) + .reply(500); expect(mock.verify()).to.eventually.be.rejected.notify(done); }); }); @@ -101,12 +116,12 @@ describe("MockService", () => { const mock = new MockService("aconsumer", "aprovider", 1234); describe("and writing is successful", () => { - it("should write the consumer and provider details into the pact", (done) => { + it("should write the consumer and provider details into the pact", done => { nock(mock.baseUrl) .post(/pact$/, { consumer: { name: "aconsumer" }, pactfile_write_mode: "overwrite", - provider: { name: "aprovider" }, + provider: { name: "aprovider" } }) .reply(200); expect(mock.writePact()).to.eventually.be.fulfilled.notify(done); @@ -114,9 +129,10 @@ describe("MockService", () => { }); describe("and writing fails", () => { - it("should return a rejected promise", (done) => { + it("should return a rejected promise", done => { nock(mock.baseUrl) - .post(/pact$/, {}).reply(500); + .post(/pact$/, {}) + .reply(500); expect(mock.writePact()).to.eventually.be.rejected.notify(done); }); }); @@ -124,9 +140,10 @@ describe("MockService", () => { describe("when consumer and provider details are not provided", () => { const mock = new MockService(undefined, undefined, 1234); - it("should not write the consumer and provider details into the pact", (done) => { + it("should not write the consumer and provider details into the pact", done => { nock(mock.baseUrl) - .post(/pact$/).reply(200); + .post(/pact$/) + .reply(200); expect(mock.writePact()).to.eventually.be.fulfilled.notify(done); }); }); diff --git a/src/dsl/mockService.ts b/src/dsl/mockService.ts index e515ef3d7..d6504f3b7 100644 --- a/src/dsl/mockService.ts +++ b/src/dsl/mockService.ts @@ -4,7 +4,6 @@ * https://gist.github.com/bethesque/9d81f21d6f77650811f4. * @module MockService */ -import { isEmpty } from "lodash"; import { HTTPMethod, Request } from "../common/request"; import { Interaction } from "./interaction"; @@ -48,19 +47,18 @@ export class MockService { // Deprecated as at https://github.com/pact-foundation/pact-js/issues/105 private consumer?: string, private provider?: string, - // Valid private port = 1234, private host = "127.0.0.1", private ssl = false, - private pactfileWriteMode: PactfileWriteMode = "overwrite") { - + private pactfileWriteMode: PactfileWriteMode = "overwrite" + ) { this.request = new Request(); - this.baseUrl = `${ssl ? "https" : "http"}://${host}:${port}`; + this.baseUrl = `${this.ssl ? "https" : "http"}://${this.host}:${this.port}`; this.pactDetails = { - consumer: (consumer) ? { name: consumer } : undefined, - pactfile_write_mode: pactfileWriteMode, - provider: (provider) ? { name: provider } : undefined, + consumer: this.consumer ? { name: this.consumer } : undefined, + pactfile_write_mode: this.pactfileWriteMode, + provider: this.provider ? { name: this.provider } : undefined }; } @@ -70,7 +68,11 @@ export class MockService { * @returns {Promise} */ public addInteraction(interaction: Interaction): Promise { - return this.request.send(HTTPMethod.POST, `${this.baseUrl}/interactions`, JSON.stringify(interaction.json())); + return this.request.send( + HTTPMethod.POST, + `${this.baseUrl}/interactions`, + JSON.stringify(interaction.json()) + ); } /** @@ -86,7 +88,10 @@ export class MockService { * @returns {Promise} */ public verify(): Promise { - return this.request.send(HTTPMethod.GET, `${this.baseUrl}/interactions/verification`); + return this.request.send( + HTTPMethod.GET, + `${this.baseUrl}/interactions/verification` + ); } /** @@ -94,7 +99,10 @@ export class MockService { * @returns {Promise} */ public writePact(): Promise { - return this.request.send(HTTPMethod.POST, `${this.baseUrl}/pact`, JSON.stringify(this.pactDetails)); + return this.request.send( + HTTPMethod.POST, + `${this.baseUrl}/pact`, + JSON.stringify(this.pactDetails) + ); } - } diff --git a/src/messageConsumerPact.spec.ts b/src/messageConsumerPact.spec.ts index bba9f2a46..037cd5ab2 100644 --- a/src/messageConsumerPact.spec.ts +++ b/src/messageConsumerPact.spec.ts @@ -1,10 +1,12 @@ /* tslint:disable:no-unused-expression no-empty */ import * as chai from "chai"; import * as chaiAsPromised from "chai-as-promised"; -import { MessageConsumerPact, synchronousBodyHandler, asynchronousBodyHandler } from "./messageConsumerPact"; -import { fail } from "assert"; +import { + MessageConsumerPact, + synchronousBodyHandler, + asynchronousBodyHandler +} from "./messageConsumerPact"; import { Message } from "./dsl/message"; -import * as sinon from "sinon"; import * as sinonChai from "sinon-chai"; chai.use(sinonChai); @@ -18,14 +20,14 @@ describe("MessageConsumer", () => { beforeEach(() => { consumer = new MessageConsumerPact({ consumer: "myconsumer", - provider: "myprovider", + provider: "myprovider" }); }); const testMessage: Message = { contents: { - foo: "bar", - }, + foo: "bar" + } }; describe("#constructor", () => { @@ -57,7 +59,7 @@ describe("MessageConsumer", () => { expect(consumer.json().providerStates).to.be.a("array"); expect(consumer.json().providerStates).to.deep.eq([ - { name: "some state" }, + { name: "some state" } ]); }); }); @@ -99,13 +101,13 @@ describe("MessageConsumer", () => { it("should successfully verify the consumer message", () => { const stubbedConsumer = new MessageConsumerPact({ consumer: "myconsumer", - provider: "myprovider", + provider: "myprovider" }); // Stub out service factory (stubbedConsumer as any).getServiceFactory = () => { return { - createMessage: (opts: any) => Promise.resolve("message created"), + createMessage: (opts: any) => Promise.resolve("message created") }; }; @@ -116,7 +118,7 @@ describe("MessageConsumer", () => { .withMetadata({ baz: "bat" }); return expect( - stubbedConsumer.verify((m: Message) => Promise.resolve("yay!")), + stubbedConsumer.verify((m: Message) => Promise.resolve("yay!")) ).to.eventually.be.fulfilled; }); }); diff --git a/src/messageConsumerPact.ts b/src/messageConsumerPact.ts index e9cbaf2d8..521ff76da 100644 --- a/src/messageConsumerPact.ts +++ b/src/messageConsumerPact.ts @@ -3,13 +3,13 @@ */ import { isEmpty, cloneDeep } from "lodash"; -import { MatcherResult, extractPayload } from "./dsl/matchers"; +import { extractPayload } from "./dsl/matchers"; import { qToPromise } from "./common/utils"; import { Metadata, Message, MessageProvider, - MessageConsumer, + MessageConsumer } from "./dsl/message"; import logger from "./common/logger"; import serviceFactory from "@pact-foundation/pact-node"; @@ -46,8 +46,8 @@ export class MessageConsumerPact { // basic interoperability this.state.providerStates = [ { - name: providerState, - }, + name: providerState + } ]; } @@ -80,7 +80,7 @@ export class MessageConsumerPact { public withContent(content: any) { if (isEmpty(content)) { throw new Error( - "You must provide a valid JSON document or primitive for the Message.", + "You must provide a valid JSON document or primitive for the Message." ); } this.state.contents = content; @@ -97,7 +97,7 @@ export class MessageConsumerPact { public withMetadata(metadata: Metadata) { if (isEmpty(metadata)) { throw new Error( - "You must provide valid metadata for the Message, or none at all", + "You must provide valid metadata for the Message, or none at all" ); } this.state.metadata = metadata; @@ -133,10 +133,10 @@ export class MessageConsumerPact { dir: this.config.dir, pactFileWriteMode: this.config.pactfileWriteMode, provider: this.config.provider, - spec: 3, - }), - ), - ); + spec: 3 + }) + ) + ); } /** @@ -164,7 +164,9 @@ const isMessage = (x: Message | any): x is Message => { // bodyHandler takes a synchronous function and returns // a wrapped function that accepts a Message and returns a Promise -export function synchronousBodyHandler(handler: (body: any) => any): MessageProvider { +export function synchronousBodyHandler( + handler: (body: any) => any +): MessageProvider { return (m: Message): Promise => { const body = m.contents; @@ -182,6 +184,8 @@ export function synchronousBodyHandler(handler: (body: any) => any): MessageProv // bodyHandler takes an asynchronous (promisified) function and returns // a wrapped function that accepts a Message and returns a Promise // TODO: move this into its own package and re-export? -export function asynchronousBodyHandler(handler: (body: any) => Promise): MessageProvider { +export function asynchronousBodyHandler( + handler: (body: any) => Promise +): MessageProvider { return (m: Message) => handler(m.contents); } diff --git a/src/messageProviderPact.spec.ts b/src/messageProviderPact.spec.ts index af16745f9..2971ac1f8 100644 --- a/src/messageProviderPact.spec.ts +++ b/src/messageProviderPact.spec.ts @@ -2,7 +2,6 @@ import * as chai from "chai"; import * as chaiAsPromised from "chai-as-promised"; import { MessageProviderPact } from "./messageProviderPact"; -import { fail } from "assert"; import { Message } from "./dsl/message"; import * as sinon from "sinon"; import * as sinonChai from "sinon-chai"; @@ -23,18 +22,18 @@ describe("MesageProvider", () => { const successfulMessage: Message = { contents: { foo: "bar" }, description: successfulRequest, - providerStates: [{ name: "some state" }], + providerStates: [{ name: "some state" }] }; const unsuccessfulMessage: Message = { contents: { foo: "bar" }, description: unsuccessfulRequest, - providerStates: [{ name: "some state not found" }], + providerStates: [{ name: "some state not found" }] }; const nonExistentMessage: Message = { contents: { foo: "bar" }, description: "does not exist", - providerStates: [{ name: "some state not found" }], + providerStates: [{ name: "some state not found" }] }; beforeEach(() => { @@ -43,12 +42,12 @@ describe("MesageProvider", () => { logLevel: "error", messageProviders: { successfulRequest: () => Promise.resolve("yay"), - unsuccessfulRequest: () => Promise.reject("nay"), + unsuccessfulRequest: () => Promise.reject("nay") }, provider: "myprovider", stateHandlers: { - "some state": () => Promise.resolve("yay"), - }, + "some state": () => Promise.resolve("yay") + } }); }); @@ -61,7 +60,7 @@ describe("MesageProvider", () => { provider = new MessageProviderPact({ consumer: "myconsumer", messageProviders: {}, - provider: "myprovider", + provider: "myprovider" }); expect(provider).to.be.a("object"); expect(provider).to.respondTo("verify"); @@ -92,23 +91,23 @@ describe("MesageProvider", () => { describe("#setupVerificationHandler", () => { describe("when their is a valid setup", () => { - it("should create a valid express handler", (done) => { + it("should create a valid express handler", done => { const setupVerificationHandler = (provider as any).setupVerificationHandler.bind( - provider, + provider )(); const req = { body: successfulMessage }; const mock = sinon.stub(); const res = { - json: () => done(), // Expect a response + json: () => done() // Expect a response }; setupVerificationHandler(req, res); }); }); describe("when their is an invalid setup", () => { - it("should create a valid express handler that rejects the message", (done) => { + it("should create a valid express handler that rejects the message", done => { const setupVerificationHandler = (provider as any).setupVerificationHandler.bind( - provider, + provider )(); const req = { body: nonExistentMessage }; const mock = sinon.stub(); @@ -117,9 +116,9 @@ describe("MesageProvider", () => { expect(status).to.eq(500); return { - send: () => done(), // Expect the status to be called with 500 + send: () => done() // Expect the status to be called with 500 }; - }, + } }; setupVerificationHandler(req, res); @@ -131,7 +130,9 @@ describe("MesageProvider", () => { describe("when given a handler that exists", () => { it("should return a Handler object", () => { const findHandler = (provider as any).findHandler.bind(provider); - return expect(findHandler(successfulMessage)).to.eventually.be.a("function"); + return expect(findHandler(successfulMessage)).to.eventually.be.a( + "function" + ); }); }); describe("when given a handler that does not exist", () => { @@ -147,7 +148,7 @@ describe("MesageProvider", () => { it("should return values of all resolved handlers", () => { const findStateHandler = (provider as any).setupStates.bind(provider); return expect( - findStateHandler(successfulMessage), + findStateHandler(successfulMessage) ).to.eventually.deep.equal(["yay"]); }); }); @@ -156,11 +157,11 @@ describe("MesageProvider", () => { provider = new MessageProviderPact({ consumer: "myconsumer", messageProviders: {}, - provider: "myprovider", + provider: "myprovider" }); const findStateHandler = (provider as any).setupStates.bind(provider); return expect( - findStateHandler(unsuccessfulMessage), + findStateHandler(unsuccessfulMessage) ).to.eventually.deep.equal([]); }); }); @@ -170,7 +171,7 @@ describe("MesageProvider", () => { describe("when the http server starts up", () => { it("should return a resolved promise", () => { const waitForServerReady = (provider as any).waitForServerReady; - const server = http.createServer(() => { }).listen(); + const server = http.createServer(() => {}).listen(); return expect(waitForServerReady(server)).to.eventually.be.fulfilled; }); @@ -189,7 +190,7 @@ describe("MesageProvider", () => { describe("#setupProxyApplication", () => { it("should return a valid express app", () => { const setupProxyApplication = (provider as any).setupProxyApplication.bind( - provider, + provider ); expect(setupProxyApplication().listen).to.be.a("function"); }); diff --git a/src/messageProviderPact.ts b/src/messageProviderPact.ts index ab4539c09..b4fa90d16 100644 --- a/src/messageProviderPact.ts +++ b/src/messageProviderPact.ts @@ -22,8 +22,6 @@ const bodyParser = require("body-parser"); * of the interaction to respond - just in this case, not immediately. */ export class MessageProviderPact { - private state: any = {}; - constructor(private config: MessageProviderOptions) { if (config.logLevel && !isEmpty(config.logLevel)) { serviceFactory.logLevel(config.logLevel); @@ -46,7 +44,7 @@ export class MessageProviderPact { // Run the verification once the proxy server is available return this.waitForServerReady(server) .then(this.runProviderVerification()) - .then((result) => { + .then(result => { server.close(); return result; }); @@ -66,7 +64,7 @@ export class MessageProviderPact { return (server: http.Server) => { const opts = { ...omit(this.config, "handlers"), - ...{ providerBaseUrl: "http://localhost:" + server.address().port }, + ...{ providerBaseUrl: "http://localhost:" + server.address().port } } as VerifierOptions; // Run verification @@ -77,7 +75,7 @@ export class MessageProviderPact { // Get the API handler for the verification CLI process to invoke on POST /* private setupVerificationHandler(): ( req: express.Request, - res: express.Response, + res: express.Response ) => void { return (req, res) => { // Extract the message request from the API @@ -87,15 +85,15 @@ export class MessageProviderPact { // wrapped in a Message this.setupStates(message) .then(() => this.findHandler(message)) - .then((handler) => handler(message)) - .then((o) => res.json({ contents: o })) - .catch((e) => res.status(500).send(e)); + .then(handler => handler(message)) + .then(o => res.json({ contents: o })) + .catch(e => res.status(500).send(e)); }; } // Get the Proxy we'll pass to the CLI for verification private setupProxyServer( - app: (request: http.IncomingMessage, response: http.ServerResponse) => void, + app: (request: http.IncomingMessage, response: http.ServerResponse) => void ): http.Server { return http.createServer(app).listen(); } @@ -105,9 +103,7 @@ export class MessageProviderPact { const app = express(); app.use(bodyParser.json()); - app.use( - bodyParser.urlencoded({ extended: true }), - ); + app.use(bodyParser.urlencoded({ extended: true })); app.use((req, res, next) => { res.header("Content-Type", "application/json; charset=utf-8"); next(); @@ -124,7 +120,7 @@ export class MessageProviderPact { const promises: Array> = new Array(); if (message.providerStates) { - message.providerStates.forEach((state) => { + message.providerStates.forEach(state => { const handler = this.config.stateHandlers ? this.config.stateHandlers[state.name] : null; @@ -148,7 +144,7 @@ export class MessageProviderPact { return Promise.reject( `No handler found for message "${message.description}".` + - ` Check your "handlers" configuration`, + ` Check your "handlers" configuration` ); } diff --git a/src/pact-web.spec.ts b/src/pact-web.spec.ts index b54070354..ddc9db81d 100644 --- a/src/pact-web.spec.ts +++ b/src/pact-web.spec.ts @@ -24,14 +24,14 @@ describe("PactWeb", () => { port: 1234, provider: "B", spec: 2, - ssl: false, + ssl: false } as PactOptionsComplete; const sandbox = sinon.sandbox.create({ injectInto: null, properties: ["spy", "stub", "mock"], useFakeServer: false, - useFakeTimers: false, + useFakeTimers: false }); afterEach(() => { @@ -41,7 +41,7 @@ describe("PactWeb", () => { describe("#constructor", () => { const defaults = { consumer: "A", - provider: "B", + provider: "B" } as PactOptions; it("throws Error when consumer not provided", () => { @@ -64,37 +64,45 @@ describe("PactWeb", () => { withRequest: { headers: { Accept: "application/json" }, method: HTTPMethod.GET, - path: "/projects", + path: "/projects" }, willRespondWith: { body: {}, headers: { "Content-Type": "application/json" }, - status: 200, - }, + status: 200 + } }; describe("when given a provider state", () => { - it("creates interaction with state", (done) => { - const pact = Object.create(PactWeb.prototype) as any as PactWeb; + it("creates interaction with state", done => { + const pact = (Object.create(PactWeb.prototype) as any) as PactWeb; pact.opts = fullOpts; - pact.mockService = { - addInteraction: (int: InteractionObject): Promise => Promise.resolve(int.state), - } as any as MockService; - expect(pact.addInteraction(interaction)).to.eventually.have.property("providerState").notify(done); + pact.mockService = ({ + addInteraction: ( + int: InteractionObject + ): Promise => Promise.resolve(int.state) + } as any) as MockService; + expect(pact.addInteraction(interaction)) + .to.eventually.have.property("providerState") + .notify(done); }); }); describe("when not given a provider state", () => { - it("creates interaction with state", (done) => { - const pact = Object.create(PactWeb.prototype) as any as PactWeb; + it("creates interaction with state", done => { + const pact = (Object.create(PactWeb.prototype) as any) as PactWeb; pact.opts = fullOpts; - pact.mockService = { - addInteraction: (int: InteractionObject): Promise => Promise.resolve(int.state), - } as any as MockService; + pact.mockService = ({ + addInteraction: ( + int: InteractionObject + ): Promise => Promise.resolve(int.state) + } as any) as MockService; const interactionWithNoState = interaction; interactionWithNoState.state = undefined; - expect(pact.addInteraction(interaction)).to.eventually.not.have.property("providerState").notify(done); + expect(pact.addInteraction(interaction)) + .to.eventually.not.have.property("providerState") + .notify(done); }); }); }); @@ -103,15 +111,21 @@ describe("PactWeb", () => { const Pact = PactWeb; describe("when pact verification is successful", () => { - it("should return a successful promise and remove interactions", (done) => { + it("should return a successful promise and remove interactions", done => { const verifyStub = sandbox.stub(MockService.prototype, "verify"); verifyStub.resolves("verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions"); + const removeInteractionsStub = sandbox.stub( + MockService.prototype, + "removeInteractions" + ); removeInteractionsStub.resolves("removeInteractions"); - const b = Object.create(PactWeb.prototype) as any as PactWeb; + const b = (Object.create(PactWeb.prototype) as any) as PactWeb; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; const verifyPromise = b.verify(); expect(verifyPromise).to.eventually.eq("removeInteractions"); @@ -120,19 +134,27 @@ describe("PactWeb", () => { }); describe("when pact verification is unsuccessful", () => { - it("should throw an error", (done) => { + it("should throw an error", done => { const verifyStub = sandbox.stub(MockService.prototype, "verify"); verifyStub.rejects("not verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions"); + const removeInteractionsStub = sandbox.stub( + MockService.prototype, + "removeInteractions" + ); removeInteractionsStub.resolves("removeInteractions"); - const b = Object.create(PactWeb.prototype) as any as PactWeb; + const b = (Object.create(PactWeb.prototype) as any) as PactWeb; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; const verifyPromise = b.verify(); - expect(verifyPromise).to.eventually.be.rejectedWith(Error).notify(done); - verifyPromise.catch((e) => { + expect(verifyPromise) + .to.eventually.be.rejectedWith(Error) + .notify(done); + verifyPromise.catch(e => { expect(removeInteractionsStub).to.callCount(0); }); }); @@ -140,17 +162,27 @@ describe("PactWeb", () => { describe("when pact verification is successful", () => { describe("and an error is thrown in the cleanup", () => { - it("should throw an error", (done) => { + it("should throw an error", done => { const verifyStub = sandbox.stub(MockService.prototype, "verify"); verifyStub.resolves("verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions"); - removeInteractionsStub.throws(new Error("error removing interactions")); - - const b = Object.create(PactWeb.prototype) as any as PactWeb; + const removeInteractionsStub = sandbox.stub( + MockService.prototype, + "removeInteractions" + ); + removeInteractionsStub.throws( + new Error("error removing interactions") + ); + + const b = (Object.create(PactWeb.prototype) as any) as PactWeb; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; - - expect(b.verify()).to.eventually.be.rejectedWith(Error).notify(done); + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; + + expect(b.verify()) + .to.eventually.be.rejectedWith(Error) + .notify(done); }); }); }); @@ -160,12 +192,17 @@ describe("PactWeb", () => { const Pact = PactWeb; describe("when writing Pact is successful", () => { - it("should return a successful promise and shut down down the mock server", (done) => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").resolves("pact file written!"); + it("should return a successful promise and shut down down the mock server", done => { + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .resolves("pact file written!"); - const p = Object.create(PactWeb.prototype) as any as PactWeb; + const p = (Object.create(PactWeb.prototype) as any) as PactWeb; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; const writePactPromise = p.finalize(); expect(writePactPromise).to.eventually.be.fulfilled.notify(done); @@ -173,15 +210,22 @@ describe("PactWeb", () => { }); describe("when writing Pact is unsuccessful", () => { - it("should throw an error", (done) => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").rejects("pact not file written!"); + it("should throw an error", done => { + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .rejects("pact not file written!"); - const p = Object.create(PactWeb.prototype) as any as PactWeb; + const p = (Object.create(PactWeb.prototype) as any) as PactWeb; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; const writePactPromise = p.finalize(); - expect(writePactPromise).to.eventually.be.rejectedWith(Error).notify(done); + expect(writePactPromise) + .to.eventually.be.rejectedWith(Error) + .notify(done); }); }); }); @@ -190,12 +234,17 @@ describe("PactWeb", () => { const Pact = PactWeb; describe("when writing Pact is successful", () => { - it("should return a successful promise", (done) => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").resolves("pact file written!"); + it("should return a successful promise", done => { + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .resolves("pact file written!"); - const p = Object.create(PactWeb.prototype) as any as PactWeb; + const p = (Object.create(PactWeb.prototype) as any) as PactWeb; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; const writePactPromise = p.writePact(); expect(writePactPromise).to.eventually.eq("pact file written!"); @@ -208,18 +257,24 @@ describe("PactWeb", () => { const Pact = PactWeb; describe("when removing interactions is successful", () => { - it("should return a successful promise", (done) => { + it("should return a successful promise", done => { const removeInteractionsStub = sandbox .stub(MockService.prototype, "removeInteractions") .resolves("interactions removed!"); - const p = Object.create(PactWeb.prototype) as any as PactWeb; + const p = (Object.create(PactWeb.prototype) as any) as PactWeb; p.opts = fullOpts; - p.mockService = { removeInteractions: removeInteractionsStub } as any as MockService; + p.mockService = ({ + removeInteractions: removeInteractionsStub + } as any) as MockService; const removeInteractionsPromise = p.removeInteractions(); - expect(removeInteractionsPromise).to.eventually.eq("interactions removed!"); - expect(removeInteractionsPromise).to.eventually.be.fulfilled.notify(done); + expect(removeInteractionsPromise).to.eventually.eq( + "interactions removed!" + ); + expect(removeInteractionsPromise).to.eventually.be.fulfilled.notify( + done + ); }); }); }); diff --git a/src/pact-web.ts b/src/pact-web.ts index 48525b0d2..819bc9e54 100644 --- a/src/pact-web.ts +++ b/src/pact-web.ts @@ -32,7 +32,7 @@ export class PactWeb { port: 1234, provider: "", spec: 2, - ssl: false, + ssl: false } as PactOptions; this.opts = { ...defaults, ...config } as PactOptionsComplete; @@ -42,10 +42,18 @@ export class PactWeb { and will be removed in the next major version`); } - console.info(`Setting up Pact using mock service on port: "${this.opts.port}"`); + console.info( + `Setting up Pact using mock service on port: "${this.opts.port}"` + ); - this.mockService = new MockService(this.opts.consumer, this.opts.provider, this.opts.port, this.opts.host, - this.opts.ssl, this.opts.pactfileWriteMode); + this.mockService = new MockService( + this.opts.consumer, + this.opts.provider, + this.opts.port, + this.opts.host, + this.opts.ssl, + this.opts.pactfileWriteMode + ); } /** @@ -76,7 +84,8 @@ export class PactWeb { * @returns {Promise} */ public verify(): Promise { - return this.mockService.verify() + return this.mockService + .verify() .then(() => this.mockService.removeInteractions()) .catch((e: any) => { throw new Error(e); @@ -89,7 +98,9 @@ export class PactWeb { * @returns {Promise} */ public finalize(): Promise { - return this.mockService.writePact().then(() => this.mockService.removeInteractions()); + return this.mockService + .writePact() + .then(() => this.mockService.removeInteractions()); } /** * Writes the Pact file but leave interactions in. diff --git a/src/pact.integration.spec.ts b/src/pact.integration.spec.ts index 2bf0e046d..45792df5d 100644 --- a/src/pact.integration.spec.ts +++ b/src/pact.integration.spec.ts @@ -12,8 +12,7 @@ const expect = chai.expect; const { eachLike, like, term } = Matchers; describe("Integration", () => { - - ["http", "https"].forEach((protocol) => { + ["http", "https"].forEach(protocol => { describe(`Pact on ${protocol} protocol`, () => { const MOCK_PORT = Math.floor(Math.random() * 999) + 9000; const PROVIDER_URL = `${protocol}://localhost:${MOCK_PORT}`; @@ -24,37 +23,39 @@ describe("Integration", () => { log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), dir: path.resolve(process.cwd(), "pacts"), logLevel: "warn", - ssl: (protocol === "https"), - spec: 2, + ssl: protocol === "https", + spec: 2 }); - const EXPECTED_BODY = [{ - id: 1, - name: "Project 1", - due: "2016-02-11T09:46:56.023Z", - tasks: [ - { - id: 1, - name: "Do the laundry", - done: true, - }, - { - id: 2, - name: "Do the dishes", - done: false, - }, - { - id: 3, - name: "Do the backyard", - done: false, - }, - { - id: 4, - name: "Do nothing", - done: false, - }, - ], - }]; + const EXPECTED_BODY = [ + { + id: 1, + name: "Project 1", + due: "2016-02-11T09:46:56.023Z", + tasks: [ + { + id: 1, + name: "Do the laundry", + done: true + }, + { + id: 2, + name: "Do the dishes", + done: false + }, + { + id: 3, + name: "Do the backyard", + done: false + }, + { + id: 4, + name: "Do nothing", + done: false + } + ] + } + ]; before(() => provider.setup()); @@ -62,43 +63,44 @@ describe("Integration", () => { after(() => provider.finalize()); context("with a single request", () => { - // add interactions, as many as needed - before(() => provider.addInteraction({ - state: "i have a list of projects", - uponReceiving: "a request for projects", - withRequest: { - method: HTTPMethod.GET, - path: "/projects", - headers: { - Accept: "application/json", - }, - }, - willRespondWith: { - status: 200, - headers: { - "Content-Type": "application/json", + before(() => + provider.addInteraction({ + state: "i have a list of projects", + uponReceiving: "a request for projects", + withRequest: { + method: HTTPMethod.GET, + path: "/projects", + headers: { + Accept: "application/json" + } }, - body: EXPECTED_BODY, - }, - })); + willRespondWith: { + status: 200, + headers: { + "Content-Type": "application/json" + }, + body: EXPECTED_BODY + } + }) + ); // execute your assertions - it("returns the correct body", () => superagent - .get(`${PROVIDER_URL}/projects`) - .set({ - Accept: "application/json", - }) - .then((res: any) => { - expect(res.text).to.eql(JSON.stringify(EXPECTED_BODY)); - })); + it("returns the correct body", () => + superagent + .get(`${PROVIDER_URL}/projects`) + .set({ + Accept: "application/json" + }) + .then((res: any) => { + expect(res.text).to.eql(JSON.stringify(EXPECTED_BODY)); + })); // verify with Pact, and reset expectations it("successfully verifies", () => provider.verify()); }); context("with a single request with query string parameters", () => { - // add interactions, as many as needed before(() => { return provider.addInteraction({ @@ -108,38 +110,38 @@ describe("Integration", () => { method: HTTPMethod.GET, path: "/projects", query: { - from: "today", + from: "today" }, headers: { - Accept: "application/json", - }, + Accept: "application/json" + } }, willRespondWith: { status: 200, headers: { - "Content-Type": "application/json", + "Content-Type": "application/json" }, - body: EXPECTED_BODY, - }, + body: EXPECTED_BODY + } }); }); // execute your assertions - it("returns the correct body", () => superagent - .get(`${PROVIDER_URL}/projects?from=today`) - .set({ - Accept: "application/json", - }) - .then((res: any) => { - expect(res.text).to.eql(JSON.stringify(EXPECTED_BODY)); - })); + it("returns the correct body", () => + superagent + .get(`${PROVIDER_URL}/projects?from=today`) + .set({ + Accept: "application/json" + }) + .then((res: any) => { + expect(res.text).to.eql(JSON.stringify(EXPECTED_BODY)); + })); // verify with Pact, and reset expectations it("successfully verifies", () => provider.verify()); }); context("with a single request and eachLike, like, term", () => { - // add interactions, as many as needed before(() => { return provider.addInteraction({ @@ -149,30 +151,35 @@ describe("Integration", () => { method: HTTPMethod.GET, path: "/projects", headers: { - Accept: "application/json", - }, + Accept: "application/json" + } }, willRespondWith: { status: 200, headers: { "Content-Type": term({ generate: "application/json", - matcher: "application\/json", - }), + matcher: "application/json" + }) }, - body: [{ - id: 1, - name: "Project 1", - due: "2016-02-11T09:46:56.023Z", - tasks: eachLike({ - id: like(1), - name: like("Do the laundry"), - done: like(true), - }, { - min: 4, - }), - }], - }, + body: [ + { + id: 1, + name: "Project 1", + due: "2016-02-11T09:46:56.023Z", + tasks: eachLike( + { + id: like(1), + name: like("Do the laundry"), + done: like(true) + }, + { + min: 4 + } + ) + } + ] + } }); }); @@ -183,7 +190,9 @@ describe("Integration", () => { .set({ Accept: "application/json" }) .then((res: any) => JSON.parse(res.text)[0]); - return expect(verificationPromise).to.eventually.have.property("tasks"); + return expect(verificationPromise).to.eventually.have.property( + "tasks" + ); }); // verify with Pact, and reset expectations @@ -191,7 +200,6 @@ describe("Integration", () => { }); context("with two requests", () => { - before(() => { const interaction1 = provider.addInteraction({ state: "i have a list of projects", @@ -200,16 +208,16 @@ describe("Integration", () => { method: HTTPMethod.GET, path: "/projects", headers: { - Accept: "application/json", - }, + Accept: "application/json" + } }, willRespondWith: { status: 200, headers: { - "Content-Type": "application/json", + "Content-Type": "application/json" }, - body: EXPECTED_BODY, - }, + body: EXPECTED_BODY + } }); const interaction2 = provider.addInteraction({ @@ -219,35 +227,38 @@ describe("Integration", () => { method: HTTPMethod.GET, path: "/projects/2", headers: { - Accept: "application/json", - }, + Accept: "application/json" + } }, willRespondWith: { status: 404, headers: { - "Content-Type": "application/json", - }, - }, + "Content-Type": "application/json" + } + } }); return Promise.all([interaction1, interaction2]); }); it("allows two requests", () => { - const verificationPromise = - superagent.get(`${PROVIDER_URL}/projects`) - .set({ - Accept: "application/json", - }) - .then((res: any) => res.text); + const verificationPromise = superagent + .get(`${PROVIDER_URL}/projects`) + .set({ + Accept: "application/json" + }) + .then((res: any) => res.text); - const verificationPromise404 = - superagent.get(`${PROVIDER_URL}/projects/2`).set({ - Accept: "application/json", + const verificationPromise404 = superagent + .get(`${PROVIDER_URL}/projects/2`) + .set({ + Accept: "application/json" }); return Promise.all([ - expect(verificationPromise).to.eventually.equal(JSON.stringify(EXPECTED_BODY)), - expect(verificationPromise404).to.eventually.be.rejected, + expect(verificationPromise).to.eventually.equal( + JSON.stringify(EXPECTED_BODY) + ), + expect(verificationPromise404).to.eventually.be.rejected ]); }); @@ -257,34 +268,44 @@ describe("Integration", () => { context("with an unexpected interaction", () => { // add interactions, as many as needed - before(() => provider.addInteraction({ - state: "i have a list of projects", - uponReceiving: "a request for projects", - withRequest: { - method: HTTPMethod.GET, - path: "/projects", - headers: { - Accept: "application/json", - }, - }, - willRespondWith: { - status: 200, - headers: { - "Content-Type": "application/json", - }, - body: EXPECTED_BODY, - }, - }).then(() => console.log("Adding interaction worked"), () => console.warn("Adding interaction failed."))); + before(() => + provider + .addInteraction({ + state: "i have a list of projects", + uponReceiving: "a request for projects", + withRequest: { + method: HTTPMethod.GET, + path: "/projects", + headers: { + Accept: "application/json" + } + }, + willRespondWith: { + status: 200, + headers: { + "Content-Type": "application/json" + }, + body: EXPECTED_BODY + } + }) + .then( + () => console.log("Adding interaction worked"), + () => console.warn("Adding interaction failed.") + ) + ); it("fails verification", () => { - const verificationPromise = - superagent.get(`${PROVIDER_URL}/projects`) - .set({ Accept: "application/json" }) - .then(() => superagent.delete(`${PROVIDER_URL}/projects/2`).catch(() => { })) - .then(() => provider.verify()); + const verificationPromise = superagent + .get(`${PROVIDER_URL}/projects`) + .set({ Accept: "application/json" }) + .then(() => + superagent.delete(`${PROVIDER_URL}/projects/2`).catch(() => {}) + ) + .then(() => provider.verify()); - return expect(verificationPromise) - .to.be.rejectedWith("Pact verification failed - expected interactions did not match actual."); + return expect(verificationPromise).to.be.rejectedWith( + "Pact verification failed - expected interactions did not match actual." + ); }); }); }); diff --git a/src/pact.spec.ts b/src/pact.spec.ts index b7849728a..ef19342c3 100644 --- a/src/pact.spec.ts +++ b/src/pact.spec.ts @@ -17,22 +17,17 @@ const proxyquire = require("proxyquire").noCallThru(); // Mock out the PactNode interfaces class PactServer { - public start(): void { - } + public start(): void {} - public delete(): void { - } + public delete(): void {} } class PactNodeFactory { - public logLevel(opts: any): void { - } + public logLevel(opts: any): void {} - public removeAllServers(): void { - } + public removeAllServers(): void {} - public createServer(opts: any): any { - } + public createServer(opts: any): any {} } class PactNodeMockService { @@ -51,7 +46,7 @@ describe("Pact", () => { logLevel: "info", spec: 2, cors: false, - pactfileWriteMode: "overwrite", + pactfileWriteMode: "overwrite" } as PactOptionsComplete; let mockServiceStub: sinon.SinonStub; @@ -59,7 +54,7 @@ describe("Pact", () => { injectInto: null, properties: ["spy", "stub", "mock"], useFakeTimers: false, - useFakeServer: false, + useFakeServer: false }); beforeEach(() => { @@ -75,7 +70,7 @@ describe("Pact", () => { beforeEach(() => { const imported = proxyquire("./pact", { - "@pact-foundation/pact-node": sinon.createStubInstance(PactNodeFactory), + "@pact-foundation/pact-node": sinon.createStubInstance(PactNodeFactory) }); Pact = imported.Pact; }); @@ -96,7 +91,7 @@ describe("Pact", () => { describe("#createOptionsWithDefault", () => { const constructorOpts: PactOptions = { consumer: "A", - provider: "B", + provider: "B" }; it("should merge options with sensible defaults", () => { @@ -123,9 +118,11 @@ describe("Pact", () => { describe("and pact-node is unable to start the server", () => { it("should return a rejected promise", () => { // TODO: actually test is pact-node is failing on start with a bad config instead of stubbing it - const startStub = sandbox.stub(PactServer.prototype, "start").throws("start"); + const startStub = sandbox + .stub(PactServer.prototype, "start") + .throws("start"); startStub.rejects(); - const b = Object.create(Pact.prototype) as any as PactType; + const b = (Object.create(Pact.prototype) as any) as PactType; b.opts = fullOpts; b.server = { start: startStub } as any; return expect(b.setup()).to.eventually.be.rejected; @@ -137,7 +134,7 @@ describe("Pact", () => { // TODO: actually test is pact-node is starting instead of stubbing it const startStub = sandbox.stub(PactServer.prototype, "start"); startStub.resolves(); - const b = Object.create(Pact.prototype) as any as PactType; + const b = (Object.create(Pact.prototype) as any) as PactType; b.opts = fullOpts; b.server = { start: startStub } as any; return expect(b.setup()).to.eventually.be.fulfilled; @@ -153,35 +150,43 @@ describe("Pact", () => { withRequest: { method: HTTPMethod.GET, path: "/projects", - headers: { Accept: "application/json" }, + headers: { Accept: "application/json" } }, willRespondWith: { status: 200, headers: { "Content-Type": "application/json" }, - body: {}, - }, + body: {} + } }; describe("when given a provider state", () => { it("creates interaction with state", () => { - const pact = Object.create(Pact.prototype) as any as PactType; + const pact = (Object.create(Pact.prototype) as any) as PactType; pact.opts = fullOpts; - pact.mockService = { - addInteraction: (int: InteractionObject): Promise => Promise.resolve(int.state), - } as any as MockService; - return expect(pact.addInteraction(interaction)).to.eventually.have.property("providerState"); + pact.mockService = ({ + addInteraction: ( + int: InteractionObject + ): Promise => Promise.resolve(int.state) + } as any) as MockService; + return expect( + pact.addInteraction(interaction) + ).to.eventually.have.property("providerState"); }); }); describe("when not given a provider state", () => { it("creates interaction with no state", () => { - const pact = Object.create(Pact.prototype) as any as PactType; + const pact = (Object.create(Pact.prototype) as any) as PactType; pact.opts = fullOpts; - pact.mockService = { - addInteraction: (int: InteractionObject): Promise => Promise.resolve(int.state), - } as any as MockService; + pact.mockService = ({ + addInteraction: ( + int: InteractionObject + ): Promise => Promise.resolve(int.state) + } as any) as MockService; interaction.state = undefined; - return expect(pact.addInteraction(interaction)).to.eventually.not.have.property("providerState"); + return expect( + pact.addInteraction(interaction) + ).to.eventually.not.have.property("providerState"); }); describe("when given an Interaction as a builder", () => { @@ -192,20 +197,23 @@ describe("Pact", () => { .withRequest({ method: HTTPMethod.GET, path: "/projects", - headers: { Accept: "application/json" }, + headers: { Accept: "application/json" } }) .willRespondWith({ status: 200, headers: { "Content-Type": "application/json" }, - body: {}, + body: {} }); - const pact = Object.create(Pact.prototype) as any as PactType; + const pact = (Object.create(Pact.prototype) as any) as PactType; pact.opts = fullOpts; - pact.mockService = { - addInteraction: (int: Interaction): Promise => Promise.resolve(int), - } as any as MockService; - return expect(pact.addInteraction(interaction2)).to.eventually.have.property("given"); + pact.mockService = ({ + addInteraction: (int: Interaction): Promise => + Promise.resolve(int) + } as any) as MockService; + return expect( + pact.addInteraction(interaction2) + ).to.eventually.have.property("given"); }); }); }); @@ -216,36 +224,50 @@ describe("Pact", () => { describe("when pact verification is successful", () => { it("should return a successful promise and remove interactions", () => { - const verifyStub = sandbox.stub(MockService.prototype, "verify").resolves("verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions") + const verifyStub = sandbox + .stub(MockService.prototype, "verify") + .resolves("verified!"); + const removeInteractionsStub = sandbox + .stub(MockService.prototype, "removeInteractions") .resolves("removeInteractions"); - const b = Object.create(Pact.prototype) as any as PactType; + const b = (Object.create(Pact.prototype) as any) as PactType; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; const verifyPromise = b.verify(); return Promise.all([ expect(verifyPromise).to.eventually.eq("removeInteractions"), - expect(verifyPromise).to.eventually.be.fulfilled, + expect(verifyPromise).to.eventually.be.fulfilled ]); }); }); describe("when pact verification is unsuccessful", () => { it("should throw an error", () => { - const verifyStub = sandbox.stub(MockService.prototype, "verify").rejects("not verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions") + const verifyStub = sandbox + .stub(MockService.prototype, "verify") + .rejects("not verified!"); + const removeInteractionsStub = sandbox + .stub(MockService.prototype, "removeInteractions") .resolves("removeInteractions"); - const b = Object.create(Pact.prototype) as any as PactType; + const b = (Object.create(Pact.prototype) as any) as PactType; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; const verifyPromise = b.verify(); return Promise.all([ expect(verifyPromise).to.eventually.be.rejectedWith(Error), - verifyPromise.catch(() => expect(removeInteractionsStub).to.callCount(0)), + verifyPromise.catch(() => + expect(removeInteractionsStub).to.callCount(0) + ) ]); }); }); @@ -253,13 +275,23 @@ describe("Pact", () => { describe("when pact verification is successful", () => { describe("and an error is thrown in the cleanup", () => { it("should throw an error", () => { - const verifyStub = sandbox.stub(MockService.prototype, "verify").resolves("verified!"); - const removeInteractionsStub = sandbox.stub(MockService.prototype, "removeInteractions"); - removeInteractionsStub.throws(new Error("error removing interactions")); - - const b = Object.create(Pact.prototype) as any as PactType; + const verifyStub = sandbox + .stub(MockService.prototype, "verify") + .resolves("verified!"); + const removeInteractionsStub = sandbox.stub( + MockService.prototype, + "removeInteractions" + ); + removeInteractionsStub.throws( + new Error("error removing interactions") + ); + + const b = (Object.create(Pact.prototype) as any) as PactType; b.opts = fullOpts; - b.mockService = { verify: verifyStub, removeInteractions: removeInteractionsStub } as any as MockService; + b.mockService = ({ + verify: verifyStub, + removeInteractions: removeInteractionsStub + } as any) as MockService; return expect(b.verify()).to.eventually.be.rejectedWith(Error); }); @@ -272,12 +304,19 @@ describe("Pact", () => { describe("when writing Pact is successful", () => { it("should return a successful promise and shut down down the mock server", () => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").resolves(); + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .resolves(); - const p = Object.create(Pact.prototype) as any as PactType; + const p = (Object.create(Pact.prototype) as any) as PactType; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; - p.server = { delete: sandbox.stub(PactServer.prototype, "delete").resolves() } as any; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; + p.server = { + delete: sandbox.stub(PactServer.prototype, "delete").resolves() + } as any; return expect(p.finalize()).to.eventually.be.fulfilled; }); @@ -285,30 +324,44 @@ describe("Pact", () => { describe("when writing Pact is unsuccessful", () => { it("should throw an error and shut down the server", () => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").rejects(); - const deleteStub = sandbox.stub(PactServer.prototype, "delete").resolves(); - - const p = Object.create(Pact.prototype) as any as PactType; + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .rejects(); + const deleteStub = sandbox + .stub(PactServer.prototype, "delete") + .resolves(); + + const p = (Object.create(Pact.prototype) as any) as PactType; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; p.server = { delete: deleteStub } as any; - return expect(p.finalize()).to.eventually.be.rejected - .then(() => expect(deleteStub).to.callCount(1)); + return expect(p.finalize()).to.eventually.be.rejected.then(() => + expect(deleteStub).to.callCount(1) + ); }); }); describe("when writing pact is successful and shutting down the mock server is unsuccessful", () => { it("should throw an error", () => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").resolves(); + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .resolves(); - const p = Object.create(Pact.prototype) as any as PactType; + const p = (Object.create(Pact.prototype) as any) as PactType; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; - p.server = { delete: sandbox.stub(PactServer.prototype, "delete").rejects() } as any; - - return expect(p.finalize) - .to.throw(Error); + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; + p.server = { + delete: sandbox.stub(PactServer.prototype, "delete").rejects() + } as any; + + return expect(p.finalize).to.throw(Error); }); }); }); @@ -318,16 +371,21 @@ describe("Pact", () => { describe("when writing Pact is successful", () => { it("should return a successful promise", () => { - const writePactStub = sandbox.stub(MockService.prototype, "writePact").resolves("pact file written!"); + const writePactStub = sandbox + .stub(MockService.prototype, "writePact") + .resolves("pact file written!"); - const p = Object.create(Pact.prototype) as any as PactType; + const p = (Object.create(Pact.prototype) as any) as PactType; p.opts = fullOpts; - p.mockService = { writePact: writePactStub, removeInteractions: sandbox.stub() } as any as MockService; + p.mockService = ({ + writePact: writePactStub, + removeInteractions: sandbox.stub() + } as any) as MockService; const writePactPromise = p.writePact(); return Promise.all([ expect(writePactPromise).to.eventually.eq("pact file written!"), - expect(writePactPromise).to.eventually.be.fulfilled, + expect(writePactPromise).to.eventually.be.fulfilled ]); }); }); @@ -342,14 +400,18 @@ describe("Pact", () => { .stub(MockService.prototype, "removeInteractions") .resolves("interactions removed!"); - const p = Object.create(Pact.prototype) as any as PactType; + const p = (Object.create(Pact.prototype) as any) as PactType; p.opts = fullOpts; - p.mockService = { removeInteractions: removeInteractionsStub } as any as MockService; + p.mockService = ({ + removeInteractions: removeInteractionsStub + } as any) as MockService; const removeInteractionsPromise = p.removeInteractions(); return Promise.all([ - expect(removeInteractionsPromise).to.eventually.eq("interactions removed!"), - expect(removeInteractionsPromise).to.eventually.be.fulfilled, + expect(removeInteractionsPromise).to.eventually.eq( + "interactions removed!" + ), + expect(removeInteractionsPromise).to.eventually.be.fulfilled ]); }); }); diff --git a/src/pact.ts b/src/pact.ts index ca22a7ff4..aa9e3e6f3 100644 --- a/src/pact.ts +++ b/src/pact.ts @@ -37,10 +37,12 @@ export class Pact { port: 1234, provider: "", spec: 2, - ssl: false, + ssl: false } as PactOptions; - public static createOptionsWithDefaults(opts: PactOptions): PactOptionsComplete { + public static createOptionsWithDefaults( + opts: PactOptions + ): PactOptionsComplete { return { ...Pact.defaults, ...opts } as PactOptionsComplete; } @@ -73,14 +75,22 @@ export class Pact { spec: this.opts.spec, ssl: this.opts.ssl, sslcert: this.opts.sslcert, - sslkey: this.opts.sslkey, + sslkey: this.opts.sslkey }); - logger.info(`Setting up Pact with Consumer "${this.opts.consumer}" and Provider "${this.opts.provider}" + logger.info(`Setting up Pact with Consumer "${ + this.opts.consumer + }" and Provider "${this.opts.provider}" using mock service on Port: "${this.opts.port}"`); - this.mockService = new MockService(undefined, undefined, this.opts.port, this.opts.host, - this.opts.ssl, this.opts.pactfileWriteMode); + this.mockService = new MockService( + undefined, + undefined, + this.opts.port, + this.opts.host, + this.opts.ssl, + this.opts.pactfileWriteMode + ); } /** @@ -88,9 +98,16 @@ export class Pact { * @returns {Promise} */ public setup(): Promise { - return isPortAvailable(this.opts.port, this.opts.host) - // Need to wrap it this way until we remove q.Promise from pact-node - .then(() => new Promise((resolve, reject) => this.server.start().then(() => resolve(), () => reject()))); + return ( + isPortAvailable(this.opts.port, this.opts.host) + // Need to wrap it this way until we remove q.Promise from pact-node + .then( + () => + new Promise((resolve, reject) => + this.server.start().then(() => resolve(), () => reject()) + ) + ) + ); } /** @@ -100,7 +117,9 @@ export class Pact { * @param {Interaction} interactionObj * @returns {Promise} */ - public addInteraction(interactionObj: InteractionObject | Interaction): Promise { + public addInteraction( + interactionObj: InteractionObject | Interaction + ): Promise { if (interactionObj instanceof Interaction) { return this.mockService.addInteraction(interactionObj); } @@ -124,7 +143,8 @@ export class Pact { * @returns {Promise} */ public verify(): Promise { - return this.mockService.verify() + return this.mockService + .verify() .then(() => this.mockService.removeInteractions()) .catch((e: any) => { // Properly format the error @@ -134,7 +154,9 @@ export class Pact { console.error(clc.red(e)); /* tslint:enable: */ - throw new Error("Pact verification failed - expected interactions did not match actual."); + throw new Error( + "Pact verification failed - expected interactions did not match actual." + ); }); } @@ -147,19 +169,33 @@ export class Pact { */ public finalize(): Promise { if (this.finalized) { - logger.warn("finalize() has already been called, this is probably a logic error in your test setup. " + - "In the future this will be an error."); + logger.warn( + "finalize() has already been called, this is probably a logic error in your test setup. " + + "In the future this will be an error." + ); } this.finalized = true; - return this.mockService.writePact() - .then(() => logger.info("Pact File Written"), (e) => { - return Promise.reject(e); - }) - .then(() => new Promise((resolve, reject) => this.server.delete().then(() => resolve(), (e) => reject(e)))) - .catch((e: Error) => new Promise((resolve, reject) => { - return this.server.delete().finally(() => reject(e)); - })); + return this.mockService + .writePact() + .then( + () => logger.info("Pact File Written"), + e => { + return Promise.reject(e); + } + ) + .then( + () => + new Promise((resolve, reject) => + this.server.delete().then(() => resolve(), e => reject(e)) + ) + ) + .catch( + (e: Error) => + new Promise((resolve, reject) => { + return this.server.delete().finally(() => reject(e)); + }) + ); } /** diff --git a/tslint.json b/tslint.json index c3656da9a..e88ced4e9 100644 --- a/tslint.json +++ b/tslint.json @@ -6,6 +6,7 @@ "interface-name": [true, "never-prefix"], "no-var-requires": false, "ordered-imports": false, + "no-unused-variable": true, "whitespace": [ true, "check-branch",