├── .editorconfig
├── .eslintrc
├── .github
├── renovate.json
└── workflows
│ ├── publish.yaml
│ ├── release.yaml
│ └── test-and-build.yaml
├── .gitignore
├── .npmignore
├── .prettierrc
├── .versionrc
├── .whitesource
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example-cypress-10
└── todo-example
│ ├── .gitignore
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ ├── e2e
│ │ ├── todo.cy.js
│ │ └── todoGet.cy.js
│ ├── fixtures
│ │ └── todo.json
│ ├── plugins
│ │ └── index.js
│ └── support
│ │ ├── commands.js
│ │ └── e2e.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── example
└── todo-example
│ ├── .gitignore
│ ├── README.md
│ ├── cypress.json
│ ├── cypress
│ ├── fixtures
│ │ └── todo.json
│ ├── integration
│ │ ├── todo.spec.js
│ │ └── todoGet.spec.js
│ ├── plugins
│ │ └── index.js
│ └── support
│ │ ├── commands.js
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── jest.config.js
├── package-lock.json
├── package.json
├── scripts
└── ci
│ └── build-and-test.sh
├── src
├── constants.ts
├── index.ts
├── plugin.ts
├── types.ts
└── utils.ts
├── test
└── utils.test.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 | # editorconfig-tools is unable to ignore longs strings or urls
11 | max_line_length = off
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "plugins": [
4 | "@typescript-eslint"
5 | ],
6 | "globals": {
7 | "cy": false,
8 | "Cypress": false,
9 | "assert": false
10 | },
11 | "extends": "./tsconfig.json"
12 | }
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:best-practices"
5 | ],
6 | "pre-commit": {
7 | "enabled": true
8 | },
9 | "git-submodules": {
10 | "enabled": true
11 | },
12 | "lockFileMaintenance": {
13 | "enabled": true
14 | },
15 | "prHourlyLimit": 0,
16 | "prConcurrentLimit": 0,
17 | "packageRules": [
18 | {
19 | "matchUpdateTypes": [
20 | "minor",
21 | "patch",
22 | "pin",
23 | "digest"
24 | ],
25 | "automerge": true
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yaml:
--------------------------------------------------------------------------------
1 | name: Publish to NPM
2 | on:
3 | workflow_run:
4 | workflows: [Release]
5 | types:
6 | - completed
7 | jobs:
8 | publish:
9 | if: ${{ github.event.workflow_run.conclusion == 'success' && !contains(github.event.head_commit.message, 'skip-release') }}
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
13 | - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
14 | with:
15 | node-version: '22.x'
16 | registry-url: 'https://registry.npmjs.org'
17 | - run: npm ci
18 | - run: npm run build
19 | - run: npm publish --access public
20 | env:
21 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
22 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | workflow_dispatch:
4 |
5 | jobs:
6 | release:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
10 | with:
11 | token: ${{ secrets.GH_TOKEN }}
12 | fetch-depth: 0
13 | - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
14 | with:
15 | node-version: '22.x'
16 | registry-url: 'https://registry.npmjs.org'
17 | - run: npm ci
18 | - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2
19 | - name: Bump version and push changelogs
20 | run: |
21 | npm run release
22 | git push origin main --follow-tags
23 |
--------------------------------------------------------------------------------
/.github/workflows/test-and-build.yaml:
--------------------------------------------------------------------------------
1 | name: Build and test
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | push:
8 | branches:
9 | - '*'
10 |
11 | jobs:
12 | build-and-test:
13 | if: ${{!contains(github.event.head_commit.message, 'chore(release)')}}
14 | runs-on: ubuntu-latest
15 | strategy:
16 | matrix:
17 | node-version: [14.x, 16.x]
18 |
19 | steps:
20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
21 | with:
22 | fetch-depth: 0
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: scripts/ci/build-and-test.sh
28 | env:
29 | NODE_VERSION: ${{ matrix.node-version }}
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 |
3 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
4 |
5 | # dependencies
6 | node_modules/
7 |
8 | # testing
9 | coverage
10 |
11 | # production
12 | dist/
13 |
14 | # misc
15 | .DS_Store
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 |
20 | # cypress
21 | lib
22 | pacts/
23 | logs/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # folders
2 | pacts/
3 | test/
4 | example/
5 | .workflow
6 | # files
7 | .babelrc
8 | .editorconfig
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "semi": false,
4 | "singleQuote": true,
5 | "printWidth": 120
6 | }
--------------------------------------------------------------------------------
/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "types": [
3 | {"type":"feat","section":"Features"},
4 | {"type":"fix","section":"Bug Fixes"},
5 | {"type":"test","section":"Tests", "hidden": true},
6 | {"type":"chore","section":"Chores", "hidden": true},
7 | {"type":"ci","hidden":true}
8 | ]
9 | }
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "settingsInheritedFrom": "pactflow/whitesource-config@main"
3 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [1.3.0](https://github.com/pactflow/cypress-pact-adapter/compare/v1.2.6...v1.3.0) (2022-09-22)
6 |
7 |
8 | ### Features
9 |
10 | * Add client name/version into pact file ([bf1c86d](https://github.com/pactflow/cypress-pact-adapter/commit/bf1c86d475a48f50265691a14f477f7c5fcebae2))
11 |
12 | ### [1.2.6](https://github.com/pactflow/cypress-pact-adapter/compare/v1.2.5...v1.2.6) (2022-03-10)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * missing index.d.ts ([22642b6](https://github.com/pactflow/cypress-pact-adapter/commit/22642b6681ce7446aad68c9409e8ef93ae71f042))
18 |
19 | ### [1.2.5](https://github.com/pactflow/cypress-pact-adapter/compare/v1.2.4...v1.2.5) (2022-03-10)
20 |
21 | ### [1.2.4](https://github.com/pactflow/cypress-pact-adaptor/compare/v1.2.3...v1.2.4) (2022-03-09)
22 |
23 | ### [1.2.3](https://github.com/pactflow/cypress-pact/compare/v1.2.2...v1.2.3) (2022-03-09)
24 |
25 |
26 | ### Bug Fixes
27 |
28 | * missing auto generated headers when no setupPactHeaderBlocklist is called ([955d215](https://github.com/pactflow/cypress-pact/commit/955d2150c9d11423e1e91ece6581636bbcc1e8e3))
29 |
30 | ### [1.2.2](https://github.com/pactflow/cypress-pact/compare/v1.2.1...v1.2.2) (2022-03-09)
31 |
32 | ### [1.2.1](https://github.com/pactflow/cypress-pact/compare/v1.2.0...v1.2.1) (2022-03-09)
33 |
34 | ## [1.2.0](https://github.com/pactflow/cypress-pact/compare/v1.1.0...v1.2.0) (2022-03-09)
35 |
36 |
37 | ### Features
38 |
39 | * add blocklist and blocklist api ([d675362](https://github.com/pactflow/cypress-pact/commit/d675362dc394e32fe09b6af004fd62b4d47c3fde))
40 | * add ignoreDefaultBlocklist ([ea99189](https://github.com/pactflow/cypress-pact/commit/ea9918993e7cd623b879adb1489f962c10af7868))
41 |
42 |
43 | ### Bug Fixes
44 |
45 | * example 404 issues ([979fcda](https://github.com/pactflow/cypress-pact/commit/979fcda111cf9a1a5a509bcc814f41aa9b42e9ec))
46 | * update readme and example ([3141fea](https://github.com/pactflow/cypress-pact/commit/3141fea7fe581133e7e692e61c362548c0bca837))
47 |
48 | ## 1.1.0 (2022-03-01)
49 |
50 |
51 | ### Features
52 |
53 | * add setupPact command ([27ee9d3](https://github.com/pactflow/cypress-pact/commit/27ee9d338cb91b9f94fc0befc2a0b72be083e5bd))
54 | * add usePactGet and usePactRequest ([e9f0624](https://github.com/pactflow/cypress-pact/commit/e9f0624b0bd01646c861a1dd4b0d816b887d1de9))
55 | * update status to statusCode ([e330b6e](https://github.com/pactflow/cypress-pact/commit/e330b6e1d6c4caca48dbee6879763f3afba98a6a))
56 | * usePactWait ([8be151e](https://github.com/pactflow/cypress-pact/commit/8be151e00b3038d3977aa41ae99639064927ccec))
57 |
58 |
59 | ### Bug Fixes
60 |
61 | * XHRrequestAndResponse type with body and headers ([30fb932](https://github.com/pactflow/cypress-pact/commit/30fb932f693ccd7acb9252ee948b802df750eee4))
62 |
63 | ### 1.0.1 (2022-03-01)
64 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 PactFlow
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pact Cypress Adapter
2 | [](https://github.com/pactflow/cypress-pact-adapter/actions/workflows/test-and-build.yaml) [](https://badge.fury.io/js/@pactflow%2Fpact-cypress-adapter)
3 |
4 | Generate Pact contracts from your existing Cypress tests.
5 |
6 | > Accelerate your entry into contract testing with the Cypress development experience you know and love. — With Pact Cypress Adapter you can get the extra layer of testing safety, easily using existing mocks you’ve created with Cypress.
7 | >
8 | > Read our [blog post](https://pactflow.io/blog/use-cypress-in-contract-testing/) to find out more, otherwise dive-right in.
9 |
10 | ## Installation
11 | **NPM**:
12 | ```bash
13 | npm i -D @pactflow/pact-cypress-adapter
14 | ```
15 |
16 | **yarn**:
17 | ```bash
18 | yarn add -D @pactflow/pact-cypress-adapter
19 | ```
20 |
21 | ## Setup for Cypress 10
22 | Setup your cypress plugin at `cypress/plugins/index.js`
23 |
24 | ```js
25 | const pactCypressPlugin = require('@pactflow/pact-cypress-adapter/dist/plugin')
26 | const fs = require('fs')
27 |
28 | module.exports = (on, config) => {
29 | pactCypressPlugin(on, config, fs)
30 | }
31 | ```
32 |
33 | Finally, update `cypress/support/e2e.js` file to include cypress-pact commands via adding:
34 | ```js
35 | import '@pactflow/pact-cypress-adapter'
36 | ```
37 |
38 | ## Setup for Cypress 9.x and below
39 | Setup your cypress plugin at `cypress/plugins/index.js`
40 |
41 | ```js
42 | const pactCypressPlugin = require('@pactflow/pact-cypress-adapter/dist/plugin')
43 | const fs = require('fs')
44 |
45 | module.exports = (on, config) => {
46 | pactCypressPlugin(on, config, fs)
47 | }
48 | ```
49 |
50 | Finally, update `cypress/support/index.js` file to include cypress-pact commands via adding:
51 | ```js
52 | import '@pactflow/pact-cypress-adapter'
53 | ```
54 |
55 | ## Configuration
56 | By default, this plugin omits most cypress auto-generated HTTP headers.
57 |
58 | ### Add more headers to blocklist for Cypress 10
59 | To exclude other headers in your pact, add them as a list of strings in `cypress.config.{js,ts,mjs,cjs}` under key `env.headersBlocklist`. Eg. in your cypress config file
60 | ```js
61 | {
62 | ...otherCypressConfig,
63 | "env": {
64 | "headersBlocklist": ["ignore-me-globally"]
65 | }
66 | }
67 | ```
68 |
69 | ### Add more headers to blocklist for Cypress 9.x and below
70 | To exclude other headers in your pact, add them as a list of strings in `cypress.json` under key `env.headersBlocklist`. Eg. in your `cypress.json`
71 | ```js
72 | {
73 | ...otherCypressConfig,
74 | "env": {
75 | "headersBlocklist": ["ignore-me-globally"]
76 | }
77 | }
78 | ```
79 |
80 | Note: Header blocklist can be set up at test level. Check command [cy.setupPactHeaderBlocklist](/#cy.setupPactHeaderBlocklist([headers]))
81 |
82 | ### Ignore cypress auto-generated header blocklist
83 | To stop cypress auto-generated HTTP headers being omitted by the plugin, set `env.ignoreDefaultBlocklist` in your `cypress.json`. Eg. in your `cypress.json`
84 | ```js
85 | {
86 | ...otherCypressConfig,
87 | "env": {
88 | "headersBlocklist": ["ignore-me-globally"],
89 | "ignoreDefaultBlocklist": true
90 |
91 | }
92 | }
93 | ```
94 |
95 | ## Commands
96 | ### cy.setupPact(consumerName:string, providerName: string)
97 | Configure your consumer and provider name
98 |
99 | **Example**
100 | ```js
101 | before(() => {
102 | cy.setupPact('ui-consumer', 'api-provider')
103 | })
104 | ```
105 | ### cy.usePactWait([alias] | alias)
106 | Listen to aliased `cy.intercept` network call(s), record network request and response to a pact file.
107 | [Usage and example](https://docs.cypress.io/api/commands/intercept) about `cy.intercept`
108 |
109 | **Example**
110 | ```js
111 | before(() => {
112 | cy.setupPact('ui-consumer', 'api-provider')
113 | cy.intercept('GET', '/users').as('getAllUsers')
114 | })
115 |
116 | //... cypress test
117 |
118 | after(() => {
119 | cy.usePactWait(['getAllUsers'])
120 | })
121 |
122 | ```
123 |
124 | ### cy.setupPactHeaderBlocklist([headers])
125 | Add a list of headers that will be excluded in a pact at test case level
126 |
127 | **Example**
128 | ```js
129 | before(() => {
130 | cy.setupPact('ui-consumer', 'api-provider')
131 | cy.intercept('GET', '/users', headers: {'ignore-me': 'ignore me please'}).as('getAllUsers')
132 | cy.setupPactHeaderBlocklist(['ignore-me'])
133 | })
134 |
135 | //... cypress test
136 |
137 | after(() => {
138 | cy.usePactWait(['getAllUsers'])
139 | })
140 | ```
141 |
142 | ### cy.usePactRequest(option, alias) and cy.usePactGet([alias] | alias)
143 | Use `cy.usePactRequest` to initiate network calls and use `cy.usePactGet` to record network request and response to a pact file.
144 |
145 | Convenience wrapper for `cy.request(options).as(alias)`
146 |
147 | - Accepts a valid Cypress request options argument [Cypress request options argument](https://docs.cypress.io/api/commands/request#Arguments)
148 |
149 | **Example**
150 | ```js
151 |
152 | before(() => {
153 | cy.setupPact('ui-consumer', 'api-provider')
154 | cy.usePactRequest(
155 | {
156 | method: 'GET',
157 | url: '/users',
158 | },
159 | 'getAllUsers'
160 | )
161 | })
162 |
163 | //... cypress test
164 |
165 | after(() => {
166 | cy.usePactGet(['getAllUsers'])
167 | })
168 |
169 | ```
170 |
171 | ## Example Project
172 | Check out simple react app example projects at [/example/todo-example](/example/todo-example/). Example contains examples for Cypress 10.x and Cypress 9.x.
173 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | video: false,
5 | env: {
6 | headersBlocklist: ['ignore-me-globally'],
7 | ignoreDefaultBlocklist: false,
8 | },
9 | e2e: {
10 | // We've imported your old cypress plugins here.
11 | // You may want to clean this up later by importing these.
12 | setupNodeEvents(on, config) {
13 | return require('./cypress/plugins/index.js')(on, config)
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/e2e/todo.cy.js:
--------------------------------------------------------------------------------
1 | import todosResponse from '../fixtures/todo.json'
2 | describe('example to-do app', () => {
3 | before(() => {
4 | cy.setupPact('ui-consumer', 'todo-api')
5 | cy.intercept(
6 | {
7 | method: 'GET',
8 | url: '**/api/todo',
9 | headers: {
10 | 'x-pactflow': 'blah',
11 | 'ignore-me': 'ignore',
12 | 'ignore-me-globally': 'ignore'
13 | }
14 | },
15 | {
16 | statusCode: 200,
17 | body: todosResponse,
18 | headers: { 'access-control-allow-origin': '*' }
19 | }
20 | ).as('getTodos')
21 | cy.setupPactHeaderBlocklist(['ignore-me'])
22 | cy.visit('http://localhost:3000/')
23 | })
24 |
25 | it('shows todo', () => {
26 | cy.contains('clean desk')
27 | })
28 |
29 | after(() => {
30 | cy.usePactWait('getTodos').its('response.statusCode').should('eq', 200)
31 | })
32 | })
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/e2e/todoGet.cy.js:
--------------------------------------------------------------------------------
1 |
2 | describe('example to-do app', () => {
3 | before(() => {
4 | cy.setupPact('ui-consumer', 'todo-api')
5 | cy.usePactRequest(
6 | {
7 | method: 'GET',
8 | url: 'https://jsonplaceholder.typicode.com/todos',
9 | headers: {
10 | 'x-pactflow': 'blah',
11 | 'ignore-me': 'ignore',
12 | 'ignore-me-globally': 'ignore'
13 | }
14 | },
15 | 'getTodosGet'
16 | )
17 | cy.setupPactHeaderBlocklist(['ignore-me'])
18 | })
19 |
20 | it('shows todo', () => {
21 | })
22 |
23 | after(() => {
24 | cy.usePactGet('getTodosGet')
25 | .its('response.statusCode')
26 | .should('eq', 200)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/fixtures/todo.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "content": "clean desk"
4 | },
5 | {
6 | "content": "make coffee"
7 | }
8 | ]
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | const pactCypressPlugin = require('@pactflow/pact-cypress-adapter/dist/plugin')
16 | const fs = require('fs')
17 |
18 | module.exports = (on, config) => {
19 | pactCypressPlugin(on, config, fs)
20 | }
21 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 | import '@pactflow/pact-cypress-adapter'
19 |
20 | // Alternatively you can use CommonJS syntax:
21 | // require('./commands')
22 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^5.0.0"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject",
19 | "cypress:open": "cypress open",
20 | "cypress:run": "cypress run --headless --browser chrome",
21 | "http-server": "http-server -p 3000 build/ &"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "devDependencies": {
42 | "@pactflow/pact-cypress-adapter": "1.3.0",
43 | "cypress": "10.11.0",
44 | "http-server": "14.1.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example-cypress-10/todo-example/public/favicon.ico
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example-cypress-10/todo-example/public/logo192.png
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example-cypress-10/todo-example/public/logo512.png
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/App.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | function App() {
4 | const [todos, setTodos] = useState([])
5 | useEffect(() => {
6 | async function fetchTodos() {
7 | let response = await fetch('/api/todo', {
8 | headers: {
9 | 'x-pactflow': 'blah',
10 | 'ignore-me': 'ignore',
11 | 'ignore-me-globally': 'ignore'
12 | }
13 | })
14 | response = await response.json()
15 | setTodos(response)
16 | }
17 | fetchTodos()
18 | }, [])
19 | return (
20 |
21 | {todos.length === 0 ? (
22 |
No todos is found
23 | ) : (
24 |
25 | {todos.map((todo, i) => {
26 | return - {todo.content}
27 | })}
28 |
29 | )}
30 |
31 | )
32 | }
33 |
34 | export default App
35 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/example-cypress-10/todo-example/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/example/todo-example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/example/todo-example/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/example/todo-example/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "video": false,
3 | "env": {
4 | "headersBlocklist": ["ignore-me-globally"],
5 | "ignoreDefaultBlocklist": false
6 | }
7 | }
--------------------------------------------------------------------------------
/example/todo-example/cypress/fixtures/todo.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "content": "clean desk"
4 | },
5 | {
6 | "content": "make coffee"
7 | }
8 | ]
--------------------------------------------------------------------------------
/example/todo-example/cypress/integration/todo.spec.js:
--------------------------------------------------------------------------------
1 | import '../../../../dist/index'
2 | import todosResponse from '../fixtures/todo.json'
3 | describe('example to-do app', () => {
4 | before(() => {
5 | cy.setupPact('ui-consumer', 'todo-api')
6 | cy.intercept(
7 | {
8 | method: 'GET',
9 | url: '**/api/todo',
10 | headers: {
11 | 'x-pactflow': 'blah',
12 | 'ignore-me': 'ignore',
13 | 'ignore-me-globally': 'ignore'
14 | }
15 | },
16 | {
17 | statusCode: 200,
18 | body: todosResponse,
19 | headers: { 'access-control-allow-origin': '*' }
20 | }
21 | ).as('getTodos')
22 | cy.setupPactHeaderBlocklist(['ignore-me'])
23 | cy.visit('http://localhost:3000/')
24 | })
25 |
26 | it('shows todo', () => {
27 | cy.contains('clean desk')
28 | })
29 |
30 | after(() => {
31 | cy.usePactWait('getTodos').its('response.statusCode').should('eq', 200)
32 | })
33 | })
--------------------------------------------------------------------------------
/example/todo-example/cypress/integration/todoGet.spec.js:
--------------------------------------------------------------------------------
1 | import '../../../../dist/index'
2 |
3 | describe('example to-do app', () => {
4 | before(() => {
5 | cy.setupPact('ui-consumer', 'todo-api')
6 | cy.usePactRequest(
7 | {
8 | method: 'GET',
9 | url: 'https://jsonplaceholder.typicode.com/todos',
10 | headers: {
11 | 'x-pactflow': 'blah',
12 | 'ignore-me': 'ignore',
13 | 'ignore-me-globally': 'ignore'
14 | }
15 | },
16 | 'getTodosGet'
17 | )
18 | cy.setupPactHeaderBlocklist(['ignore-me'])
19 | })
20 |
21 | it('shows todo', () => {
22 | })
23 |
24 | after(() => {
25 | cy.usePactGet('getTodosGet')
26 | .its('response.statusCode')
27 | .should('eq', 200)
28 | })
29 | })
30 |
--------------------------------------------------------------------------------
/example/todo-example/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | const pactCypressPlugin = require('@pactflow/pact-cypress-adapter/dist/plugin')
16 | const fs = require('fs')
17 |
18 | module.exports = (on, config) => {
19 | pactCypressPlugin(on, config, fs)
20 | }
21 |
--------------------------------------------------------------------------------
/example/todo-example/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/example/todo-example/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 | import '@pactflow/pact-cypress-adapter'
19 |
20 | // Alternatively you can use CommonJS syntax:
21 | // require('./commands')
22 |
--------------------------------------------------------------------------------
/example/todo-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^5.0.0"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject",
19 | "cypress:open": "cypress open",
20 | "cypress:run": "cypress run --headless --browser chrome",
21 | "http-server": "http-server -p 3000 build/ &"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "devDependencies": {
42 | "@pactflow/pact-cypress-adapter": "1.3.0",
43 | "cypress": "9.7.0",
44 | "http-server": "14.1.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/todo-example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example/todo-example/public/favicon.ico
--------------------------------------------------------------------------------
/example/todo-example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/example/todo-example/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example/todo-example/public/logo192.png
--------------------------------------------------------------------------------
/example/todo-example/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pactflow/pact-cypress-adapter/d13dc9f66f2c02fb9bb3cdc79c1d5db0fd0b28c1/example/todo-example/public/logo512.png
--------------------------------------------------------------------------------
/example/todo-example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/example/todo-example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/example/todo-example/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/todo-example/src/App.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | function App() {
4 | const [todos, setTodos] = useState([])
5 | useEffect(() => {
6 | async function fetchTodos() {
7 | let response = await fetch('/api/todo', {
8 | headers: {
9 | 'x-pactflow': 'blah',
10 | 'ignore-me': 'ignore',
11 | 'ignore-me-globally': 'ignore'
12 | }
13 | })
14 | response = await response.json()
15 | setTodos(response)
16 | }
17 | fetchTodos()
18 | }, [])
19 | return (
20 |
21 | {todos.length === 0 ? (
22 |
No todos is found
23 | ) : (
24 |
25 | {todos.map((todo, i) => {
26 | return - {todo.content}
27 | })}
28 |
29 | )}
30 |
31 | )
32 | }
33 |
34 | export default App
35 |
--------------------------------------------------------------------------------
/example/todo-example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/example/todo-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/example/todo-example/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/todo-example/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/example/todo-example/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pactflow/pact-cypress-adapter",
3 | "version": "1.3.0",
4 | "description": "A cypress adapter for pact",
5 | "keywords": [
6 | "pact",
7 | "cypress",
8 | "contract testing",
9 | "ui"
10 | ],
11 | "main": "./dist/index.js",
12 | "types": "./dist/index.d.ts",
13 | "files": [
14 | "dist/*.js",
15 | "dist/**/*.js",
16 | "dist/index.d.ts"
17 | ],
18 | "scripts": {
19 | "test": "jest --testPathPattern=test",
20 | "build": "tsc",
21 | "test:example": "cd example/todo-example && npm i && npm run build && npm run http-server && npm run cypress:run",
22 | "release": "standard-version"
23 | },
24 | "author": "",
25 | "license": "MIT",
26 | "devDependencies": {
27 | "@types/jest": "27.5.2",
28 | "@typescript-eslint/eslint-plugin": "8.32.1",
29 | "cypress": "9.7.0",
30 | "eslint": "8.57.1",
31 | "jest": "27.5.1",
32 | "standard-version": "9.5.0",
33 | "ts-jest": "27.1.5",
34 | "typescript": "4.9.5"
35 | },
36 | "dependencies": {
37 | "@types/node": "^22.0.0",
38 | "@types/lodash": "^4.14.178",
39 | "lodash": "^4.17.21"
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "git+https://github.com/pactflow/cypress-pact-adapter.git"
44 | },
45 | "bugs": {
46 | "url": "https://github.com/pactflow/cypress-pact-adapter/issues"
47 | },
48 | "homepage": "https://github.com/pactflow/cypress-pact-adapter#readme"
49 | }
50 |
--------------------------------------------------------------------------------
/scripts/ci/build-and-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eu
3 |
4 | echo "=====Install dependency====="
5 | npm i
6 | echo "=====Run unit test====="
7 | npm run test
8 | echo "=====Build project====="
9 | npm run build
10 | echo "=====Run cypress example====="
11 | npm run test:example
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const AUTOGEN_HEADER_BLOCKLIST = [
2 | 'access-control-expose-headers',
3 | 'access-control-allow-credentials',
4 | 'host',
5 | 'proxy-connection',
6 | 'sec-ch-ua',
7 | 'sec-ch-ua-mobile',
8 | 'user-agent',
9 | 'sec-ch-ua-platform',
10 | 'origin',
11 | 'sec-fetch-site',
12 | 'sec-fetch-mode',
13 | 'sec-fetch-dest',
14 | 'referer',
15 | 'accept-encoding',
16 | 'accept-language',
17 | 'date',
18 | 'x-powered-by'
19 | ]
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { AUTOGEN_HEADER_BLOCKLIST } from './constants'
2 | import { AliasType, AnyObject, PactConfigType, XHRRequestAndResponse, RequestOptionType } from 'types'
3 | import { formatAlias, writePact } from './utils'
4 | import { env } from 'process'
5 |
6 | declare global {
7 | // eslint-disable-next-line @typescript-eslint/no-namespace
8 | namespace Cypress {
9 | interface Chainable {
10 | usePactWait: (alias: AliasType) => Chainable
11 | usePactRequest: (option: AnyObject, alias: string) => Chainable
12 | usePactGet: (alias: string, pactConfig: PactConfigType) => Chainable
13 | setupPact: (consumerName: string, providerName: string) => Chainable
14 | setupPactHeaderBlocklist: (headers: string[]) => Chainable
15 | }
16 | }
17 | }
18 |
19 | const pactConfig: PactConfigType = {
20 | consumerName: 'customer',
21 | providerName: 'provider'
22 | }
23 |
24 | const setupPact = (consumerName: string, providerName: string) => {
25 | pactConfig['consumerName'] = consumerName
26 | pactConfig['providerName'] = providerName
27 | }
28 |
29 | const ignoreDefaultBlocklist = Cypress.env('ignoreDefaultBlocklist') || false
30 | const globalBlocklist = Cypress.env('headersBlocklist') || []
31 | let headersBlocklist: string[] = ignoreDefaultBlocklist
32 | ? globalBlocklist
33 | : [...globalBlocklist, ...AUTOGEN_HEADER_BLOCKLIST]
34 |
35 | const setupPactHeaderBlocklist = (headers: string[]) => {
36 | headersBlocklist = [...headers, ...headersBlocklist]
37 | }
38 |
39 | const usePactWait = (alias: AliasType) => {
40 | const formattedAlias = formatAlias(alias)
41 | // Cypress versions older than 8.2 do not have a currentTest objects
42 | const testCaseTitle = Cypress.currentTest ? Cypress.currentTest.title : ''
43 | //NOTE: spread only works for array containing more than one item
44 | if (formattedAlias.length > 1) {
45 | cy.wait([...formattedAlias]).spread((...intercepts) => {
46 | intercepts.forEach((intercept, index) => {
47 | writePact({
48 | intercept,
49 | testCaseTitle: `${testCaseTitle}-${formattedAlias[index]}`,
50 | pactConfig,
51 | blocklist: headersBlocklist
52 | })
53 | })
54 | })
55 | } else {
56 | cy.wait(formattedAlias).then((intercept) => {
57 | const flattenIntercept = Array.isArray(intercept) ? intercept[0] : intercept
58 | writePact({
59 | intercept: flattenIntercept,
60 | testCaseTitle: `${testCaseTitle}`,
61 | pactConfig,
62 | blocklist: headersBlocklist
63 | })
64 | })
65 | }
66 | }
67 |
68 | const requestDataMap: AnyObject = {}
69 |
70 | const usePactGet = (alias: string) => {
71 | const formattedAlias = formatAlias(alias)
72 | // Cypress versions older than 8.2 do not have a currentTest objects
73 | const testCaseTitle = Cypress.currentTest ? Cypress.currentTest.title : ''
74 |
75 | formattedAlias.forEach((alias) => {
76 | cy.get(alias).then((response: any) => {
77 | const fullRequestAndResponse = {
78 | request: {
79 | method: requestDataMap[alias].method,
80 | url: requestDataMap[alias].url,
81 | headers: response.requestHeaders,
82 | body: response.requestBody
83 | },
84 | response: {
85 | body: response.body,
86 | statusCode: response.status,
87 | headers: response.headers,
88 | statusText: response.statusText
89 | }
90 | } as XHRRequestAndResponse
91 | writePact({
92 | intercept: fullRequestAndResponse,
93 | testCaseTitle: `${testCaseTitle}-${alias}`,
94 | pactConfig,
95 | blocklist: headersBlocklist
96 | })
97 | })
98 | })
99 | }
100 |
101 | const usePactRequest = (option: Partial, alias: string) => {
102 | cy.request(option).as(alias)
103 | // Store request url and method to a global item as cy.request.get() doesn't
104 | // provide related information
105 | requestDataMap[`@${alias}`] = option
106 | }
107 |
108 | Cypress.Commands.add('usePactWait', usePactWait)
109 | Cypress.Commands.add('usePactRequest', usePactRequest)
110 | Cypress.Commands.add('usePactGet', usePactGet)
111 | Cypress.Commands.add('setupPact', setupPact)
112 | Cypress.Commands.add('setupPactHeaderBlocklist', setupPactHeaderBlocklist)
113 |
--------------------------------------------------------------------------------
/src/plugin.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {Cypress.PluginConfig}
3 | */
4 |
5 | // eslint-disable-next-line no-unused-vars
6 | const { readFileAsync } = require('./utils')
7 | module.exports = (on: any, config: any, fs: any) => {
8 | const readFile = (filename: string) => readFileAsync(fs.promises, filename)
9 | const removePactDir = () => {
10 | fs.promises.rm('cypress/pacts', { recursive: true, force: true }).then(() => {
11 | console.log('Clear up pacts')
12 | })
13 | }
14 | on('before:run', () => {
15 | removePactDir()
16 | })
17 | on('task', {
18 | readFile
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { Interception } from 'cypress/types/net-stubbing'
2 |
3 | export type AliasType = string | string[]
4 |
5 | export type AnyObject = {
6 | [K in string | number]: any
7 | }
8 |
9 | export type PactConfigType = {
10 | consumerName: string
11 | providerName: string
12 | }
13 |
14 | export type HeaderType = Record | undefined
15 |
16 | type BaseXHR = {
17 | headers: HeaderType
18 | body: any | undefined
19 | }
20 | export type Interaction = {
21 | description: string
22 | providerState: string
23 | request: {
24 | method: string
25 | path: string
26 | query: string
27 | } & BaseXHR
28 | response: {
29 | status: string | number | undefined
30 | } & BaseXHR
31 | }
32 |
33 | export type XHRRequestAndResponse = {
34 | request:
35 | | {
36 | method: string
37 | url: string
38 | } & BaseXHR
39 | response: {
40 | statusCode: string | number | undefined
41 | statusText: string | undefined
42 | } & BaseXHR
43 | }
44 |
45 | type Encodings =
46 | | 'ascii'
47 | | 'base64'
48 | | 'binary'
49 | | 'hex'
50 | | 'latin1'
51 | | 'utf8'
52 | | 'utf-8'
53 | | 'ucs2'
54 | | 'ucs-2'
55 | | 'utf16le'
56 | | 'utf-16le'
57 | | null
58 |
59 | export type RequestOptionType = {
60 | auth: object
61 | body: AnyObject
62 | encoding: Encodings
63 | followRedirect: boolean
64 | form: boolean
65 | gzip: boolean
66 | headers: object
67 | method: string
68 | qs: object
69 | url: string
70 | }
71 |
72 | export type PactFileType = {
73 | intercept: Interception | XHRRequestAndResponse
74 | testCaseTitle: string
75 | pactConfig: PactConfigType
76 | blocklist?: string[],
77 | content?: any
78 | }
79 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { Interception } from 'cypress/types/net-stubbing'
2 | import { uniqBy, reverse, omit } from 'lodash'
3 | import { AliasType, Interaction, PactConfigType, XHRRequestAndResponse, PactFileType, HeaderType } from 'types'
4 | const pjson = require('../package.json')
5 | export const formatAlias = (alias: AliasType) => {
6 | if (Array.isArray(alias)) {
7 | return [...alias].map((a) => `@${a}`)
8 | }
9 | return [`@${alias}`]
10 | }
11 |
12 | const constructFilePath = ({ consumerName, providerName }: PactConfigType) =>
13 | `cypress/pacts/${providerName}-${consumerName}.json`
14 |
15 | export const writePact = ({ intercept, testCaseTitle, pactConfig, blocklist }: PactFileType) => {
16 | const filePath = constructFilePath(pactConfig)
17 | cy.task('readFile', filePath)
18 | .then((content) => {
19 | if (content) {
20 | const parsedContent = JSON.parse(content as string)
21 | return constructPactFile({ intercept, testCaseTitle, pactConfig, blocklist, content: parsedContent })
22 | } else {
23 | return constructPactFile({ intercept, testCaseTitle, pactConfig, blocklist })
24 | }
25 | })
26 | .then((data) => {
27 | cy.writeFile(filePath, JSON.stringify(data))
28 | })
29 | .then(() => {
30 | return intercept
31 | })
32 | }
33 |
34 | export const omitHeaders = (headers: HeaderType, blocklist: string[]) => {
35 | return omit(headers, [...blocklist])
36 | }
37 |
38 | const constructInteraction = (
39 | intercept: Interception | XHRRequestAndResponse,
40 | testTitle: string,
41 | blocklist: string[]
42 | ): Interaction => {
43 | const path = new URL(intercept.request.url).pathname
44 | const search = new URL(intercept.request.url).search
45 | const query = new URLSearchParams(search).toString()
46 | return {
47 | description: testTitle,
48 | providerState: '',
49 | request: {
50 | method: intercept.request.method,
51 | path: path,
52 | headers: omitHeaders(intercept.request.headers, blocklist),
53 | body: intercept.request.body,
54 | query: query
55 | },
56 | response: {
57 | status: intercept.response?.statusCode,
58 | headers: omitHeaders(intercept.response?.headers, blocklist),
59 | body: intercept.response?.body
60 | }
61 | }
62 | }
63 | export const constructPactFile = ({ intercept, testCaseTitle, pactConfig, blocklist = [], content }: PactFileType) => {
64 | const pactSkeletonObject = {
65 | consumer: { name: pactConfig.consumerName },
66 | provider: { name: pactConfig.providerName },
67 | interactions: [],
68 | metadata: {
69 | pactSpecification: {
70 | version: '2.0.0'
71 | },
72 | client: {
73 | name: 'pact-cypress-adapter',
74 | version: pjson.version
75 | }
76 | }
77 | }
78 |
79 | if (content) {
80 | const interactions = [...content.interactions, constructInteraction(intercept, testCaseTitle, blocklist)]
81 | const nonDuplicatesInteractions = reverse(uniqBy(reverse(interactions), 'description'))
82 | const data = {
83 | ...pactSkeletonObject,
84 | ...content,
85 | interactions: nonDuplicatesInteractions
86 | }
87 | return data
88 | }
89 |
90 | return {
91 | ...pactSkeletonObject,
92 | interactions: [...pactSkeletonObject.interactions, constructInteraction(intercept, testCaseTitle, blocklist)]
93 | }
94 | }
95 |
96 | const isFileExisted = async (fs: any, filename: string) => !!(await fs.stat(filename).catch((e: any) => false))
97 | export const readFileAsync = async (fs: any, filename: string) => {
98 | if (await isFileExisted(fs, filename)) {
99 | const data = await fs.readFile(filename, 'utf8')
100 | return data
101 | }
102 | return null
103 | }
104 |
--------------------------------------------------------------------------------
/test/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { formatAlias, constructPactFile, readFileAsync, omitHeaders } from '../src/utils'
2 | import { expect } from '@jest/globals'
3 | import { XHRRequestAndResponse } from '../src/types'
4 | const pjson = require('../package.json')
5 |
6 | import { promises, Stats } from 'fs'
7 |
8 | describe('formatAlias', () => {
9 | it('should format array of string in alias format', () => {
10 | expect(formatAlias(['a', 'b'])).toEqual(['@a', '@b'])
11 | })
12 |
13 | it('should format single string to a formatted array', () => {
14 | expect(formatAlias('a')).toEqual(['@a'])
15 | })
16 | })
17 |
18 | describe('constructPactFile', () => {
19 | it('should append intercept to the existing content', () => {
20 | const existingContent = {
21 | consumer: {
22 | name: 'ui-consumer'
23 | },
24 | provider: {
25 | name: 'todo-api'
26 | },
27 | interactions: [
28 | {
29 | description: 'shows todo',
30 | providerState: '',
31 | request: {
32 | method: 'GET',
33 | path: '/api/todo',
34 | body: '',
35 | query: ''
36 | },
37 | response: {
38 | status: 200,
39 | headers: {
40 | 'access-control-allow-origin': '*',
41 | 'content-type': 'application/json',
42 | 'access-control-expose-headers': '*',
43 | 'access-control-allow-credentials': 'true'
44 | },
45 | body: [
46 | {
47 | content: 'clean desk'
48 | },
49 | {
50 | content: 'make coffee'
51 | }
52 | ]
53 | }
54 | }
55 | ],
56 | metadata: {
57 | pactSpecification: {
58 | version: '2.0.0'
59 | },
60 | client: {
61 | name: 'pact-cypress-adapter',
62 | version: pjson.version
63 | }
64 | }
65 | }
66 | const newIntercept = {
67 | request: {
68 | method: 'POST',
69 | url: 'https://localhost:3000/create',
70 | body: 'hello'
71 | },
72 | response: {
73 | statusCode: 201,
74 | statusText: 'Created'
75 | }
76 | } as XHRRequestAndResponse
77 | const result = constructPactFile({
78 | intercept: newIntercept,
79 | testCaseTitle: 'create todo',
80 | pactConfig: {
81 | consumerName: 'ui-consumer',
82 | providerName: 'todo-api'
83 | },
84 | blocklist: [],
85 | content: existingContent
86 | })
87 | expect(result.interactions.length).toBe(2)
88 | expect(result.interactions[1].description).toBe('create todo')
89 | })
90 |
91 | it('should create a new file when no pact file is found', () => {
92 | const newIntercept = {
93 | request: {
94 | method: 'POST',
95 | url: 'https://localhost:3000/create',
96 | body: 'hello'
97 | },
98 | response: {
99 | statusCode: 201,
100 | statusText: 'Created'
101 | }
102 | } as XHRRequestAndResponse
103 | const result = constructPactFile({
104 | intercept: newIntercept,
105 | testCaseTitle: 'create todo',
106 | pactConfig: {
107 | consumerName: 'ui-consumer',
108 | providerName: 'todo-api'
109 | }
110 | })
111 | expect(result.consumer.name).toBe('ui-consumer')
112 | expect(result.provider.name).toBe('todo-api')
113 | expect(result.interactions.length).toBe(1)
114 | })
115 | })
116 |
117 | describe('readFile', () => {
118 | it('should return null when no file is found', async () => {
119 | const mock = jest.spyOn(promises, 'stat')
120 | mock.mockReturnValue(
121 | new Promise((resolve, reject) => {
122 | reject()
123 | })
124 | )
125 | const fileContent = await readFileAsync(promises, 'hello')
126 | expect(fileContent).toBeNull()
127 | })
128 |
129 | it('should return file content', async () => {
130 | const statMock = jest.spyOn(promises, 'stat')
131 | statMock.mockReturnValue(
132 | new Promise((resolve) => {
133 | resolve({} as Stats)
134 | })
135 | )
136 |
137 | const readFileMock = jest.spyOn(promises, 'readFile')
138 | readFileMock.mockReturnValue(
139 | new Promise((resolve) => {
140 | resolve('hello')
141 | })
142 | )
143 | const fileContent = await readFileAsync(promises, 'hello')
144 | expect(fileContent).toBe('hello')
145 | })
146 | })
147 |
148 | describe('omitHeaders', () => {
149 | it('should omit auto-generated headers and header from customised blocklist', () => {
150 | const result = omitHeaders(
151 | {
152 | referer: 'me',
153 | 'x-pactflow': 'lol',
154 | 'ignore-me': 'ignore'
155 | },
156 | ['ignore-me', 'referer']
157 | )
158 | expect(result).toStrictEqual({ 'x-pactflow': 'lol' })
159 | })
160 | })
161 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "noLib": false,
8 | "noImplicitReturns": true,
9 | "noImplicitAny": true,
10 | "noImplicitThis": true,
11 | "resolveJsonModule": true,
12 | "strictNullChecks": true,
13 | "moduleResolution": "node",
14 | "noEmitOnError": true,
15 | "emitDecoratorMetadata": true,
16 | "declaration": true,
17 | "experimentalDecorators": true,
18 | "target": "es5",
19 | "lib": ["es5", "dom"],
20 | "types": ["node", "cypress", "jest"],
21 | "skipLibCheck": true
22 | },
23 | "include": ["src"],
24 | "exclude": ["./node_modules/**", "dist", "examples"]
25 | }
26 |
--------------------------------------------------------------------------------