├── .commitlintrc.js
├── .eslintignore
├── .eslintrc.js
├── .github
├── issue_template.md
└── pull_request_template.md
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .mocharc.js
├── .prettierrc.js
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── OSSMETADATA
├── README.md
├── docs
├── .nojekyll
├── _coverpage.md
├── _sidebar.md
├── adapters
│ ├── custom.md
│ ├── fetch.md
│ ├── node-http.md
│ ├── playwright.md
│ ├── puppeteer.md
│ └── xhr.md
├── api.md
├── assets
│ ├── images
│ │ ├── Netflix_Logo.png
│ │ ├── favicon.ico
│ │ ├── logo.png
│ │ ├── wordmark-logo-alt-twitter.png
│ │ ├── wordmark-logo-alt.png
│ │ └── wordmark-logo.png
│ └── styles.css
├── cli
│ ├── commands.md
│ └── overview.md
├── configuration.md
├── examples.md
├── frameworks
│ └── ember-cli.md
├── index.html
├── node-server
│ ├── express-integrations.md
│ └── overview.md
├── persisters
│ ├── custom.md
│ ├── fs.md
│ ├── local-storage.md
│ └── rest.md
├── quick-start.md
├── server
│ ├── api.md
│ ├── event.md
│ ├── events-and-middleware.md
│ ├── overview.md
│ ├── request.md
│ ├── response.md
│ └── route-handler.md
└── test-frameworks
│ ├── jest-jasmine.md
│ ├── mocha.md
│ └── qunit.md
├── examples
├── .eslintrc.js
├── client-server
│ ├── index.html
│ ├── package.json
│ └── tests
│ │ ├── events.test.js
│ │ ├── intercept.test.js
│ │ └── setup.js
├── dummy-app
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── posts.js
│ │ ├── todos.js
│ │ └── users.js
├── jest-node-fetch
│ ├── .eslintrc.js
│ ├── __recordings__
│ │ └── jest-node-fetch_1142061259
│ │ │ ├── posts_1278140380
│ │ │ └── should-return-post_148615714
│ │ │ │ └── recording.har
│ │ │ └── users_1585235219
│ │ │ └── should-return-user_4259424139
│ │ │ └── recording.har
│ ├── __tests__
│ │ └── index.test.js
│ ├── package.json
│ └── src
│ │ ├── index.js
│ │ ├── posts.js
│ │ └── users.js
├── jest-puppeteer
│ ├── .eslintrc.js
│ ├── __recordings__
│ │ └── jest-puppeteer_2726822272
│ │ │ └── should-be-able-to-navigate-to-all-routes_1130491217
│ │ │ └── recording.har
│ ├── __tests__
│ │ └── dummy-app.test.js
│ ├── jest-puppeteer.config.js
│ ├── jest.config.js
│ └── package.json
├── node-fetch
│ ├── package.json
│ ├── recordings
│ │ └── node-fetch_2851505768
│ │ │ └── should-work_3457346403
│ │ │ └── recording.har
│ └── tests
│ │ └── node-fetch.test.js
├── puppeteer
│ ├── index.js
│ ├── package.json
│ └── recordings
│ │ └── puppeteer_2155046665
│ │ └── recording.har
├── rest-persister
│ ├── index.html
│ ├── package.json
│ ├── recordings
│ │ └── REST-Persister_2289553200
│ │ │ └── should-work_3457346403
│ │ │ └── recording.har
│ └── tests
│ │ ├── rest-persister.test.js
│ │ └── setup.js
└── typescript-jest-node-fetch
│ ├── __recordings__
│ └── github-api-client_2139812550
│ │ └── getUser_1648904580
│ │ └── recording.har
│ ├── jest.config.ts
│ ├── package.json
│ ├── src
│ ├── github-api.test.ts
│ ├── github-api.ts
│ └── utils
│ │ └── auto-setup-polly.ts
│ └── tsconfig.json
├── jest.config.js
├── lerna.json
├── package.json
├── packages
└── @pollyjs
│ ├── adapter-fetch
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── index.js
│ │ └── utils
│ │ │ └── serializer-headers.js
│ ├── tests
│ │ ├── integration
│ │ │ ├── adapter-test.js
│ │ │ ├── persister-local-storage-test.js
│ │ │ ├── persister-rest-test.js
│ │ │ └── server-test.js
│ │ └── utils
│ │ │ └── polly-config.js
│ └── types.d.ts
│ ├── adapter-node-http
│ ├── .eslintrc.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.js
│ ├── rollup.config.shared.js
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── index.js
│ │ └── utils
│ │ │ ├── get-url-from-options.js
│ │ │ ├── merge-chunks.js
│ │ │ └── url-to-options.js
│ ├── tests
│ │ ├── integration
│ │ │ ├── adapter-node-fetch-test.js
│ │ │ ├── adapter-test.js
│ │ │ └── persister-fs-test.js
│ │ ├── jest
│ │ │ └── integration
│ │ │ │ ├── fetch-test.js
│ │ │ │ └── xhr-test.js
│ │ ├── unit
│ │ │ └── utils
│ │ │ │ └── merge-chunks-test.js
│ │ └── utils
│ │ │ ├── get-buffer-from-stream.js
│ │ │ ├── get-response-from-request.js
│ │ │ ├── native-request.js
│ │ │ └── polly-config.js
│ └── types.d.ts
│ ├── adapter-puppeteer
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.js
│ ├── rollup.config.test.js
│ ├── src
│ │ └── index.js
│ ├── tests
│ │ ├── helpers
│ │ │ └── fetch.js
│ │ ├── integration
│ │ │ └── adapter-test.js
│ │ └── unit
│ │ │ └── adapter-test.js
│ └── types.d.ts
│ ├── adapter-xhr
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── index.js
│ │ └── utils
│ │ │ ├── resolve-xhr.js
│ │ │ └── serialize-response-headers.js
│ ├── tests
│ │ ├── integration
│ │ │ └── adapter-test.js
│ │ └── utils
│ │ │ └── xhr-request.js
│ └── types.d.ts
│ ├── adapter
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── index.js
│ │ └── utils
│ │ │ ├── dehumanize-time.js
│ │ │ ├── is-expired.js
│ │ │ ├── normalize-recorded-response.js
│ │ │ └── stringify-request.js
│ ├── tests
│ │ └── unit
│ │ │ ├── adapter-test.js
│ │ │ └── utils
│ │ │ ├── dehumanize-time-test.js
│ │ │ └── is-expired-test.js
│ └── types.d.ts
│ ├── cli
│ ├── .eslintrc.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── bin
│ │ └── cli.js
│ └── package.json
│ ├── core
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── -private
│ │ │ ├── container.js
│ │ │ ├── event-emitter.js
│ │ │ ├── event.js
│ │ │ ├── http-base.js
│ │ │ ├── interceptor.js
│ │ │ ├── logger.js
│ │ │ ├── request.js
│ │ │ └── response.js
│ │ ├── defaults
│ │ │ └── config.js
│ │ ├── index.js
│ │ ├── polly.js
│ │ ├── server
│ │ │ ├── handler.js
│ │ │ ├── index.js
│ │ │ ├── middleware.js
│ │ │ └── route.js
│ │ ├── test-helpers
│ │ │ ├── lib.js
│ │ │ ├── mocha.js
│ │ │ └── qunit.js
│ │ └── utils
│ │ │ ├── cancel-fn-after-n-times.js
│ │ │ ├── deferred-promise.js
│ │ │ ├── guid-for-recording.js
│ │ │ ├── http-headers.js
│ │ │ ├── merge-configs.js
│ │ │ ├── normalize-request.js
│ │ │ ├── parse-url.js
│ │ │ ├── remove-host-from-url.js
│ │ │ ├── timing.js
│ │ │ └── validators.js
│ ├── tests
│ │ └── unit
│ │ │ ├── -private
│ │ │ ├── container-test.js
│ │ │ ├── event-emitter-test.js
│ │ │ ├── event-test.js
│ │ │ ├── http-base-test.js
│ │ │ ├── interceptor-test.js
│ │ │ └── response-test.js
│ │ │ ├── index-test.js
│ │ │ ├── polly-test.js
│ │ │ ├── server
│ │ │ ├── handler-test.js
│ │ │ └── server-test.js
│ │ │ ├── test-helpers
│ │ │ └── mocha-test.js
│ │ │ └── utils
│ │ │ ├── deferred-promise-test.js
│ │ │ ├── guid-for-recording-test.js
│ │ │ ├── http-headers-test.js
│ │ │ ├── merge-configs-test.js
│ │ │ ├── normalize-request-test.js
│ │ │ ├── parse-url-test.js
│ │ │ ├── remove-host-from-url-test.js
│ │ │ └── timing-test.js
│ └── types.d.ts
│ ├── ember
│ ├── .editorconfig
│ ├── .ember-cli
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .npmignore
│ ├── .prettierignore
│ ├── .prettierrc.js
│ ├── .template-lintrc.js
│ ├── .watchmanconfig
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── addon
│ │ └── -private
│ │ │ └── preconfigure.js
│ ├── blueprints
│ │ └── @pollyjs
│ │ │ └── ember
│ │ │ ├── files
│ │ │ └── config
│ │ │ │ └── polly.js
│ │ │ └── index.js
│ ├── config
│ │ ├── ember-try.js
│ │ ├── environment.js
│ │ └── polly.js
│ ├── ember-cli-build.js
│ ├── index.js
│ ├── package.json
│ ├── testem.js
│ ├── tests
│ │ ├── dummy
│ │ │ ├── app
│ │ │ │ ├── app.js
│ │ │ │ ├── components
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── controllers
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── helpers
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── index.html
│ │ │ │ ├── models
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── router.js
│ │ │ │ ├── routes
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── styles
│ │ │ │ │ └── app.css
│ │ │ │ └── templates
│ │ │ │ │ └── application.hbs
│ │ │ ├── config
│ │ │ │ ├── ember-cli-update.json
│ │ │ │ ├── environment.js
│ │ │ │ ├── optional-features.json
│ │ │ │ └── targets.js
│ │ │ └── public
│ │ │ │ └── robots.txt
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── index.html
│ │ ├── integration
│ │ │ └── .gitkeep
│ │ ├── test-helper.js
│ │ └── unit
│ │ │ └── polly-test.js
│ └── vendor
│ │ └── .gitkeep
│ ├── node-server
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ ├── api.js
│ │ ├── config.js
│ │ ├── express
│ │ │ └── register-api.js
│ │ ├── index.js
│ │ └── server.js
│ └── types.d.ts
│ ├── persister-fs
│ ├── .eslintrc.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.js
│ ├── rollup.config.test.js
│ ├── src
│ │ └── index.js
│ ├── tests
│ │ └── unit
│ │ │ └── persister-test.js
│ └── types.d.ts
│ ├── persister-in-memory
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ └── index.js
│ └── types.d.ts
│ ├── persister-local-storage
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ └── index.js
│ └── types.d.ts
│ ├── persister-rest
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── ajax.js
│ │ └── index.js
│ └── types.d.ts
│ ├── persister
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ │ ├── har
│ │ │ ├── entry.js
│ │ │ ├── index.js
│ │ │ ├── log.js
│ │ │ ├── request.js
│ │ │ ├── response.js
│ │ │ └── utils
│ │ │ │ ├── get-first-header.js
│ │ │ │ └── to-nv-pairs.js
│ │ └── index.js
│ ├── tests
│ │ └── unit
│ │ │ ├── har-test.js
│ │ │ └── persister-test.js
│ └── types.d.ts
│ └── utils
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.test.js
│ ├── src
│ ├── constants
│ │ ├── actions.js
│ │ ├── expiry-strategies.js
│ │ ├── http-methods.js
│ │ ├── http-status-codes.js
│ │ └── modes.js
│ ├── index.js
│ └── utils
│ │ ├── assert.js
│ │ ├── build-url.js
│ │ ├── clone-arraybuffer.js
│ │ ├── is-buffer-utf8-representable.js
│ │ ├── polly-error.js
│ │ ├── serializers
│ │ ├── blob.js
│ │ ├── buffer.js
│ │ ├── form-data.js
│ │ └── index.js
│ │ ├── timeout.js
│ │ ├── timestamp.js
│ │ └── url.js
│ ├── tests
│ ├── browser
│ │ └── unit
│ │ │ └── utils
│ │ │ └── serializers
│ │ │ ├── blob.js
│ │ │ └── form-data.js
│ ├── node
│ │ └── unit
│ │ │ └── utils
│ │ │ └── serializers
│ │ │ └── buffer.js
│ ├── serializer-tests.js
│ └── unit
│ │ └── utils
│ │ ├── assert-test.js
│ │ ├── build-url-test.js
│ │ ├── polly-error-test.js
│ │ ├── timeout-test.js
│ │ ├── timestamp-test.js
│ │ └── url-test.js
│ └── types.d.ts
├── scripts
├── require-clean-work-tree.sh
├── require-test-build.sh
└── rollup
│ ├── browser.config.js
│ ├── browser.test.config.js
│ ├── default.config.js
│ ├── jest.test.config.js
│ ├── node.config.js
│ ├── node.test.config.js
│ └── utils.js
├── testem.js
├── tests
├── assets
│ └── 32x32.png
├── helpers
│ ├── file.js
│ ├── global-node-fetch.js
│ ├── setup-fetch-record.js
│ └── setup-persister.js
├── index.mustache
├── integration
│ ├── adapter-browser-tests.js
│ ├── adapter-identifier-tests.js
│ ├── adapter-node-tests.js
│ ├── adapter-polly-tests.js
│ ├── adapter-tests.js
│ └── persister-tests.js
├── middleware.js
└── node-setup.js
└── yarn.lock
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | module.exports = {
4 | extends: [
5 | '@commitlint/config-lerna-scopes',
6 | '@commitlint/config-conventional'
7 | ],
8 | rules: {
9 | 'subject-case': [2, 'always', ['sentence-case']]
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /packages/@pollyjs/ember/tests/**/index.html
2 | CHANGELOG.md
3 | package.json
4 | node_modules
5 | tmp
6 | build
7 | dist
8 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Motivation and Context
8 |
9 |
14 |
15 | ## Types of Changes
16 |
17 |
20 |
21 | - [ ] Bug fix (non-breaking change which fixes an issue)
22 | - [ ] New feature (non-breaking change which adds functionality)
23 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
24 |
25 | ## Checklist
26 |
27 |
32 |
33 | - [ ] I have added tests to cover my changes.
34 | - [ ] My change requires a change to the documentation.
35 | - [ ] I have updated the documentation accordingly.
36 | - [ ] My code follows the code style of this project.
37 | - [ ] My commits and the title of this PR follow the [Conventional Commits Specification](https://www.conventionalcommits.org).
38 | - [ ] I have read the [contributing guidelines](https://github.com/Netflix/pollyjs/blob/master/CONTRIBUTING.md).
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | package-lock.json
4 | lerna-debug.log
5 | packages/**/dist/
6 | yarn-error.log
7 | tmp
8 | build
9 | dist
10 | *.lerna_backup
11 | .npmrc
12 |
13 | # Test recordings can write be written here if the test job did not
14 | # get a chance to run to completion. The test will cleans these files up afterwards.
15 | /recordings
16 |
17 | # Examples
18 | examples/**/*/yarn.lock
19 |
20 | # IDE
21 | .vscode/
22 | .tool-versions
23 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn commitlint --edit "$1"
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.mocharc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | spec: './packages/@pollyjs/*/build/node/*.js',
3 | ui: 'bdd',
4 | require: 'tests/node-setup.js'
5 | };
6 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | singleQuote: true,
5 | trailingComma: 'none'
6 | };
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '12'
4 | - '14'
5 | - '16'
6 |
7 | addons:
8 | chrome: stable
9 |
10 | cache:
11 | yarn: true
12 |
13 | before_install:
14 | - curl -o- -L https://yarnpkg.com/install.sh | bash
15 | - export PATH=$HOME/.yarn/bin:$PATH
16 |
17 | install:
18 | - yarn install --frozen-lockfile --non-interactive
19 |
20 | script:
21 | - commitlint-travis
22 | - yarn run test:ci
23 | - ./scripts/require-clean-work-tree.sh
24 |
--------------------------------------------------------------------------------
/OSSMETADATA:
--------------------------------------------------------------------------------
1 | osslifecycle=active
2 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | 
6 |
7 | > Record, replay, and stub HTTP interactions.
8 |
9 | - 🚀 Node & Browser Support
10 | - ⚡️️ Simple, Powerful, & Intuitive API
11 | - 💎 First Class Mocha & QUnit Test Helpers
12 | - 🔥 Intercept, Pass-Through, and Attach Events
13 | - 📼 Record to Disk or Local Storage
14 | - ⏱ Slow Down or Speed Up Time
15 |
16 |
20 |
21 | 
22 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - Getting Started
2 |
3 | - [Overview](README.md)
4 | - [Quick Start](quick-start.md)
5 | - [Examples](examples.md)
6 |
7 | - Test Frameworks
8 |
9 | - [Mocha](test-frameworks/mocha.md)
10 | - [QUnit](test-frameworks/qunit.md)
11 | - [Jest & Jasmine](test-frameworks/jest-jasmine.md)
12 |
13 | - Frameworks
14 |
15 | - [Ember CLI](frameworks/ember-cli.md)
16 |
17 | - Adapters
18 |
19 | - [Fetch](adapters/fetch.md)
20 | - [Node HTTP](adapters/node-http.md)
21 | - [Playwright](adapters/playwright.md)
22 | - [Puppeteer](adapters/puppeteer.md)
23 | - [XHR](adapters/xhr.md)
24 | - [Custom](adapters/custom.md)
25 |
26 | - Persisters
27 |
28 | - [File System](persisters/fs.md)
29 | - [Local Storage](persisters/local-storage.md)
30 | - [REST](persisters/rest.md)
31 | - [Custom](persisters/custom.md)
32 |
33 | - Client Server
34 |
35 | - [Overview](server/overview.md)
36 | - [API](server/api.md)
37 | - [Events & Middleware](server/events-and-middleware.md)
38 | - [Route Handler](server/route-handler.md)
39 | - [Request](server/request.md)
40 | - [Response](server/response.md)
41 | - [Event](server/event.md)
42 |
43 | - Node Server
44 |
45 | - [Overview](node-server/overview.md)
46 | - [Express Integrations](node-server/express-integrations.md)
47 |
48 | - CLI
49 |
50 | - [Overview](cli/overview.md)
51 | - [Commands](cli/commands.md)
52 |
53 | - Reference
54 |
55 | - [API](api.md)
56 | - [Configuration](configuration.md)
57 |
58 | - [Contributing](CONTRIBUTING.md)
59 |
--------------------------------------------------------------------------------
/docs/adapters/fetch.md:
--------------------------------------------------------------------------------
1 | # Fetch Adapter
2 |
3 | The fetch adapter wraps the global fetch method for seamless
4 | recording and replaying of requests.
5 |
6 | ## Installation
7 |
8 | _Note that you must have node (and npm) installed._
9 |
10 | ```bash
11 | npm install @pollyjs/adapter-fetch -D
12 | ```
13 |
14 | If you want to install it with [yarn](https://yarnpkg.com):
15 |
16 | ```bash
17 | yarn add @pollyjs/adapter-fetch -D
18 | ```
19 |
20 | ## Usage
21 |
22 | Use the [configure](api#configure), [connectTo](api#connectto), and
23 | [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the
24 | adapter.
25 |
26 | ```js
27 | import { Polly } from '@pollyjs/core';
28 | import FetchAdapter from '@pollyjs/adapter-fetch';
29 |
30 | // Register the fetch adapter so its accessible by all future polly instances
31 | Polly.register(FetchAdapter);
32 |
33 | const polly = new Polly('', {
34 | adapters: ['fetch']
35 | });
36 |
37 | // Disconnect using the `configure` API
38 | polly.configure({ adapters: [] });
39 |
40 | // Reconnect using the `connectTo` API
41 | polly.connectTo('fetch');
42 |
43 | // Disconnect using the `disconnectFrom` API
44 | polly.disconnectFrom('fetch');
45 | ```
46 |
47 | ## Options
48 |
49 | ### context
50 |
51 | _Type_: `Object`
52 | _Default_: `global|self|window`
53 |
54 | The context object which contains the fetch API. Typically this is `window` or `self` in the browser and `global` in node.
55 |
56 | **Example**
57 |
58 | ```js
59 | polly.configure({
60 | adapters: ['fetch'],
61 | adapterOptions: {
62 | fetch: {
63 | context: window
64 | }
65 | }
66 | });
67 | ```
68 |
--------------------------------------------------------------------------------
/docs/adapters/node-http.md:
--------------------------------------------------------------------------------
1 | # Node HTTP Adapter
2 |
3 | The node-http adapter provides a low level nodejs http request adapter that uses [nock](https://github.com/nock/nock) to patch the [http](https://nodejs.org/api/http.html) and [https](https://nodejs.org/api/https.html) modules in nodejs for seamless recording and replaying of requests.
4 |
5 | ## Installation
6 |
7 | _Note that you must have node (and npm) installed._
8 |
9 | ```bash
10 | npm install @pollyjs/adapter-node-http -D
11 | ```
12 |
13 | If you want to install it with [yarn](https://yarnpkg.com):
14 |
15 | ```bash
16 | yarn add @pollyjs/adapter-node-http -D
17 | ```
18 |
19 | ## Usage
20 |
21 | Use the [configure](api#configure), [connectTo](api#connectto), and
22 | [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the
23 | adapter.
24 |
25 | ```js
26 | import { Polly } from '@pollyjs/core';
27 | import NodeHttpAdapter from '@pollyjs/adapter-node-http';
28 |
29 | // Register the node http adapter so its accessible by all future polly instances
30 | Polly.register(NodeHttpAdapter);
31 |
32 | const polly = new Polly('', {
33 | adapters: ['node-http']
34 | });
35 |
36 | // Disconnect using the `configure` API
37 | polly.configure({ adapters: [] });
38 |
39 | // Reconnect using the `connectTo` API
40 | polly.connectTo('node-http');
41 |
42 | // Disconnect using the `disconnectFrom` API
43 | polly.disconnectFrom('node-http');
44 | ```
45 |
--------------------------------------------------------------------------------
/docs/adapters/playwright.md:
--------------------------------------------------------------------------------
1 | # Playwright Adapter
2 |
3 | The 3rd party [Playwright](https://playwright.dev/) adapter is provided by [@gribnoysup](https://github.com/redabacha). Please follow the readme below for installation and usage instructions.
4 |
5 | [README.md](https://raw.githubusercontent.com/redabacha/polly-adapter-playwright/master/README.md ':include :type=markdown')
6 |
--------------------------------------------------------------------------------
/docs/adapters/xhr.md:
--------------------------------------------------------------------------------
1 | # XHR Adapter
2 |
3 | The XHR adapter uses Sinon's [Nise](https://github.com/sinonjs/nise) library
4 | to fake the global `XMLHttpRequest` object while wrapping the native one to allow
5 | for seamless recording and replaying of requests.
6 |
7 | ## Installation
8 |
9 | _Note that you must have node (and npm) installed._
10 |
11 | ```bash
12 | npm install @pollyjs/adapter-xhr -D
13 | ```
14 |
15 | If you want to install it with [yarn](https://yarnpkg.com):
16 |
17 | ```bash
18 | yarn add @pollyjs/adapter-xhr -D
19 | ```
20 |
21 | ## Usage
22 |
23 | Use the [configure](api#configure), [connectTo](api#connectto), and
24 | [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the
25 | adapter.
26 |
27 | ```js
28 | import { Polly } from '@pollyjs/core';
29 | import XHRAdapter from '@pollyjs/adapter-xhr';
30 |
31 | // Register the xhr adapter so its accessible by all future polly instances
32 | Polly.register(XHRAdapter);
33 |
34 | const polly = new Polly('', {
35 | adapters: ['xhr']
36 | });
37 |
38 | // Disconnect using the `configure` API
39 | polly.configure({ adapters: [] });
40 |
41 | // Reconnect using the `connectTo` API
42 | polly.connectTo('xhr');
43 |
44 | // Disconnect using the `disconnectFrom` API
45 | polly.disconnectFrom('xhr');
46 | ```
47 |
48 | ## Options
49 |
50 | ### context
51 |
52 | _Type_: `Object`
53 | _Default_: `global|self|window`
54 |
55 | The context object which contains the XMLHttpRequest object. Typically this is `window` or `self` in the browser and `global` in node.
56 |
57 | **Example**
58 |
59 | ```js
60 | polly.configure({
61 | adapters: ['xhr'],
62 | adapterOptions: {
63 | xhr: {
64 | context: window
65 | }
66 | }
67 | });
68 | ```
69 |
--------------------------------------------------------------------------------
/docs/assets/images/Netflix_Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/Netflix_Logo.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/favicon.ico
--------------------------------------------------------------------------------
/docs/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/logo.png
--------------------------------------------------------------------------------
/docs/assets/images/wordmark-logo-alt-twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/wordmark-logo-alt-twitter.png
--------------------------------------------------------------------------------
/docs/assets/images/wordmark-logo-alt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/wordmark-logo-alt.png
--------------------------------------------------------------------------------
/docs/assets/images/wordmark-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/docs/assets/images/wordmark-logo.png
--------------------------------------------------------------------------------
/docs/cli/commands.md:
--------------------------------------------------------------------------------
1 | # Commands
2 |
3 | As of right now, the Polly CLI only knows one command but expect to see more
4 | in the near future!
5 |
6 | ## listen
7 |
8 | Start up a node server and listen for Polly requests via the
9 | [REST Persister](persisters/rest) to be able to record and replay recordings
10 | to and from disk.
11 |
12 | ### Usage
13 |
14 | ```text
15 | Usage: polly listen|l [options]
16 |
17 | start the server and listen for requests
18 |
19 | Options:
20 |
21 | -H, --host host
22 | -p, --port port number (default: 3000)
23 | -n, --api-namespace api namespace (default: polly)
24 | -d, --recordings-dir recordings directory (default: recordings)
25 | -q, --quiet disable the logging
26 | -h, --help output usage information
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/cli/overview.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | The `@pollyjs/cli` package provides a standalone CLI to quickly get you setup
4 | and ready to go.
5 |
6 | ## Installation
7 |
8 | _Note that you must have node (and npm) installed._
9 |
10 | ```bash
11 | npm install @pollyjs/cli -g
12 | ```
13 |
14 | If you want to install it with [yarn](https://yarnpkg.com):
15 |
16 | ```bash
17 | yarn global add @pollyjs/cli
18 | ```
19 |
20 | ## Usage
21 |
22 | ```text
23 | Usage: polly [options] [command]
24 |
25 | Options:
26 |
27 | -v, --version output the version number
28 | -h, --help output usage information
29 |
30 | Commands:
31 |
32 | listen|l [options] start the server and listen for requests
33 | ```
34 |
--------------------------------------------------------------------------------
/docs/frameworks/ember-cli.md:
--------------------------------------------------------------------------------
1 | # Ember CLI
2 |
3 | Installing the `@pollyjs/ember` addon will import and vendor the necessary
4 | Polly.JS packages as well as register the [Express API](node-server/express-integrations)
5 | required by the [REST Persister](persisters/rest).
6 |
7 | ?> **NOTE:** By default, this addon installs and registers the
8 | [XHR](adapters/xhr) & [Fetch](adapters/fetch) adapters as well as the
9 | [REST](persisters/rest) & [Local Storage](persisters/local-storage) persisters.
10 |
11 | ## Installation
12 |
13 | ```bash
14 | ember install @pollyjs/ember
15 | ```
16 |
17 | ## Configuration
18 |
19 | Addon and [server API configuration](node-server/overview#api-configuration) can be
20 | be specified in `/config/polly.js`. The default configuration options are shown below.
21 |
22 | ```js
23 | module.exports = function(env) {
24 | return {
25 | // Addon Configuration Options
26 | enabled: env !== 'production',
27 |
28 | // Server Configuration Options
29 | server: {
30 | apiNamespace: '/polly',
31 | recordingsDir: 'recordings'
32 | }
33 | };
34 | };
35 | ```
36 |
37 | ## Usage
38 |
39 | Once installed and configured, you can import and use Polly as documented. Check
40 | out the [Quick Start](quick-start#usage) documentation to get started.
41 |
42 | ?> For an even better testing experience, check out the provided
43 | [QUnit Test Helper](test-frameworks/qunit)!
44 |
--------------------------------------------------------------------------------
/docs/node-server/express-integrations.md:
--------------------------------------------------------------------------------
1 | # Express Integrations
2 |
3 | The `@pollyjs/node-server` package exports a `registerExpressAPI` method which
4 | takes in an [Express](http://expressjs.com/) app and a config to register the
5 | necessary routes to be used with the REST Persister.
6 |
7 | ```js
8 | const { registerExpressAPI } = require('@pollyjs/node-server');
9 |
10 | registerExpressAPI(app, config);
11 | ```
12 |
13 | ## Webpack DevServer
14 |
15 | ```js
16 | const path = require('path');
17 | const { registerExpressAPI } = require('@pollyjs/node-server');
18 |
19 | const config = {
20 | devServer: {
21 | before(app) {
22 | registerExpressAPI(app, config);
23 | }
24 | }
25 | };
26 |
27 | module.exports = config;
28 | ```
29 |
30 | ## Ember CLI
31 |
32 | See the [Ember CLI Addon](frameworks/ember-cli) documentation for more details.
33 |
--------------------------------------------------------------------------------
/docs/persisters/fs.md:
--------------------------------------------------------------------------------
1 | # File System Persister
2 |
3 | Read and write recordings to and from the file system.
4 |
5 | ## Installation
6 |
7 | _Note that you must have node (and npm) installed._
8 |
9 | ```bash
10 | npm install @pollyjs/persister-fs -D
11 | ```
12 |
13 | If you want to install it with [yarn](https://yarnpkg.com):
14 |
15 | ```bash
16 | yarn add @pollyjs/persister-fs -D
17 | ```
18 |
19 | ## Usage
20 |
21 | ```js
22 | import { Polly } from '@pollyjs/core';
23 | import FSPersister from '@pollyjs/persister-fs';
24 |
25 | // Register the fs persister so its accessible by all future polly instances
26 | Polly.register(FSPersister);
27 |
28 | new Polly('', {
29 | persister: 'fs'
30 | });
31 | ```
32 |
33 | ## Options
34 |
35 | ### recordingsDir
36 |
37 | _Type_: `String`
38 | _Default_: `'recordings'`
39 |
40 | The root directory to store all recordings. Supports both relative and
41 | absolute paths.
42 |
43 | **Example**
44 |
45 | ```js
46 | polly.configure({
47 | persisterOptions: {
48 | fs: {
49 | recordingsDir: '__recordings__'
50 | }
51 | }
52 | });
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/persisters/local-storage.md:
--------------------------------------------------------------------------------
1 | # Local Storage Persister
2 |
3 | Read and write recordings to and from the browser's Local Storage.
4 |
5 | ## Installation
6 |
7 | _Note that you must have node (and npm) installed._
8 |
9 | ```bash
10 | npm install @pollyjs/persister-local-storage -D
11 | ```
12 |
13 | If you want to install it with [yarn](https://yarnpkg.com):
14 |
15 | ```bash
16 | yarn add @pollyjs/persister-local-storage -D
17 | ```
18 |
19 | ## Usage
20 |
21 | ```js
22 | import { Polly } from '@pollyjs/core';
23 | import LocalStoragePersister from '@pollyjs/persister-local-storage';
24 |
25 | // Register the local-storage persister so its accessible by all future polly instances
26 | Polly.register(LocalStoragePersister);
27 |
28 | new Polly('', {
29 | persister: 'local-storage'
30 | });
31 | ```
32 |
33 | ## Options
34 |
35 | ### context
36 |
37 | _Type_: `Object`
38 | _Default_: `global|self|window`
39 |
40 | The context object which contains the localStorage API.
41 | Typically this is `window` or `self` in the browser and `global` in node.
42 |
43 | **Example**
44 |
45 | ```js
46 | polly.configure({
47 | persisterOptions: {
48 | 'local-storage': {
49 | context: window
50 | }
51 | }
52 | });
53 | ```
54 |
55 | ### key
56 |
57 | _Type_: `String`
58 | _Default_: `'pollyjs'`
59 |
60 | The localStorage key to store the recordings data under.
61 |
62 | **Example**
63 |
64 | ```js
65 | polly.configure({
66 | persisterOptions: {
67 | 'local-storage': {
68 | key: '__pollyjs__'
69 | }
70 | }
71 | });
72 | ```
73 |
--------------------------------------------------------------------------------
/docs/server/event.md:
--------------------------------------------------------------------------------
1 | # Event
2 |
3 | ## Properties
4 |
5 | ### type
6 |
7 | _Type_: `String`
8 |
9 | The event type. (e.g. `request`, `response`, `beforePersist`)
10 |
11 | ## Methods
12 |
13 | ### stopPropagation
14 |
15 | If several event listeners are attached to the same event type, they are called in the order in which they were added. If `stopPropagation` is invoked during one such call, no remaining listeners will be called.
16 |
17 | **Example**
18 |
19 | ```js
20 | server.get('/session/:id').on('beforeResponse', (req, res, event) => {
21 | event.stopPropagation();
22 | res.setHeader('X-SESSION-ID', 'ABC123');
23 | });
24 |
25 | server.get('/session/:id').on('beforeResponse', (req, res, event) => {
26 | // This will never be reached
27 | res.setHeader('X-SESSION-ID', 'XYZ456');
28 | });
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/test-frameworks/jest-jasmine.md:
--------------------------------------------------------------------------------
1 | # Jest & Jasmine
2 |
3 | Due to the nature of the Jest & Jasmine APIs and their restrictions on accessing
4 | the current running test and its parent modules, we've decided to keep this test helper
5 | as a 3rd party library provided by [@gribnoysup](https://github.com/gribnoysup).
6 |
7 | The [setup-polly-jest](https://github.com/gribnoysup/setup-polly-jest) package provides a `setupPolly` utility which will setup a new polly instance for each test as well as stop it once the test has ended.
8 | The Polly instance's recording name is derived from the current test name as well as its
9 | parent module(s).
10 |
11 | [README.md](https://raw.githubusercontent.com/gribnoysup/setup-polly-jest/master/README.md ':include :type=markdown')
12 |
--------------------------------------------------------------------------------
/examples/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | browser: true
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/examples/client-server/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Client Server Tests
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/client-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/client-server-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "test": "http-server -p 3000 -o -c-1 -s"
8 | },
9 | "devDependencies": {
10 | "@pollyjs/adapter-fetch": "*",
11 | "@pollyjs/core": "*",
12 | "@pollyjs/persister-local-storage": "*",
13 | "chai": "*",
14 | "http-server": "*",
15 | "mocha": "*"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/client-server/tests/events.test.js:
--------------------------------------------------------------------------------
1 | /* global setupPolly */
2 |
3 | describe('Events', function () {
4 | setupPolly({
5 | adapters: ['fetch'],
6 | persister: 'local-storage'
7 | });
8 |
9 | it('can help test dynamic data', async function () {
10 | const { server } = this.polly;
11 | let numPosts = 0;
12 |
13 | server
14 | .get('https://jsonplaceholder.typicode.com/posts')
15 | .on('response', (_, res) => {
16 | numPosts = res.jsonBody().length;
17 | });
18 |
19 | const res = await fetch('https://jsonplaceholder.typicode.com/posts');
20 | const posts = await res.json();
21 |
22 | expect(res.status).to.equal(200);
23 | expect(posts.length).to.equal(numPosts);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/examples/client-server/tests/setup.js:
--------------------------------------------------------------------------------
1 | // Expose common globals
2 | window.PollyJS = window['@pollyjs/core'];
3 | window.setupPolly = window.PollyJS.setupMocha;
4 | window.expect = window.chai.expect;
5 |
6 | // Register the fetch adapter and local-storage persister
7 | window.PollyJS.Polly.register(window['@pollyjs/adapter-fetch']);
8 | window.PollyJS.Polly.register(window['@pollyjs/persister-local-storage']);
9 |
10 | // Setup Mocha
11 | mocha.setup({ ui: 'bdd', noHighlighting: true });
12 |
--------------------------------------------------------------------------------
/examples/dummy-app/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['plugin:react/recommended'],
3 | settings: {
4 | react: {
5 | version: '16.5.1'
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/examples/dummy-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/dummy-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/dummy-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "dependencies": {
7 | "prop-types": "^15.6.2",
8 | "ra-data-json-server": "^2.3.1",
9 | "react": "^16.5.1",
10 | "react-admin": "^2.3.1",
11 | "react-dom": "^16.5.1",
12 | "react-scripts": "^1.1.5"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "start:ci": "BROWSER=none CI=true yarn start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/dummy-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/examples/dummy-app/public/favicon.ico
--------------------------------------------------------------------------------
/examples/dummy-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Dummy App
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/examples/dummy-app/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Dummy App",
3 | "name": "Polly.JS Examples Dummy App",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/dummy-app/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Admin, Resource } from 'react-admin';
3 | import jsonServerProvider from 'ra-data-json-server';
4 | import PostIcon from '@material-ui/icons/Book';
5 | import TodoIcon from '@material-ui/icons/ViewList';
6 | import UserIcon from '@material-ui/icons/Group';
7 | import { createMuiTheme } from '@material-ui/core/styles';
8 |
9 | import { UserList, UserShow } from './users';
10 | import { TodoList, TodoShow, TodoEdit, TodoCreate } from './todos';
11 | import { PostList, PostShow, PostEdit, PostCreate } from './posts';
12 |
13 | const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
14 | const theme = createMuiTheme({
15 | palette: {
16 | primary: {
17 | light: '#ff5740',
18 | main: '#e50914',
19 | dark: '#aa0000',
20 | contrastText: '#fff'
21 | },
22 | secondary: {
23 | light: '#ff5740',
24 | main: '#e50914',
25 | dark: '#aa0000',
26 | contrastText: '#fff'
27 | }
28 | }
29 | });
30 |
31 | const App = () => (
32 |
33 |
41 |
49 |
50 |
51 | );
52 |
53 | export default App;
54 |
--------------------------------------------------------------------------------
/examples/dummy-app/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/dummy-app/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import './index.css';
5 | import App from './App';
6 |
7 | ReactDOM.render( , document.getElementById('root'));
8 |
--------------------------------------------------------------------------------
/examples/dummy-app/src/users.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {
4 | List,
5 | Datagrid,
6 | EmailField,
7 | TextField,
8 | Show,
9 | SimpleShowLayout,
10 | ShowButton
11 | } from 'react-admin';
12 |
13 | const UserTitle = ({ record }) => {
14 | return Users - {record ? `${record.name}` : ''} ;
15 | };
16 |
17 | UserTitle.propTypes = {
18 | record: PropTypes.PropTypes.shape({
19 | name: PropTypes.string
20 | })
21 | };
22 |
23 | export const UserList = (props) => (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | export const UserShow = (props) => (
35 | } {...props}>
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const { Polly } = require('@pollyjs/core');
4 | const { setupPolly } = require('setup-polly-jest');
5 | const NodeHttpAdapter = require('@pollyjs/adapter-node-http');
6 | const FSPersister = require('@pollyjs/persister-fs');
7 |
8 | Polly.register(NodeHttpAdapter);
9 | Polly.register(FSPersister);
10 |
11 | const { posts, users } = require('../src');
12 |
13 | describe('jest-node-fetch', () => {
14 | setupPolly({
15 | adapters: ['node-http'],
16 | persister: 'fs',
17 | persisterOptions: {
18 | fs: {
19 | recordingsDir: path.resolve(__dirname, '../__recordings__')
20 | }
21 | }
22 | });
23 |
24 | describe('posts', () => {
25 | it('should return post', async () => {
26 | const post = await posts('1');
27 |
28 | expect(post.id).toBe(1);
29 | expect(post.title).toBe(
30 | 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit'
31 | );
32 | });
33 | });
34 |
35 | describe('users', () => {
36 | it('should return user', async () => {
37 | const user = await users('1');
38 |
39 | expect(user.id).toBe(1);
40 | expect(user.name).toBe('Leanne Graham');
41 | expect(user.email).toBe('Sincere@april.biz');
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/jest-node-fetch-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "test": "jest"
8 | },
9 | "devDependencies": {
10 | "@pollyjs/adapter-node-http": "*",
11 | "@pollyjs/core": "*",
12 | "@pollyjs/persister-fs": "*",
13 | "jest": "*",
14 | "node-fetch": "*",
15 | "setup-polly-jest": "*"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/src/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | posts: require('./posts'),
3 | users: require('./users')
4 | };
5 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/src/posts.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch');
2 |
3 | module.exports = async (id) => {
4 | const response = await fetch(
5 | `https://jsonplaceholder.typicode.com/posts/${id}`
6 | );
7 |
8 | return await response.json();
9 | };
10 |
--------------------------------------------------------------------------------
/examples/jest-node-fetch/src/users.js:
--------------------------------------------------------------------------------
1 | const fetch = require('node-fetch');
2 |
3 | module.exports = async (id) => {
4 | const response = await fetch(
5 | `https://jsonplaceholder.typicode.com/users/${id}`
6 | );
7 |
8 | return await response.json();
9 | };
10 |
--------------------------------------------------------------------------------
/examples/jest-puppeteer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | },
5 | globals: {
6 | page: true,
7 | browser: true,
8 | jestPuppeteer: true
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/examples/jest-puppeteer/jest-puppeteer.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | launch: {
3 | headless: true
4 | },
5 | server: {
6 | command: '(cd ../dummy-app && yarn start:ci)',
7 | port: 3000,
8 | launchTimeout: 60000
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/examples/jest-puppeteer/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'jest-puppeteer'
3 | };
4 |
--------------------------------------------------------------------------------
/examples/jest-puppeteer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/jest-puppeteer-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "postinstall": "(cd ../dummy-app && yarn install)",
8 | "test": "jest"
9 | },
10 | "devDependencies": {
11 | "@pollyjs/adapter-puppeteer": "^4.0.2",
12 | "@pollyjs/core": "^4.0.2",
13 | "@pollyjs/persister-fs": "^4.0.2",
14 | "jest": "^24.0.0",
15 | "jest-puppeteer": "^4.0.0",
16 | "puppeteer": "1.10.0",
17 | "setup-polly-jest": "^0.6.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/node-fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/node-fetch-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "test": "mocha './tests/*.test.js'"
8 | },
9 | "devDependencies": {
10 | "@pollyjs/adapter-node-http": "*",
11 | "@pollyjs/core": "*",
12 | "@pollyjs/persister-fs": "*",
13 | "chai": "*",
14 | "mocha": "*",
15 | "node-fetch": "*"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/node-fetch/tests/node-fetch.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const NodeHttpAdapter = require('@pollyjs/adapter-node-http');
4 | const FSPersister = require('@pollyjs/persister-fs');
5 | const fetch = require('node-fetch');
6 | const { Polly, setupMocha: setupPolly } = require('@pollyjs/core');
7 | const { expect } = require('chai');
8 |
9 | Polly.register(NodeHttpAdapter);
10 | Polly.register(FSPersister);
11 |
12 | describe('node-fetch', function () {
13 | setupPolly({
14 | adapters: ['node-http'],
15 | persister: 'fs',
16 | persisterOptions: {
17 | fs: {
18 | recordingsDir: path.resolve(__dirname, '../recordings')
19 | }
20 | }
21 | });
22 |
23 | it('should work', async function () {
24 | const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
25 | const post = await res.json();
26 |
27 | expect(res.status).to.equal(200);
28 | expect(post.id).to.equal(1);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/examples/puppeteer/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const puppeteer = require('puppeteer');
4 | const { Polly } = require('@pollyjs/core');
5 | const PuppeteerAdapter = require('@pollyjs/adapter-puppeteer');
6 | const FSPersister = require('@pollyjs/persister-fs');
7 |
8 | Polly.register(PuppeteerAdapter);
9 | Polly.register(FSPersister);
10 |
11 | (async () => {
12 | const browser = await puppeteer.launch({ headless: false });
13 | const page = await browser.newPage();
14 |
15 | await page.setRequestInterception(true);
16 |
17 | const polly = new Polly('puppeteer', {
18 | adapters: ['puppeteer'],
19 | adapterOptions: { puppeteer: { page } },
20 | persister: 'fs',
21 | persisterOptions: {
22 | fs: {
23 | recordingsDir: path.join(__dirname, 'recordings')
24 | }
25 | }
26 | });
27 |
28 | const { server } = polly;
29 |
30 | server.host('http://localhost:3000', () => {
31 | server.get('/favicon.ico').passthrough();
32 | server.get('/sockjs-node/*').intercept((_, res) => res.sendStatus(200));
33 | });
34 |
35 | await page.goto('http://localhost:3000', { waitUntil: 'networkidle0' });
36 |
37 | await polly.flush();
38 | await polly.stop();
39 | await browser.close();
40 | })();
41 |
--------------------------------------------------------------------------------
/examples/puppeteer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/puppeteer-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "postinstall": "(cd ../dummy-app && yarn install)",
8 | "start": "start-server-and-test start:server http://localhost:3000 start:puppeteer",
9 | "start:server": "(cd ../dummy-app && yarn start:ci)",
10 | "start:puppeteer": "node index.js"
11 | },
12 | "devDependencies": {
13 | "@pollyjs/adapter-puppeteer": "*",
14 | "@pollyjs/core": "*",
15 | "@pollyjs/persister-fs": "*",
16 | "puppeteer": "*",
17 | "start-server-and-test": "*"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/rest-persister/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | REST Persister Tests
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/rest-persister/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/rest-persister-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "test": "start-server-and-test test:polly-server http://localhost:3000 test:server",
8 | "test:server": "http-server -p 4000 -o -c-1 -s",
9 | "test:polly-server": "polly listen"
10 | },
11 | "devDependencies": {
12 | "@pollyjs/adapter-fetch": "*",
13 | "@pollyjs/cli": "*",
14 | "@pollyjs/core": "*",
15 | "@pollyjs/persister-rest": "*",
16 | "chai": "*",
17 | "http-server": "*",
18 | "mocha": "*",
19 | "start-server-and-test": "*"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/rest-persister/tests/rest-persister.test.js:
--------------------------------------------------------------------------------
1 | /* global setupPolly */
2 |
3 | describe('REST Persister', function () {
4 | setupPolly({
5 | adapters: ['fetch'],
6 | persister: 'rest'
7 | });
8 |
9 | it('should work', async function () {
10 | const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
11 | const post = await res.json();
12 |
13 | expect(res.status).to.equal(200);
14 | expect(post.id).to.equal(1);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/examples/rest-persister/tests/setup.js:
--------------------------------------------------------------------------------
1 | // Expose common globals
2 | window.PollyJS = window['@pollyjs/core'];
3 | window.setupPolly = window.PollyJS.setupMocha;
4 | window.expect = window.chai.expect;
5 |
6 | // Register the fetch adapter and REST persister
7 | window.PollyJS.Polly.register(window['@pollyjs/adapter-fetch']);
8 | window.PollyJS.Polly.register(window['@pollyjs/persister-rest']);
9 |
10 | // Setup Mocha
11 | mocha.setup({ ui: 'bdd', noHighlighting: true });
12 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "@jest/types";
2 |
3 | const config: Config.InitialOptions = {
4 | rootDir: ".",
5 | preset: "ts-jest",
6 | testEnvironment: "setup-polly-jest/jest-environment-jsdom",
7 | verbose: true,
8 | testPathIgnorePatterns: ["node_modules", "dist"],
9 | resetModules: true,
10 | globals: {
11 | "ts-jest": {
12 | useESM: true,
13 | },
14 | },
15 | transform: { },
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/typescript-jest-node-fetch-example",
3 | "version": "1.0.0",
4 | "private": true,
5 | "main": "./dist/index.js",
6 | "type": "commonjs",
7 | "exports": "./dist/index.js",
8 | "scripts": {
9 | "test": "jest --runInBand",
10 | "test:record": "POLLY_MODE=record jest --runInBand --verbose"
11 | },
12 | "keywords": [
13 | "pollyjs",
14 | "test-mocking",
15 | "jest",
16 | "node",
17 | "typescript",
18 | "fetch"
19 | ],
20 | "author": "",
21 | "license": "Apache-2.0",
22 | "dependencies": {
23 | "node-fetch": "^2.6.6"
24 | },
25 | "devDependencies": {
26 | "@pollyjs/adapter-fetch": "^5.1.1",
27 | "@pollyjs/adapter-node-http": "^5.1.1",
28 | "@pollyjs/core": "^5.1.1",
29 | "@pollyjs/node-server": "^5.1.1",
30 | "@pollyjs/persister-fs": "^5.1.1",
31 | "@types/jest": "^26.0.0",
32 | "@types/node": "^16.11.11",
33 | "@types/node-fetch": "^2.5.12",
34 | "@types/pollyjs__adapter": "^4.3.1",
35 | "@types/pollyjs__adapter-fetch": "^2.0.1",
36 | "@types/pollyjs__adapter-node-http": "^2.0.1",
37 | "@types/pollyjs__core": "^4.3.3",
38 | "@types/pollyjs__persister": "^4.3.1",
39 | "@types/pollyjs__persister-fs": "^2.0.1",
40 | "@types/pollyjs__utils": "^2.6.1",
41 | "@types/setup-polly-jest": "^0.5.1",
42 | "jest": "^26.6.0",
43 | "nodemon": "^2.0.15",
44 | "setup-polly-jest": "^0.10.0",
45 | "ts-jest": "^26.5.6",
46 | "ts-node": "^10.4.0",
47 | "typescript": "^4.5.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/src/github-api.test.ts:
--------------------------------------------------------------------------------
1 | /** @jest-environment setup-polly-jest/jest-environment-node */
2 | import autoSetupPolly from './utils/auto-setup-polly';
3 | import { getUser } from './github-api';
4 |
5 | describe('github-api client', () => {
6 | let pollyContext = autoSetupPolly();
7 |
8 | beforeEach(() => {
9 | // Intercept /ping healthcheck requests (example)
10 | pollyContext.polly.server
11 | .any("/ping")
12 | .intercept((req, res) => void res.sendStatus(200));
13 | });
14 |
15 | it('getUser', async () => {
16 | const user: any = await getUser('netflix');
17 | expect(typeof user).toBe('object');
18 | expect(user?.login).toBe('Netflix');
19 | });
20 |
21 | it('getUser: custom interceptor', async () => {
22 | expect.assertions(1);
23 | pollyContext.polly.server
24 | .get('https://api.github.com/users/failing_request_trigger')
25 | .intercept((req, res) => void res.sendStatus(500));
26 |
27 | await expect(getUser('failing_request_trigger')).rejects.toThrow(
28 | 'Http Error: 500'
29 | );
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/src/github-api.ts:
--------------------------------------------------------------------------------
1 | import fetch from "node-fetch";
2 | import type { Response } from "node-fetch";
3 |
4 | export const getUser = async (username: string): Promise => {
5 | return fetch(`https://api.github.com/users/${username}`, {
6 | headers: {
7 | "Accept": "application/json+vnd.github.v3.raw",
8 | "Content-type": "application/json",
9 | },
10 | })
11 | .then(checkErrorAndReturnJson);
12 | };
13 |
14 | function checkErrorAndReturnJson(response: Response) {
15 | return response.ok
16 | ? response.json()
17 | : Promise.reject(new Error(`Http Error: ${response.status}`));
18 | }
19 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/src/utils/auto-setup-polly.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import { setupPolly } from "setup-polly-jest";
3 | import { Polly, PollyConfig } from "@pollyjs/core";
4 | import NodeHttpAdapter from "@pollyjs/adapter-node-http";
5 | import FSPersister from "@pollyjs/persister-fs";
6 |
7 | Polly.register(NodeHttpAdapter);
8 | Polly.register(FSPersister);
9 |
10 | let recordIfMissing = true;
11 | let mode: PollyConfig['mode'] = 'replay';
12 |
13 | switch (process.env.POLLY_MODE) {
14 | case 'record':
15 | mode = 'record';
16 | break;
17 | case 'replay':
18 | mode = 'replay';
19 | break;
20 | case 'offline':
21 | mode = 'replay';
22 | recordIfMissing = false;
23 | break;
24 | }
25 |
26 | export default function autoSetupPolly() {
27 | /**
28 | * This persister can be adapted for both Node.js and Browser environments.
29 | *
30 | * TODO: Customize your config.
31 | */
32 | return setupPolly({
33 | // 🟡 Note: In node, most `fetch` like libraries use the http/https modules.
34 | // `node-fetch` is handled by `NodeHttpAdapter`, NOT the `FetchAdapter`.
35 | adapters: ["node-http"],
36 | mode,
37 | recordIfMissing,
38 | flushRequestsOnStop: true,
39 | logging: false,
40 | recordFailedRequests: true,
41 | persister: "fs",
42 | persisterOptions: {
43 | fs: {
44 | recordingsDir: path.resolve(__dirname, "../../__recordings__"),
45 | },
46 | },
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/examples/typescript-jest-node-fetch/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES5",
4 | "lib": ["esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "alwaysStrict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "module": "ESNext",
14 | "moduleResolution": "node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "sourceMap": true,
18 | "baseUrl": ".",
19 | "rootDir": ".",
20 | "outDir": "dist",
21 | "useUnknownInCatchVariables": false
22 | },
23 | "include": ["src"]
24 | }
25 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | module.exports = {
4 | testURL: 'http://localhost:4000/api',
5 | testMatch: ['**/@pollyjs/*/build/jest/*.js'],
6 | roots: ['/packages/@pollyjs'],
7 | reporters: ['jest-tap-reporter'],
8 | testEnvironment: 'jsdom'
9 | };
10 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "6.0.7",
3 | "npmClient": "yarn",
4 | "useWorkspaces": true,
5 | "packages": ["packages/@pollyjs/*"],
6 | "command": {
7 | "publish": {
8 | "allowBranch": "master",
9 | "conventionalCommits": true,
10 | "message": "chore: Publish %s",
11 | "ignoreChanges": ["docs/**", "examples/**", "**/tests/**", "**/*.md"]
12 | }
13 | },
14 | "changelog": {
15 | "repo": "Netflix/pollyjs",
16 | "labels": {
17 | "Tag: Breaking Change": ":boom: Breaking Change",
18 | "Tag: Enhancement": ":rocket: Enhancement",
19 | "Tag: Bug Fix": ":bug: Bug Fix",
20 | "Tag: Documentation": ":memo: Documentation"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/adapter-fetch",
3 | "version": "6.0.7",
4 | "description": "Fetch adapter for @pollyjs",
5 | "main": "dist/cjs/pollyjs-adapter-fetch.js",
6 | "module": "dist/es/pollyjs-adapter-fetch.js",
7 | "browser": "dist/umd/pollyjs-adapter-fetch.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/adapter-fetch",
15 | "license": "Apache-2.0",
16 | "contributors": [
17 | {
18 | "name": "Jason Mitchell",
19 | "email": "jason.mitchell.w@gmail.com"
20 | },
21 | {
22 | "name": "Offir Golan",
23 | "email": "offirgolan@gmail.com"
24 | }
25 | ],
26 | "keywords": [
27 | "polly",
28 | "pollyjs",
29 | "record",
30 | "replay",
31 | "fetch",
32 | "adapter"
33 | ],
34 | "publishConfig": {
35 | "access": "public"
36 | },
37 | "scripts": {
38 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
39 | "test:build": "rollup -c rollup.config.test.js",
40 | "test:build:watch": "rollup -c rollup.config.test.js -w",
41 | "build:watch": "yarn build -w",
42 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
43 | },
44 | "dependencies": {
45 | "@pollyjs/adapter": "^6.0.6",
46 | "@pollyjs/utils": "^6.0.6",
47 | "to-arraybuffer": "^1.0.1"
48 | },
49 | "devDependencies": {
50 | "@pollyjs/core": "^6.0.6",
51 | "@pollyjs/persister-local-storage": "^6.0.6",
52 | "@pollyjs/persister-rest": "^6.0.6",
53 | "rollup": "^1.14.6"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
2 |
3 | export default [createBrowserTestConfig()];
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/src/utils/serializer-headers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Serialize a Headers instance into a pojo since it cannot be stringified.
3 | * @param {*} headers
4 | */
5 | export default function serializeHeaders(headers) {
6 | if (headers && typeof headers.forEach === 'function') {
7 | const serializedHeaders = {};
8 |
9 | headers.forEach((value, key) => (serializedHeaders[key] = value));
10 |
11 | return serializedHeaders;
12 | }
13 |
14 | return headers || {};
15 | }
16 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/tests/integration/persister-local-storage-test.js:
--------------------------------------------------------------------------------
1 | import { setupMocha as setupPolly } from '@pollyjs/core';
2 | import LocalStoragePersister from '@pollyjs/persister-local-storage';
3 | import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record';
4 | import setupPersister from '@pollyjs-tests/helpers/setup-persister';
5 | import persisterTests from '@pollyjs-tests/integration/persister-tests';
6 |
7 | import pollyConfig from '../utils/polly-config';
8 |
9 | describe('Integration | Local Storage Persister', function () {
10 | setupPolly.beforeEach({
11 | ...pollyConfig,
12 | persister: LocalStoragePersister
13 | });
14 |
15 | setupFetchRecord();
16 | setupPersister();
17 | setupPolly.afterEach();
18 |
19 | persisterTests();
20 | });
21 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/tests/integration/persister-rest-test.js:
--------------------------------------------------------------------------------
1 | import { setupMocha as setupPolly } from '@pollyjs/core';
2 | import RESTPersister from '@pollyjs/persister-rest';
3 | import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record';
4 | import setupPersister from '@pollyjs-tests/helpers/setup-persister';
5 | import persisterTests from '@pollyjs-tests/integration/persister-tests';
6 |
7 | import pollyConfig from '../utils/polly-config';
8 |
9 | describe('Integration | REST Persister', function () {
10 | setupPolly.beforeEach({
11 | ...pollyConfig,
12 | persister: RESTPersister,
13 | persisterOptions: {
14 | rest: { host: '' }
15 | }
16 | });
17 |
18 | setupFetchRecord();
19 | setupPersister();
20 | setupPolly.afterEach();
21 |
22 | persisterTests();
23 | });
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/tests/utils/polly-config.js:
--------------------------------------------------------------------------------
1 | import InMemoryPersister from '@pollyjs/persister-in-memory';
2 |
3 | import FetchAdapter from '../../src';
4 |
5 | export default {
6 | recordFailedRequests: true,
7 | adapters: [FetchAdapter],
8 | persister: InMemoryPersister
9 | };
10 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-fetch/types.d.ts:
--------------------------------------------------------------------------------
1 | import Adapter from '@pollyjs/adapter';
2 |
3 | export default class FetchAdapter extends Adapter<{
4 | context?: any;
5 | }> {}
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: false,
4 | node: true
5 | },
6 | overrides: [
7 | {
8 | files: ['tests/jest/**/*.js'],
9 | env: {
10 | browser: true
11 | }
12 | }
13 | ]
14 | };
15 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/adapter-node-http",
3 | "version": "6.0.6",
4 | "description": "Node HTTP adapter for @pollyjs",
5 | "main": "dist/cjs/pollyjs-adapter-node-http.js",
6 | "module": "dist/es/pollyjs-adapter-node-http.js",
7 | "types": "types.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "types.d.ts"
12 | ],
13 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/adapter-node-http",
14 | "license": "Apache-2.0",
15 | "contributors": [
16 | {
17 | "name": "Yasin Uslu",
18 | "email": "a.yasin.uslu@gmail.com"
19 | },
20 | {
21 | "name": "Jason Mitchell",
22 | "email": "jason.mitchell.w@gmail.com"
23 | },
24 | {
25 | "name": "Offir Golan",
26 | "email": "offirgolan@gmail.com"
27 | }
28 | ],
29 | "keywords": [
30 | "polly",
31 | "pollyjs",
32 | "record",
33 | "replay",
34 | "http",
35 | "adapter"
36 | ],
37 | "publishConfig": {
38 | "access": "public"
39 | },
40 | "scripts": {
41 | "build": "rollup -c",
42 | "test:build": "rollup -c rollup.config.test.js",
43 | "test:build:watch": "rollup -c rollup.config.test.js -w",
44 | "build:watch": "yarn build -w",
45 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
46 | },
47 | "dependencies": {
48 | "@pollyjs/adapter": "^6.0.6",
49 | "@pollyjs/utils": "^6.0.6",
50 | "lodash-es": "^4.17.21",
51 | "nock": "^13.2.1"
52 | },
53 | "devDependencies": {
54 | "@pollyjs/core": "^6.0.6",
55 | "@pollyjs/persister-fs": "^6.0.6",
56 | "form-data": "^4.0.0",
57 | "get-stream": "^6.0.1",
58 | "node-fetch": "^2.6.6",
59 | "rollup": "^1.14.6"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/rollup.config.js:
--------------------------------------------------------------------------------
1 | import createNodeConfig from '../../../scripts/rollup/node.config';
2 |
3 | import { external } from './rollup.config.shared';
4 |
5 | export default createNodeConfig({ external });
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/rollup.config.shared.js:
--------------------------------------------------------------------------------
1 | export const external = [
2 | 'http',
3 | 'https',
4 | 'url',
5 | 'stream',
6 | 'timers',
7 | 'tty',
8 | 'util',
9 | 'os'
10 | ];
11 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createJestTestConfig from '../../../scripts/rollup/jest.test.config';
3 |
4 | import { external } from './rollup.config.shared';
5 |
6 | const testExternal = [...external, 'fs', 'path'];
7 |
8 | export default [
9 | createNodeTestConfig({ external: testExternal }),
10 | createJestTestConfig({ external: testExternal })
11 | ];
12 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/src/utils/get-url-from-options.js:
--------------------------------------------------------------------------------
1 | import { URL } from '@pollyjs/utils';
2 |
3 | /**
4 | * Generate an absolute url from options passed into `new http.ClientRequest`.
5 | *
6 | * @export
7 | * @param {Object} [options]
8 | * @returns {string}
9 | */
10 | export default function getUrlFromOptions(options = {}) {
11 | if (options.href) {
12 | return options.href;
13 | }
14 |
15 | const protocol = options.protocol || `${options.proto}:` || 'http:';
16 | const host = options.hostname || options.host || 'localhost';
17 | const { path, port } = options;
18 | const url = new URL();
19 |
20 | url.set('protocol', protocol);
21 | url.set('host', host);
22 | url.set('pathname', path);
23 |
24 | if (
25 | port &&
26 | !host.includes(':') &&
27 | (port !== 80 || protocol !== 'http:') &&
28 | (port !== 443 || protocol !== 'https:')
29 | ) {
30 | url.set('port', port);
31 | }
32 |
33 | return url.href;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/src/utils/merge-chunks.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Merge an array of strings into a single string or concat an array
3 | * of buffers into a single buffer.
4 | *
5 | * @export
6 | * @param {string[] | Buffer[]} [chunks]
7 | * @returns {string | Buffer}
8 | */
9 | export default function mergeChunks(chunks) {
10 | if (!chunks || chunks.length === 0) {
11 | return Buffer.alloc(0);
12 | }
13 |
14 | // We assume that all chunks are Buffer objects if the first is buffer object.
15 | if (!Buffer.isBuffer(chunks[0])) {
16 | // When the chunks are not buffers we assume that they are strings.
17 | return chunks.join('');
18 | }
19 |
20 | // Merge all the buffers into a single Buffer object.
21 | return Buffer.concat(chunks);
22 | }
23 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/src/utils/url-to-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility function that converts a URL object into an ordinary
3 | * options object as expected by the http.request and https.request APIs.
4 | *
5 | * This was copied from Node's source
6 | * https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/internal/url.js#L1257
7 | */
8 | export default function urlToOptions(url) {
9 | const options = {
10 | protocol: url.protocol,
11 | hostname:
12 | typeof url.hostname === 'string' && url.hostname.startsWith('[')
13 | ? url.hostname.slice(1, -1)
14 | : url.hostname,
15 | hash: url.hash,
16 | search: url.search,
17 | pathname: url.pathname,
18 | path: `${url.pathname}${url.search || ''}`,
19 | href: url.href
20 | };
21 |
22 | if (url.port !== '') {
23 | options.port = Number(url.port);
24 | }
25 |
26 | if (url.username || url.password) {
27 | options.auth = `${url.username}:${url.password}`;
28 | }
29 |
30 | return options;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/integration/adapter-node-fetch-test.js:
--------------------------------------------------------------------------------
1 | import '@pollyjs-tests/helpers/global-node-fetch';
2 |
3 | import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record';
4 | import adapterTests from '@pollyjs-tests/integration/adapter-tests';
5 | import adapterNodeTests from '@pollyjs-tests/integration/adapter-node-tests';
6 | import { setupMocha as setupPolly } from '@pollyjs/core';
7 |
8 | import pollyConfig from '../utils/polly-config';
9 |
10 | describe('Integration | Node Http Adapter | node-fetch', function () {
11 | setupPolly.beforeEach(pollyConfig);
12 |
13 | setupFetchRecord({ host: 'http://localhost:4000' });
14 | setupPolly.afterEach();
15 |
16 | adapterTests();
17 | adapterNodeTests();
18 | });
19 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/integration/persister-fs-test.js:
--------------------------------------------------------------------------------
1 | import http from 'http';
2 |
3 | import setupPersister from '@pollyjs-tests/helpers/setup-persister';
4 | import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record';
5 | import persisterTests from '@pollyjs-tests/integration/persister-tests';
6 | import { setupMocha as setupPolly } from '@pollyjs/core';
7 | import FSPersister from '@pollyjs/persister-fs';
8 |
9 | import nativeRequest from '../utils/native-request';
10 | import pollyConfig from '../utils/polly-config';
11 |
12 | describe('Integration | FS Persister', function () {
13 | setupPolly.beforeEach({
14 | ...pollyConfig,
15 | persister: FSPersister,
16 | persisterOptions: {
17 | fs: { recordingsDir: 'tests/recordings' }
18 | }
19 | });
20 |
21 | setupFetchRecord({
22 | host: 'http://localhost:4000',
23 | fetch: nativeRequest.bind(undefined, http)
24 | });
25 |
26 | setupPersister();
27 | setupPolly.afterEach();
28 |
29 | persisterTests();
30 | });
31 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/jest/integration/fetch-test.js:
--------------------------------------------------------------------------------
1 | import '@pollyjs-tests/helpers/global-node-fetch';
2 |
3 | import { Polly } from '@pollyjs/core';
4 |
5 | import pollyConfig from '../../utils/polly-config';
6 |
7 | describe('Integration | Jest | Fetch', function () {
8 | let polly;
9 |
10 | beforeEach(() => {
11 | polly = new Polly('Integration | Jest | Fetch', pollyConfig);
12 | });
13 |
14 | afterEach(async () => await polly.stop());
15 |
16 | test('it works', async () => {
17 | polly.recordingName += '/it works';
18 |
19 | const { persister, recordingId } = polly;
20 |
21 | expect((await fetch('http://localhost:4000/api/db/foo')).status).toBe(404);
22 | await persister.persist();
23 |
24 | const har = await persister.findRecording(recordingId);
25 |
26 | expect(har).toBeDefined();
27 | expect(har.log.entries.length).toBe(1);
28 | expect(har.log.entries[0].request.url.includes('/api/db/foo')).toBe(true);
29 | expect(har.log.entries[0].response.status).toBe(404);
30 |
31 | await persister.deleteRecording(recordingId);
32 | expect(persister.findRecording(recordingId)).resolves.toBeNull();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/jest/integration/xhr-test.js:
--------------------------------------------------------------------------------
1 | import { Polly } from '@pollyjs/core';
2 |
3 | import pollyConfig from '../../utils/polly-config';
4 |
5 | function request(url) {
6 | return new Promise((resolve, reject) => {
7 | const xhr = new XMLHttpRequest();
8 |
9 | xhr.addEventListener('load', function () {
10 | resolve(xhr);
11 | });
12 |
13 | xhr.addEventListener('error', reject);
14 | xhr.open('GET', url, true);
15 | xhr.send();
16 | });
17 | }
18 |
19 | describe('Integration | Jest | XHR', function () {
20 | let polly;
21 |
22 | beforeEach(() => {
23 | polly = new Polly('Integration | Jest | XHR', pollyConfig);
24 | });
25 |
26 | afterEach(async () => await polly.stop());
27 |
28 | test('it works', async () => {
29 | polly.recordingName += '/it works';
30 |
31 | const { persister, recordingId } = polly;
32 |
33 | expect((await request('/api/db/foo')).status).toBe(404);
34 | await persister.persist();
35 |
36 | const har = await persister.findRecording(recordingId);
37 |
38 | expect(har).toBeDefined();
39 | expect(har.log.entries.length).toBe(1);
40 | expect(har.log.entries[0].request.url.includes('/api/db/foo')).toBe(true);
41 | expect(har.log.entries[0].response.status).toBe(404);
42 |
43 | await persister.deleteRecording(recordingId);
44 | expect(persister.findRecording(recordingId)).resolves.toBeNull();
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/unit/utils/merge-chunks-test.js:
--------------------------------------------------------------------------------
1 | import mergeChunks from '../../../src/utils/merge-chunks';
2 |
3 | describe('Unit | Utils | mergeChunks', function () {
4 | it('should exist', function () {
5 | expect(mergeChunks).to.be.a('function');
6 | });
7 |
8 | it('should work', function () {
9 | [null, []].forEach((chunks) => {
10 | const buffer = mergeChunks(chunks);
11 |
12 | expect(Buffer.isBuffer(buffer)).to.be.true;
13 | expect(buffer.toString()).to.have.lengthOf(0);
14 | });
15 |
16 | const str = mergeChunks(['T', 'e', 's', 't']);
17 |
18 | expect(Buffer.isBuffer(str)).to.be.false;
19 | expect(str).to.equal('Test');
20 |
21 | const buffer = mergeChunks(['T', 'e', 's', 't'].map((c) => Buffer.from(c)));
22 |
23 | expect(Buffer.isBuffer(buffer)).to.be.true;
24 | expect(buffer.toString()).to.equal('Test');
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/utils/get-buffer-from-stream.js:
--------------------------------------------------------------------------------
1 | export default function getBufferFromStream(stream) {
2 | return new Promise((resolve) => {
3 | const chunks = [];
4 |
5 | stream.on('data', (chunk) => {
6 | chunks.push(chunk);
7 | });
8 |
9 | stream.on('end', () => {
10 | resolve(Buffer.concat(chunks));
11 | });
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/utils/get-response-from-request.js:
--------------------------------------------------------------------------------
1 | export default function getResponseFromRequest(req, data) {
2 | return new Promise((resolve, reject) => {
3 | req.once('response', resolve);
4 | req.once('error', reject);
5 | req.once('abort', reject);
6 |
7 | req.end(data);
8 | });
9 | }
10 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/utils/native-request.js:
--------------------------------------------------------------------------------
1 | import Url from 'url';
2 |
3 | import { Response } from 'node-fetch';
4 |
5 | import getResponseFromRequest from './get-response-from-request';
6 |
7 | export default async function nativeRequest(transport, url, options) {
8 | const opts = {
9 | ...(options || {}),
10 | ...Url.parse(url)
11 | };
12 | let reqBody;
13 |
14 | if (opts.body) {
15 | reqBody = opts.body;
16 | delete opts.body;
17 | }
18 |
19 | const response = await getResponseFromRequest(
20 | transport.request(opts),
21 | reqBody
22 | );
23 |
24 | return new Response(response, {
25 | status: response.statusCode,
26 | headers: response.headers
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/tests/utils/polly-config.js:
--------------------------------------------------------------------------------
1 | import InMemoryPersister from '@pollyjs/persister-in-memory';
2 |
3 | import NodeHttpAdapter from '../../src';
4 |
5 | export default {
6 | recordFailedRequests: true,
7 | adapters: [NodeHttpAdapter],
8 | persister: InMemoryPersister,
9 | persisterOptions: {}
10 | };
11 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-node-http/types.d.ts:
--------------------------------------------------------------------------------
1 | import Adapter from '@pollyjs/adapter';
2 |
3 | export default class NodeHttpAdapter extends Adapter {}
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/adapter-puppeteer",
3 | "version": "6.0.6",
4 | "description": "File system persister for @pollyjs",
5 | "main": "dist/cjs/pollyjs-adapter-puppeteer.js",
6 | "module": "dist/es/pollyjs-adapter-puppeteer.js",
7 | "types": "types.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "types.d.ts"
12 | ],
13 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/adapter-puppeteer",
14 | "license": "Apache-2.0",
15 | "contributors": [
16 | {
17 | "name": "Jason Mitchell",
18 | "email": "jason.mitchell.w@gmail.com"
19 | },
20 | {
21 | "name": "Offir Golan",
22 | "email": "offirgolan@gmail.com"
23 | }
24 | ],
25 | "keywords": [
26 | "polly",
27 | "pollyjs",
28 | "record",
29 | "replay",
30 | "fs",
31 | "file"
32 | ],
33 | "publishConfig": {
34 | "access": "public"
35 | },
36 | "scripts": {
37 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
38 | "build:watch": "yarn build -w",
39 | "test:build": "rollup -c rollup.config.test.js",
40 | "test:build:watch": "rollup -c rollup.config.test.js -w",
41 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
42 | },
43 | "dependencies": {
44 | "@pollyjs/adapter": "^6.0.6",
45 | "@pollyjs/utils": "^6.0.6"
46 | },
47 | "devDependencies": {
48 | "@pollyjs/core": "^6.0.6",
49 | "@pollyjs/persister-fs": "^6.0.6",
50 | "node-fetch": "^2.6.6",
51 | "puppeteer": "1.10.0",
52 | "rollup": "^1.14.6"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/rollup.config.js:
--------------------------------------------------------------------------------
1 | import createNodeConfig from '../../../scripts/rollup/node.config';
2 |
3 | export default createNodeConfig();
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 |
3 | export default createNodeTestConfig({
4 | external: ['puppeteer']
5 | });
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/tests/helpers/fetch.js:
--------------------------------------------------------------------------------
1 | import { Response } from 'node-fetch';
2 |
3 | export default async function fetch() {
4 | const res = await this.page.evaluate((...args) => {
5 | // This is run within the browser's context meaning it's using the
6 | // browser's native window.fetch method.
7 | return fetch(...args).then((res) => {
8 | const { url, status, headers } = res;
9 |
10 | return res.text().then((body) => {
11 | return { url, status, body, headers };
12 | });
13 | });
14 | }, ...arguments);
15 |
16 | return new Response(res.body, res);
17 | }
18 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/tests/unit/adapter-test.js:
--------------------------------------------------------------------------------
1 | import { setupMocha as setupPolly } from '@pollyjs/core';
2 | import { PollyError } from '@pollyjs/utils';
3 |
4 | import PuppeteerAdapter from '../../src';
5 |
6 | describe('Unit | Puppeteer Adapter', function () {
7 | setupPolly();
8 |
9 | it('should throw without a page instance', function () {
10 | expect(() =>
11 | this.polly.configure({
12 | adapters: [PuppeteerAdapter]
13 | })
14 | ).to.throw(PollyError, /A puppeteer page instance is required/);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-puppeteer/types.d.ts:
--------------------------------------------------------------------------------
1 | import Adapter from '@pollyjs/adapter';
2 |
3 | export default class PuppeteerAdapter extends Adapter<{
4 | page: any;
5 | requestResourceTypes?: string[];
6 | }> {}
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/adapter-xhr",
3 | "version": "6.0.6",
4 | "description": "XHR adapter for @pollyjs",
5 | "main": "dist/cjs/pollyjs-adapter-xhr.js",
6 | "module": "dist/es/pollyjs-adapter-xhr.js",
7 | "browser": "dist/umd/pollyjs-adapter-xhr.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/adapter-xhr",
15 | "license": "Apache-2.0",
16 | "contributors": [
17 | {
18 | "name": "Jason Mitchell",
19 | "email": "jason.mitchell.w@gmail.com"
20 | },
21 | {
22 | "name": "Offir Golan",
23 | "email": "offirgolan@gmail.com"
24 | }
25 | ],
26 | "keywords": [
27 | "polly",
28 | "pollyjs",
29 | "record",
30 | "replay",
31 | "xhr",
32 | "adapter"
33 | ],
34 | "publishConfig": {
35 | "access": "public"
36 | },
37 | "scripts": {
38 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
39 | "build:watch": "yarn build -w",
40 | "test:build": "rollup -c rollup.config.test.js",
41 | "test:build:watch": "rollup -c rollup.config.test.js -w",
42 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
43 | },
44 | "dependencies": {
45 | "@offirgolan/nise": "^4.1.0",
46 | "@pollyjs/adapter": "^6.0.6",
47 | "@pollyjs/utils": "^6.0.6",
48 | "to-arraybuffer": "^1.0.1"
49 | },
50 | "devDependencies": {
51 | "@pollyjs/core": "^6.0.6",
52 | "@pollyjs/persister-rest": "^6.0.6",
53 | "rollup": "^1.14.6"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
2 |
3 | export default [createBrowserTestConfig()];
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/src/utils/resolve-xhr.js:
--------------------------------------------------------------------------------
1 | export default function resolveXhr(xhr, body) {
2 | return new Promise((resolve) => {
3 | xhr.send(body);
4 |
5 | if (xhr.async) {
6 | const { onreadystatechange } = xhr;
7 |
8 | xhr.onreadystatechange = (...args) => {
9 | onreadystatechange && onreadystatechange.apply(xhr, ...args);
10 | xhr.readyState === XMLHttpRequest.DONE && resolve();
11 | };
12 | } else {
13 | resolve();
14 | }
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/src/utils/serialize-response-headers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Serialize response headers which is received as a string, into a pojo
3 | *
4 | * @param {String} responseHeaders
5 | */
6 | export default function serializeResponseHeaders(responseHeaders) {
7 | if (typeof responseHeaders !== 'string') {
8 | return responseHeaders;
9 | }
10 |
11 | return responseHeaders.split('\n').reduce((headers, header) => {
12 | const [key, value] = header.split(':');
13 |
14 | if (key) {
15 | headers[key] = value.replace(/(\r|\n|^\s+)/g, '');
16 | }
17 |
18 | return headers;
19 | }, {});
20 | }
21 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/tests/utils/xhr-request.js:
--------------------------------------------------------------------------------
1 | import serializeResponseHeaders from '../../src/utils/serialize-response-headers';
2 |
3 | export default function request(url, obj = {}) {
4 | return new Promise((resolve) => {
5 | const xhr = obj.xhr || new XMLHttpRequest();
6 |
7 | xhr.open(obj.method || 'GET', url);
8 |
9 | if (obj.headers) {
10 | for (const h in obj.headers) {
11 | xhr.setRequestHeader(h, obj.headers[h]);
12 | }
13 | }
14 |
15 | if (obj.responseType) {
16 | xhr.responseType = obj.responseType;
17 | }
18 |
19 | xhr.onreadystatechange = () =>
20 | xhr.readyState === XMLHttpRequest.DONE && resolve(xhr);
21 | xhr.onerror = () => resolve(xhr);
22 |
23 | xhr.send(obj.body);
24 | }).then((xhr) => {
25 | const responseBody =
26 | xhr.status === 204 && xhr.response === '' ? null : xhr.response;
27 |
28 | return new Response(responseBody, {
29 | status: xhr.status || 500,
30 | statusText: xhr.statusText,
31 | headers: serializeResponseHeaders(xhr.getAllResponseHeaders())
32 | });
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter-xhr/types.d.ts:
--------------------------------------------------------------------------------
1 | import Adapter from '@pollyjs/adapter';
2 |
3 | export default class XHRAdapter extends Adapter<{
4 | context?: any;
5 | }> {}
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/adapter",
3 | "version": "6.0.6",
4 | "description": "Extendable base adapter class used by @pollyjs",
5 | "main": "dist/cjs/pollyjs-adapter.js",
6 | "module": "dist/es/pollyjs-adapter.js",
7 | "browser": "dist/umd/pollyjs-adapter.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/adapter",
15 | "scripts": {
16 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
17 | "test:build": "rollup -c rollup.config.test.js",
18 | "test:build:watch": "rollup -c rollup.config.test.js -w",
19 | "build:watch": "yarn build -w",
20 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
21 | },
22 | "keywords": [
23 | "polly",
24 | "pollyjs",
25 | "adapter"
26 | ],
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "contributors": [
31 | {
32 | "name": "Jason Mitchell",
33 | "email": "jason.mitchell.w@gmail.com"
34 | },
35 | {
36 | "name": "Offir Golan",
37 | "email": "offirgolan@gmail.com"
38 | }
39 | ],
40 | "license": "Apache-2.0",
41 | "dependencies": {
42 | "@pollyjs/utils": "^6.0.6"
43 | },
44 | "devDependencies": {
45 | "rollup": "^1.14.6"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
3 |
4 | export default [createNodeTestConfig(), createBrowserTestConfig()];
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/src/utils/dehumanize-time.js:
--------------------------------------------------------------------------------
1 | const ALPHA_NUMERIC_DOT = /([0-9.]+)([a-zA-Z]+)/g;
2 | const TIMES = {
3 | ms: 1,
4 | millisecond: 1,
5 | milliseconds: 1,
6 | s: 1000,
7 | sec: 1000,
8 | secs: 1000,
9 | second: 1000,
10 | seconds: 1000,
11 | m: 60000,
12 | min: 60000,
13 | mins: 60000,
14 | minute: 60000,
15 | minutes: 60000,
16 | h: 3600000,
17 | hr: 3600000,
18 | hrs: 3600000,
19 | hour: 3600000,
20 | hours: 3600000,
21 | d: 86400000,
22 | day: 86400000,
23 | days: 86400000,
24 | w: 604800000,
25 | wk: 604800000,
26 | wks: 604800000,
27 | week: 604800000,
28 | weeks: 604800000,
29 | y: 31536000000,
30 | yr: 31536000000,
31 | yrs: 31536000000,
32 | year: 31536000000,
33 | years: 31536000000
34 | };
35 |
36 | export default function dehumanizeTime(input) {
37 | if (typeof input !== 'string') {
38 | return NaN;
39 | }
40 |
41 | const parts = input.replace(/ /g, '').match(ALPHA_NUMERIC_DOT);
42 | const sets = parts.map((part) =>
43 | part.split(ALPHA_NUMERIC_DOT).filter((o) => o)
44 | );
45 |
46 | return sets.reduce((accum, [number, unit]) => {
47 | return accum + parseFloat(number) * TIMES[unit];
48 | }, 0);
49 | }
50 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/src/utils/is-expired.js:
--------------------------------------------------------------------------------
1 | import dehumanizeTime from './dehumanize-time';
2 |
3 | export default function isExpired(recordedOn, expiresIn) {
4 | if (recordedOn && expiresIn) {
5 | return (
6 | new Date() >
7 | new Date(new Date(recordedOn).getTime() + dehumanizeTime(expiresIn))
8 | );
9 | }
10 |
11 | return false;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/src/utils/normalize-recorded-response.js:
--------------------------------------------------------------------------------
1 | const { isArray } = Array;
2 |
3 | export default function normalizeRecordedResponse(response) {
4 | const { status, statusText, headers, content } = response;
5 |
6 | return {
7 | statusText,
8 | statusCode: status,
9 | headers: normalizeHeaders(headers),
10 | body: content && content.text,
11 | encoding: content && content.encoding
12 | };
13 | }
14 |
15 | function normalizeHeaders(headers) {
16 | return (headers || []).reduce((accum, { name, value, _fromType }) => {
17 | const existingValue = accum[name];
18 |
19 | if (existingValue) {
20 | if (!isArray(existingValue)) {
21 | accum[name] = [existingValue];
22 | }
23 |
24 | accum[name].push(value);
25 | } else {
26 | accum[name] = _fromType === 'array' ? [value] : value;
27 | }
28 |
29 | return accum;
30 | }, {});
31 | }
32 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/src/utils/stringify-request.js:
--------------------------------------------------------------------------------
1 | export default function stringifyRequest(req, ...args) {
2 | const config = { ...req.config };
3 |
4 | // Remove all adapter & persister config options as they can cause a circular
5 | // structure to the final JSON
6 | ['adapter', 'adapterOptions', 'persister', 'persisterOptions'].forEach(
7 | (k) => delete config[k]
8 | );
9 |
10 | return JSON.stringify(
11 | {
12 | url: req.url,
13 | method: req.method,
14 | headers: req.headers,
15 | body: req.body,
16 | recordingName: req.recordingName,
17 | id: req.id,
18 | order: req.order,
19 | identifiers: req.identifiers,
20 | config
21 | },
22 | ...args
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/tests/unit/adapter-test.js:
--------------------------------------------------------------------------------
1 | import Adapter from '../../src';
2 |
3 | describe('Unit | Adapter', function () {
4 | it('should exist', function () {
5 | expect(Adapter).to.be.a('function');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/tests/unit/utils/dehumanize-time-test.js:
--------------------------------------------------------------------------------
1 | import dehumanizeTime from '../../../src/utils/dehumanize-time';
2 |
3 | describe('Unit | Utils | dehumanizeTime', function () {
4 | it('should exist', function () {
5 | expect(dehumanizeTime).to.be.a('function');
6 | });
7 |
8 | it('should work', function () {
9 | expect(dehumanizeTime(null)).to.be.NaN;
10 | expect(dehumanizeTime(undefined)).to.be.NaN;
11 | expect(dehumanizeTime(true)).to.be.NaN;
12 | expect(dehumanizeTime(false)).to.be.NaN;
13 |
14 | [
15 | [['1ms', '1millisecond', '1 milliseconds'], 1],
16 | [['10ms', '10millisecond', '10 milliseconds'], 10],
17 | [['100ms', '100millisecond', '100 milliseconds'], 100],
18 | [['1s', '1sec', '1secs', '1 second', '1 seconds'], 1000],
19 | [['1.5s', '1.5sec', '1.5secs', '1.5 second', '1.5 seconds'], 1500],
20 | [['1m', '1min', '1mins', '1 minute', '1 minutes'], 1000 * 60],
21 | [['1h', '1hr', '1hrs', '1 hour', '1 hours'], 1000 * 60 * 60],
22 | [['1d', '1day', '1 days'], 1000 * 60 * 60 * 24],
23 | [['1w', '1wk', '1wks', '1 week', '1 weeks'], 1000 * 60 * 60 * 24 * 7],
24 | [['1y', '1yr', '1 yrs', '1 year', '1 years'], 1000 * 60 * 60 * 24 * 365],
25 | [
26 | [
27 | '1y 2 wks 3day 4 minutes 5secs 6 ms',
28 | '6 millisecond 5s 4 min 3d 2 weeks 1yrs'
29 | ],
30 | 33005045006
31 | ]
32 | ].forEach(([inputs, value]) => {
33 | inputs.forEach((str) => expect(dehumanizeTime(str)).to.equal(value));
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/tests/unit/utils/is-expired-test.js:
--------------------------------------------------------------------------------
1 | import isExpired from '../../../src/utils/is-expired';
2 |
3 | describe('Unit | Utils | isExpired', function () {
4 | it('should exist', function () {
5 | expect(isExpired).to.be.a('function');
6 | });
7 |
8 | it('should work', function () {
9 | [
10 | [undefined, undefined, false],
11 | [null, null, false],
12 | [new Date(), undefined, false],
13 | [undefined, '1 day', false],
14 | [new Date(), '1 day', false],
15 | [new Date('1/1/2018'), '100 years', false],
16 | [new Date('1/1/2017'), '1y', true],
17 | [new Date('1/1/2018'), '1m5d10h', true]
18 | ].forEach(([recordedOn, expiresIn, value]) => {
19 | expect(isExpired(recordedOn, expiresIn)).to.equal(value);
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/packages/@pollyjs/adapter/types.d.ts:
--------------------------------------------------------------------------------
1 | import { Polly, Request, Interceptor, Response } from '@pollyjs/core';
2 |
3 | export default class Adapter<
4 | TOptions extends {} = {},
5 | TRequest extends Request = Request
6 | > {
7 | static readonly id: string;
8 | static readonly type: string;
9 | constructor(polly: Polly);
10 | polly: Polly;
11 | isConnected: boolean;
12 | readonly defaultOptions: TOptions;
13 | readonly options: TOptions;
14 | persister: Polly['persister'];
15 | connect(): void;
16 | onConnect(): void;
17 | disconnect(): void;
18 | onDisconnect(): void;
19 | private timeout(request: TRequest, options: { time: number }): Promise;
20 | handleRequest(
21 | request: Pick<
22 | TRequest,
23 | 'url' | 'method' | 'headers' | 'body' | 'requestArguments'
24 | >
25 | ): Promise;
26 | private passthrough(request: TRequest): Promise;
27 | onPassthrough(request: TRequest): Promise;
28 | private intercept(request: TRequest, interceptor: Interceptor): Promise;
29 | onIntercept(request: TRequest, interceptor: Interceptor): Promise;
30 | private record(request: TRequest): Promise;
31 | onRecord(request: TRequest): Promise;
32 | private replay(request: TRequest): Promise;
33 | onReplay(request: TRequest): Promise;
34 | assert(message: string, condition?: boolean): void;
35 | onFetchResponse(
36 | pollyRequest: TRequest
37 | ): Promise>;
38 | onRespond(request: TRequest, error?: Error): Promise;
39 | onIdentifyRequest(request: TRequest): Promise;
40 | onRequest(request: TRequest): Promise;
41 | onRequestFinished(request: TRequest): Promise;
42 | onRequestFailed(request: TRequest): Promise;
43 | }
44 |
--------------------------------------------------------------------------------
/packages/@pollyjs/cli/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserOptions: {
3 | sourceType: 'script',
4 | ecmaVersion: 2020
5 | },
6 | env: {
7 | browser: false,
8 | node: true
9 | },
10 | plugins: ['node'],
11 | extends: ['plugin:node/recommended']
12 | };
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/cli/bin/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // Provide a title to the process in `ps`
4 | process.title = 'polly';
5 |
6 | const Polly = require('@pollyjs/node-server');
7 | const { program } = require('commander');
8 |
9 | const version = require('../package.json').version;
10 |
11 | program.name('polly').version(version, '-v, --version');
12 |
13 | program
14 | .command('listen')
15 | .alias('l')
16 | .description('start the server and listen for requests')
17 | .option('-H, --host ', 'host')
18 | .option('-p, --port ', 'port number', Polly.Defaults.port)
19 | .option(
20 | '-n, --api-namespace ',
21 | 'api namespace',
22 | Polly.Defaults.apiNamespace
23 | )
24 | .option(
25 | '-d, --recordings-dir ',
26 | 'recordings directory',
27 | Polly.Defaults.recordingsDir
28 | )
29 | .option(
30 | '-s, --recording-size-limit ',
31 | 'recording size limit',
32 | Polly.Defaults.recordingSizeLimit
33 | )
34 | .option('-q, --quiet', 'disable logging')
35 | .action(function (options) {
36 | new Polly.Server(options).listen();
37 | });
38 |
39 | program.parse(process.argv);
40 |
41 | // if cli was called with no arguments, show help.
42 | if (program.args.length === 0) {
43 | program.help();
44 | }
45 |
--------------------------------------------------------------------------------
/packages/@pollyjs/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/cli",
3 | "version": "6.0.6",
4 | "description": "@pollyjs CLI",
5 | "files": [
6 | "bin"
7 | ],
8 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/cli",
9 | "keywords": [
10 | "polly",
11 | "pollyjs",
12 | "cli",
13 | "server",
14 | "listen"
15 | ],
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "contributors": [
20 | {
21 | "name": "Jason Mitchell",
22 | "email": "jason.mitchell.w@gmail.com"
23 | },
24 | {
25 | "name": "Offir Golan",
26 | "email": "offirgolan@gmail.com"
27 | }
28 | ],
29 | "license": "Apache-2.0",
30 | "dependencies": {
31 | "@pollyjs/node-server": "^6.0.6",
32 | "commander": "^8.3.0"
33 | },
34 | "devDependencies": {
35 | "npm-run-all": "^4.1.5",
36 | "rimraf": "^3.0.2",
37 | "rollup": "^1.14.6"
38 | },
39 | "bin": {
40 | "polly": "./bin/cli.js"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/core",
3 | "version": "6.0.6",
4 | "description": "Record, replay, and stub HTTP Interactions",
5 | "main": "dist/cjs/pollyjs-core.js",
6 | "module": "dist/es/pollyjs-core.js",
7 | "browser": "dist/umd/pollyjs-core.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/core",
15 | "scripts": {
16 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
17 | "test:build": "rollup -c rollup.config.test.js",
18 | "test:build:watch": "rollup -c rollup.config.test.js -w",
19 | "build:watch": "yarn build -w",
20 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
21 | },
22 | "keywords": [
23 | "polly",
24 | "pollyjs",
25 | "vcr",
26 | "record",
27 | "replay",
28 | "recorder",
29 | "test",
30 | "mock"
31 | ],
32 | "publishConfig": {
33 | "access": "public"
34 | },
35 | "contributors": [
36 | {
37 | "name": "Jason Mitchell",
38 | "email": "jason.mitchell.w@gmail.com"
39 | },
40 | {
41 | "name": "Offir Golan",
42 | "email": "offirgolan@gmail.com"
43 | }
44 | ],
45 | "license": "Apache-2.0",
46 | "dependencies": {
47 | "@pollyjs/utils": "^6.0.6",
48 | "@sindresorhus/fnv1a": "^2.0.1",
49 | "blueimp-md5": "^2.19.0",
50 | "fast-json-stable-stringify": "^2.1.0",
51 | "is-absolute-url": "^3.0.3",
52 | "lodash-es": "^4.17.21",
53 | "loglevel": "^1.8.0",
54 | "route-recognizer": "^0.3.4",
55 | "slugify": "^1.6.3"
56 | },
57 | "devDependencies": {
58 | "@pollyjs/adapter": "^6.0.6",
59 | "@pollyjs/persister": "^6.0.6",
60 | "rollup": "^1.14.6"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
3 |
4 | export default [createNodeTestConfig(), createBrowserTestConfig()];
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/-private/event.js:
--------------------------------------------------------------------------------
1 | import { assert } from '@pollyjs/utils';
2 |
3 | const STOP_PROPAGATION = Symbol();
4 |
5 | export default class Event {
6 | constructor(type, props) {
7 | assert(
8 | `Invalid type provided. Expected a non-empty string, received: "${typeof type}".`,
9 | type && typeof type === 'string'
10 | );
11 |
12 | Object.defineProperty(this, 'type', { value: type });
13 | // eslint-disable-next-line no-restricted-properties
14 | Object.assign(this, props || {});
15 |
16 | this[STOP_PROPAGATION] = false;
17 | }
18 |
19 | stopPropagation() {
20 | this[STOP_PROPAGATION] = true;
21 | }
22 |
23 | get shouldStopPropagating() {
24 | return this[STOP_PROPAGATION];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/-private/interceptor.js:
--------------------------------------------------------------------------------
1 | import Event from './event';
2 |
3 | const ABORT = Symbol();
4 | const PASSTHROUGH = Symbol();
5 |
6 | function setDefaults(interceptor) {
7 | interceptor[ABORT] = false;
8 | interceptor[PASSTHROUGH] = false;
9 | }
10 |
11 | export default class Interceptor extends Event {
12 | constructor() {
13 | super('intercept');
14 | setDefaults(this);
15 | }
16 |
17 | abort() {
18 | setDefaults(this);
19 | this[ABORT] = true;
20 | }
21 |
22 | passthrough() {
23 | setDefaults(this);
24 | this[PASSTHROUGH] = true;
25 | }
26 |
27 | get shouldAbort() {
28 | return this[ABORT];
29 | }
30 |
31 | get shouldPassthrough() {
32 | return this[PASSTHROUGH];
33 | }
34 |
35 | get shouldIntercept() {
36 | return !this.shouldAbort && !this.shouldPassthrough;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/-private/response.js:
--------------------------------------------------------------------------------
1 | import { assert, HTTP_STATUS_CODES } from '@pollyjs/utils';
2 |
3 | import HTTPBase from './http-base';
4 |
5 | const DEFAULT_STATUS_CODE = 200;
6 |
7 | export default class PollyResponse extends HTTPBase {
8 | constructor(statusCode, headers, body, encoding) {
9 | super();
10 | this.status(statusCode || DEFAULT_STATUS_CODE);
11 | this.setHeaders(headers);
12 | this.body = body;
13 | this.encoding = encoding;
14 | }
15 |
16 | get ok() {
17 | return this.statusCode && this.statusCode >= 200 && this.statusCode < 300;
18 | }
19 |
20 | get statusText() {
21 | return (
22 | HTTP_STATUS_CODES[this.statusCode] ||
23 | HTTP_STATUS_CODES[DEFAULT_STATUS_CODE]
24 | );
25 | }
26 |
27 | status(statusCode) {
28 | const status = parseInt(statusCode, 10);
29 |
30 | assert(
31 | `[Response] Invalid status code: ${status}`,
32 | status >= 100 && status < 600
33 | );
34 |
35 | this.statusCode = status;
36 |
37 | return this;
38 | }
39 |
40 | sendStatus(status) {
41 | this.status(status);
42 | this.type('text/plain');
43 |
44 | return this.send(this.statusText);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/defaults/config.js:
--------------------------------------------------------------------------------
1 | import { MODES, EXPIRY_STRATEGIES } from '@pollyjs/utils';
2 | import logLevel from 'loglevel';
3 |
4 | import Timing from '../utils/timing';
5 |
6 | export default {
7 | mode: MODES.REPLAY,
8 |
9 | adapters: [],
10 | adapterOptions: {},
11 |
12 | persister: null,
13 | persisterOptions: {
14 | keepUnusedRequests: false,
15 | disableSortingHarEntries: false
16 | },
17 |
18 | logLevel: logLevel.levels.WARN,
19 | flushRequestsOnStop: false,
20 |
21 | recordIfMissing: true,
22 | recordFailedRequests: false,
23 |
24 | expiresIn: null,
25 | expiryStrategy: EXPIRY_STRATEGIES.WARN,
26 | timing: Timing.fixed(0),
27 |
28 | matchRequestsBy: {
29 | method: true,
30 | headers: true,
31 | body: true,
32 | order: true,
33 |
34 | url: {
35 | protocol: true,
36 | username: true,
37 | password: true,
38 | hostname: true,
39 | port: true,
40 | pathname: true,
41 | query: true,
42 | hash: false
43 | }
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as Polly } from './polly';
2 | export { default as Timing } from './utils/timing';
3 |
4 | export { default as setupQunit } from './test-helpers/qunit';
5 | export { default as setupMocha } from './test-helpers/mocha';
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/server/middleware.js:
--------------------------------------------------------------------------------
1 | import RouteRecognizer from 'route-recognizer';
2 |
3 | import Route from './route';
4 |
5 | const GLOBAL = '__GLOBAL__';
6 |
7 | export default class Middleware {
8 | constructor({ host, paths, global, handler }) {
9 | this.global = Boolean(global);
10 | this.handler = handler;
11 | this.host = host;
12 | this.paths = this.global ? [GLOBAL] : paths;
13 | this._routeRecognizer = new RouteRecognizer();
14 |
15 | this.paths.forEach((path) =>
16 | this._routeRecognizer.add([{ path, handler: [handler] }])
17 | );
18 | }
19 |
20 | match(host, path) {
21 | if (this.global) {
22 | return new Route(this._routeRecognizer.recognize(GLOBAL));
23 | }
24 |
25 | if (this.host === host) {
26 | const recognizeResult = this._routeRecognizer.recognize(path);
27 |
28 | return recognizeResult && new Route(recognizeResult);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/test-helpers/lib.js:
--------------------------------------------------------------------------------
1 | import { PollyError } from '@pollyjs/utils';
2 |
3 | import Polly from '../polly';
4 |
5 | const { defineProperty } = Object;
6 |
7 | export function beforeEach(context, recordingName, defaults) {
8 | defineProperty(context, 'polly', {
9 | writable: true,
10 | enumerable: true,
11 | configurable: true,
12 | value: new Polly(recordingName, defaults)
13 | });
14 | }
15 |
16 | export async function afterEach(context, framework) {
17 | await context.polly.stop();
18 |
19 | defineProperty(context, 'polly', {
20 | enumerable: true,
21 | configurable: true,
22 | get() {
23 | throw new PollyError(
24 | `You are trying to access an instance of Polly that is no longer available.\n` +
25 | `See: https://netflix.github.io/pollyjs/#/test-frameworks/${framework}?id=test-hook-ordering`
26 | );
27 | }
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/test-helpers/mocha.js:
--------------------------------------------------------------------------------
1 | import { afterEach, beforeEach } from './lib';
2 |
3 | function generateRecordingName(context) {
4 | const { currentTest } = context;
5 | const parts = [currentTest.title];
6 | let parent = currentTest.parent;
7 |
8 | while (parent && parent.title) {
9 | parts.push(parent.title);
10 | parent = parent.parent;
11 | }
12 |
13 | return parts.reverse().join('/');
14 | }
15 |
16 | export default function setupMocha(defaults = {}, ctx = global) {
17 | setupMocha.beforeEach(defaults, ctx);
18 | setupMocha.afterEach(ctx);
19 | }
20 |
21 | setupMocha.beforeEach = function setupMochaBeforeEach(defaults, ctx = global) {
22 | ctx.beforeEach(function () {
23 | return beforeEach(this, generateRecordingName(this), defaults);
24 | });
25 | };
26 |
27 | setupMocha.afterEach = function setupMochaAfterEach(ctx = global) {
28 | ctx.afterEach(function () {
29 | return afterEach(this, 'mocha');
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/test-helpers/qunit.js:
--------------------------------------------------------------------------------
1 | import { afterEach, beforeEach } from './lib';
2 |
3 | function generateRecordingName(assert) {
4 | return assert.test.testReport.fullName.join('/');
5 | }
6 |
7 | export default function setupQunit(hooks, defaults = {}) {
8 | setupQunit.beforeEach(hooks, defaults);
9 | setupQunit.afterEach(hooks);
10 | }
11 |
12 | setupQunit.beforeEach = function setupQunitBeforeEach(hooks, defaults = {}) {
13 | hooks.beforeEach(function () {
14 | return beforeEach(this, generateRecordingName(...arguments), defaults);
15 | });
16 | };
17 |
18 | setupQunit.afterEach = function setupQunitAfterEach(hooks) {
19 | hooks.afterEach(function () {
20 | return afterEach(this, 'qunit');
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/cancel-fn-after-n-times.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a function that will execute the given fn and call the cancel
3 | * callback after being called n times.
4 | *
5 | * @export
6 | * @param {Function} fn
7 | * @param {Number} nTimes
8 | * @param {Function} cancel
9 | * @returns
10 | */
11 | export default function cancelFnAfterNTimes(fn, nTimes, cancel) {
12 | let callCount = 0;
13 |
14 | return function (...args) {
15 | if (++callCount >= nTimes) {
16 | cancel();
17 | }
18 |
19 | return fn(...args);
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/deferred-promise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a deferred promise with `resolve` and `reject` methods.
3 | */
4 | export default function defer() {
5 | let _resolve;
6 | let _reject;
7 |
8 | const promise = new Promise((resolve, reject) => {
9 | _resolve = resolve;
10 | _reject = reject;
11 | });
12 |
13 | // Prevent unhandled rejection warnings
14 | promise.catch(() => {});
15 |
16 | promise.resolve = _resolve;
17 | promise.reject = _reject;
18 |
19 | return promise;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/guid-for-recording.js:
--------------------------------------------------------------------------------
1 | import fnv1a from '@sindresorhus/fnv1a';
2 | import slugify from 'slugify';
3 |
4 | function sanitize(str) {
5 | // Strip non-alphanumeric chars (\W is the equivalent of [^0-9a-zA-Z_])
6 | return str.replace(/\W/g, '-');
7 | }
8 |
9 | function guidFor(str) {
10 | const hash = fnv1a(str).toString();
11 | let slug = slugify(sanitize(str));
12 |
13 | // Max the slug at 100 char
14 | slug = slug.substring(0, 100 - hash.length - 1);
15 |
16 | return `${slug}_${hash}`;
17 | }
18 |
19 | export default function guidForRecording(recording) {
20 | return (recording || '').split('/').map(guidFor).join('/');
21 | }
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/http-headers.js:
--------------------------------------------------------------------------------
1 | import isObjectLike from 'lodash-es/isObjectLike';
2 |
3 | const { keys } = Object;
4 |
5 | const HANDLER = {
6 | get(obj, prop) {
7 | // `prop` can be a Symbol so only lower-case string based props.
8 | return obj[typeof prop === 'string' ? prop.toLowerCase() : prop];
9 | },
10 |
11 | set(obj, prop, value) {
12 | if (typeof prop !== 'string') {
13 | return false;
14 | }
15 |
16 | if (value === null || typeof value === 'undefined') {
17 | delete obj[prop.toLowerCase()];
18 | } else {
19 | obj[prop.toLowerCase()] = value;
20 | }
21 |
22 | return true;
23 | },
24 |
25 | deleteProperty(obj, prop) {
26 | if (typeof prop !== 'string') {
27 | return false;
28 | }
29 |
30 | delete obj[prop.toLowerCase()];
31 |
32 | return true;
33 | }
34 | };
35 |
36 | export default function HTTPHeaders(headers) {
37 | const proxy = new Proxy({}, HANDLER);
38 |
39 | if (isObjectLike(headers)) {
40 | keys(headers).forEach((h) => (proxy[h] = headers[h]));
41 | }
42 |
43 | return proxy;
44 | }
45 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/merge-configs.js:
--------------------------------------------------------------------------------
1 | import mergeWith from 'lodash-es/mergeWith';
2 |
3 | function customizer(objValue, srcValue, key) {
4 | // Arrays and `context` options should just replace the existing value
5 | // and not be deep merged.
6 | if (Array.isArray(objValue) || ['context'].includes(key)) {
7 | return srcValue;
8 | }
9 | }
10 |
11 | export default function mergeConfigs(...configs) {
12 | return mergeWith({}, ...configs, customizer);
13 | }
14 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/parse-url.js:
--------------------------------------------------------------------------------
1 | import isAbsoluteUrl from 'is-absolute-url';
2 | import { URL } from '@pollyjs/utils';
3 |
4 | import removeHostFromUrl from './remove-host-from-url';
5 |
6 | /**
7 | * Creates an exact representation of the passed url string with url-parse.
8 | *
9 | * @param {String} url
10 | * @param {...args} args Arguments to pass through to the URL constructor
11 | * @returns {URL} A url-parse URL instance
12 | */
13 | export default function parseUrl(url, ...args) {
14 | const parsedUrl = new URL(url, ...args);
15 |
16 | if (!isAbsoluteUrl(url)) {
17 | if (url.startsWith('//')) {
18 | /*
19 | If the url is protocol-relative, strip out the protocol
20 | */
21 | parsedUrl.set('protocol', '');
22 | } else {
23 | /*
24 | If the url is relative, setup the parsed url to reflect just that
25 | by removing the host. By default URL sets the host via window.location if
26 | it does not exist.
27 | */
28 | removeHostFromUrl(parsedUrl);
29 | }
30 | }
31 |
32 | return parsedUrl;
33 | }
34 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/remove-host-from-url.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Remove the host, protocol, and slashes from a URL instance.
3 | *
4 | * @param {URL} url
5 | */
6 | export default function removeHostFromUrl(url) {
7 | url.set('protocol', '');
8 | url.set('host', '');
9 | url.set('slashes', false);
10 |
11 | return url;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/timing.js:
--------------------------------------------------------------------------------
1 | import { timeout } from '@pollyjs/utils';
2 |
3 | export default {
4 | fixed(ms) {
5 | return () => timeout(ms);
6 | },
7 |
8 | relative(ratio) {
9 | return (ms) => timeout(ratio * ms);
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/src/utils/validators.js:
--------------------------------------------------------------------------------
1 | import isObjectLike from 'lodash-es/isObjectLike';
2 | import { assert } from '@pollyjs/utils';
3 |
4 | export function validateRecordingName(name) {
5 | assert(
6 | `Invalid recording name provided. Expected string, received: "${typeof name}".`,
7 | typeof name === 'string'
8 | );
9 |
10 | assert(
11 | `Invalid recording name provided. Received An empty or blank string.`,
12 | name.trim().length > 0
13 | );
14 | }
15 |
16 | export function validateRequestConfig(config) {
17 | assert(
18 | `Invalid config provided. Expected object, received: "${typeof config}".`,
19 | isObjectLike(config) && !Array.isArray(config)
20 | );
21 |
22 | // The following options cannot be overridden on a per request basis
23 | [
24 | 'mode',
25 | 'adapters',
26 | 'adapterOptions',
27 | 'persister',
28 | 'persisterOptions'
29 | ].forEach((key) =>
30 | assert(
31 | `Invalid configuration option provided. The "${key}" option cannot be overridden using the server configuration API.`,
32 | !(key in config)
33 | )
34 | );
35 | }
36 |
37 | export function validateTimesOption(times) {
38 | assert(
39 | `Invalid number provided. Expected number, received: "${typeof times}".`,
40 | typeof times === 'number'
41 | );
42 |
43 | assert(
44 | `Invalid number provided. The number must be greater than 0, received "${typeof times}".`,
45 | times > 0
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/-private/event-test.js:
--------------------------------------------------------------------------------
1 | import { PollyError } from '@pollyjs/utils';
2 |
3 | import Event from '../../../src/-private/event';
4 |
5 | const EVENT_TYPE = 'foo';
6 |
7 | describe('Unit | Event', function () {
8 | it('should exist', function () {
9 | expect(Event).to.be.a('function');
10 | });
11 |
12 | it('should throw if no type is specified', function () {
13 | expect(() => new Event()).to.throw(PollyError, /Invalid type provided/);
14 | });
15 |
16 | it('should have the correct defaults', function () {
17 | const event = new Event(EVENT_TYPE);
18 |
19 | expect(event.type).to.equal(EVENT_TYPE);
20 | expect(event.shouldStopPropagating).to.be.false;
21 | });
22 |
23 | it('should not be able to edit the type', function () {
24 | const event = new Event(EVENT_TYPE);
25 |
26 | expect(event.type).to.equal(EVENT_TYPE);
27 | expect(() => (event.type = 'bar')).to.throw(Error);
28 | });
29 |
30 | it('should be able to attach any other properties', function () {
31 | const event = new Event(EVENT_TYPE, { foo: 1, bar: 2 });
32 |
33 | expect(event.foo).to.equal(1);
34 | expect(event.bar).to.equal(2);
35 | });
36 |
37 | it('.stopPropagation()', function () {
38 | const event = new Event(EVENT_TYPE);
39 |
40 | expect(event.shouldStopPropagating).to.be.false;
41 | event.stopPropagation();
42 | expect(event.shouldStopPropagating).to.be.true;
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/index-test.js:
--------------------------------------------------------------------------------
1 | import * as PollyExports from '../../src';
2 |
3 | describe('Unit | Index', function () {
4 | it('should export all the necessary modules', function () {
5 | ['Polly', 'Timing', 'setupQunit', 'setupMocha'].forEach((name) => {
6 | expect(PollyExports[name]).to.be.ok;
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/test-helpers/mocha-test.js:
--------------------------------------------------------------------------------
1 | import setupPolly from '../../../src/test-helpers/mocha';
2 |
3 | class Sandbox {
4 | constructor(context) {
5 | this.beforeEachCalled = new Set();
6 | this.afterEachCalled = new Set();
7 | this.context = context || { currentTest: { title: 'mockname' } };
8 | }
9 |
10 | beforeEach(fn) {
11 | this.beforeEachCalled.add(fn);
12 | fn.call(this.context, undefined);
13 | }
14 |
15 | afterEach(fn) {
16 | this.afterEachCalled.add(fn);
17 | fn.call(this.context, undefined);
18 | }
19 | }
20 |
21 | describe('Unit | Test Helpers | mocha', function () {
22 | it('should exist', function () {
23 | expect(setupPolly).to.be.a('function');
24 | expect(setupPolly.beforeEach).to.be.a('function');
25 | expect(setupPolly.afterEach).to.be.a('function');
26 | });
27 |
28 | it('should invoke beforeEach and afterEach', function () {
29 | const stub = new Sandbox();
30 |
31 | setupPolly({}, stub);
32 | expect(stub.beforeEachCalled.size).to.equal(1);
33 | expect(stub.afterEachCalled.size).to.equal(1);
34 | });
35 |
36 | it('should create a polly property and set recordingName', function () {
37 | const stub = new Sandbox();
38 |
39 | setupPolly({}, stub);
40 | expect(stub.context.polly).to.be.a('object');
41 | expect(stub.context.polly.recordingName).to.equal('mockname');
42 | });
43 |
44 | it('should concat title if test is deeply nested', function () {
45 | const stub = new Sandbox({
46 | currentTest: {
47 | title: 'foo',
48 | parent: {
49 | title: 'bar',
50 | parent: {
51 | title: 'baz'
52 | }
53 | }
54 | }
55 | });
56 |
57 | setupPolly({}, stub);
58 | expect(stub.context.polly.recordingName).to.equal('baz/bar/foo');
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/deferred-promise-test.js:
--------------------------------------------------------------------------------
1 | import defer from '../../../src/utils/deferred-promise';
2 |
3 | describe('Unit | Utils | DeferredPromise', function () {
4 | it('should exist', function () {
5 | expect(defer).to.be.a('function');
6 | expect(defer().resolve).to.be.a('function');
7 | expect(defer().reject).to.be.a('function');
8 | });
9 |
10 | it('should resolve when calling .resolve()', async function () {
11 | const promise = defer();
12 |
13 | promise.resolve(42);
14 | expect(await promise).to.equal(42);
15 | });
16 |
17 | it('should reject when calling .reject()', async function () {
18 | const promise = defer();
19 |
20 | promise.reject(new Error('42'));
21 |
22 | try {
23 | await promise;
24 | } catch (error) {
25 | expect(error).to.be.an('error');
26 | expect(error.message).to.equal('42');
27 | }
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/guid-for-recording-test.js:
--------------------------------------------------------------------------------
1 | import guidForRecording from '../../../src/utils/guid-for-recording';
2 |
3 | describe('Unit | Utils | guidForRecording', function () {
4 | it('should exist', function () {
5 | expect(guidForRecording).to.be.a('function');
6 | });
7 |
8 | it('should remove illegal file system characters', function () {
9 | expect(guidForRecording(`'?<>\\:*|"`)).to.equal('_3218500777');
10 | });
11 |
12 | it('should create a guid for each segment of the name', function () {
13 | const name = guidForRecording(`foo!/bar%/baz..`);
14 |
15 | expect(name).to.equal('foo_2152783170/bar_567945773/baz_1682401886');
16 | });
17 |
18 | it('should trim name to 100 characters', function () {
19 | const name = guidForRecording(new Array(200).fill('A').join(''));
20 |
21 | expect(name.length).to.be.lte(100);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/merge-configs-test.js:
--------------------------------------------------------------------------------
1 | import mergeConfigs from '../../../src/utils/merge-configs';
2 |
3 | describe('Unit | Utils | mergeConfigs', function () {
4 | it('should exist', function () {
5 | expect(mergeConfigs).to.be.a('function');
6 | });
7 |
8 | it('should not deep merge context objects', async function () {
9 | const context = {};
10 | const config = mergeConfigs(
11 | { fetch: {}, xhr: {} },
12 | { fetch: { context } },
13 | { xhr: { context } },
14 | { fetch: {}, xhr: {} }
15 | );
16 |
17 | expect(config.fetch.context).to.equal(context);
18 | expect(config.xhr.context).to.equal(context);
19 | });
20 |
21 | it('should not deep merge arrays', async function () {
22 | const array = [{}];
23 | const config = mergeConfigs({ array: [] }, { array });
24 |
25 | expect(config.array).to.equal(array);
26 | expect(config.array[0]).to.equal(array[0]);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/parse-url-test.js:
--------------------------------------------------------------------------------
1 | import parseUrl from '../../../src/utils/parse-url';
2 |
3 | describe('Unit | Utils | parseUrl', function () {
4 | it('should exist', function () {
5 | expect(parseUrl).to.be.a('function');
6 | });
7 |
8 | it('should exactly match passed urls', function () {
9 | [
10 | '/movies/1',
11 | 'http://netflix.com/movies/1',
12 | 'http://netflix.com/movies/1?sort=title&dir=asc'
13 | ].forEach((url) => expect(parseUrl(url).href).to.equal(url));
14 | });
15 |
16 | it('should passthrough arguments to url-parse', function () {
17 | // Passing true tells url-parse to transform the querystring into an object
18 | expect(parseUrl('/movies/1?sort=title&dir=asc', true).query).to.deep.equal({
19 | sort: 'title',
20 | dir: 'asc'
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/remove-host-from-url-test.js:
--------------------------------------------------------------------------------
1 | import { URL } from '@pollyjs/utils';
2 |
3 | import removeHost from '../../../src/utils/remove-host-from-url';
4 |
5 | describe('Unit | Utils | removeHostFromUrl', function () {
6 | it('should exist', function () {
7 | expect(removeHost).to.be.a('function');
8 | });
9 |
10 | it('should remove hostname', function () {
11 | const url = removeHost(new URL('http://foo.com/bar/baz/'));
12 |
13 | expect(url.toString()).to.equal('/bar/baz/');
14 | });
15 |
16 | it('should remove hostname without a tld', function () {
17 | const url = removeHost(new URL('http://foo/bar/baz/'));
18 |
19 | expect(url.toString()).to.equal('/bar/baz/');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/core/tests/unit/utils/timing-test.js:
--------------------------------------------------------------------------------
1 | import Timing from '../../../src/utils/timing';
2 |
3 | function fixedTest(ms) {
4 | it(`should handle ${ms}ms`, async function () {
5 | // Fail the test if it exceeds ms + 10ms buffer
6 | this.timeout(ms + 50);
7 |
8 | const promise = Timing.fixed(ms)();
9 | let resolved = false;
10 |
11 | promise.then(() => (resolved = true));
12 |
13 | if (ms) {
14 | setTimeout(() => expect(resolved).to.be.false, ms / 2);
15 | }
16 | setTimeout(() => expect(resolved).to.be.true, ms + 1);
17 |
18 | await promise;
19 | });
20 | }
21 |
22 | function relativeTest(ratio) {
23 | const timeout = ratio * 100;
24 |
25 | it(`should handle a ratio of ${ratio}`, async function () {
26 | // Fail the test if it exceeds timeout + 10ms buffer
27 | this.timeout(timeout + 50);
28 |
29 | const promise = Timing.relative(ratio)(100);
30 | let resolved = false;
31 |
32 | promise.then(() => (resolved = true));
33 |
34 | if (timeout) {
35 | setTimeout(() => expect(resolved).to.be.false, timeout / 2);
36 | }
37 | setTimeout(() => expect(resolved).to.be.true, timeout + 1);
38 |
39 | await promise;
40 | });
41 | }
42 |
43 | describe('Unit | Utils | Timing', function () {
44 | it('should exist', function () {
45 | expect(Timing).to.be.a('object');
46 | expect(Timing.fixed).to.be.a('function');
47 | expect(Timing.relative).to.be.a('function');
48 | });
49 |
50 | describe('fixed', function () {
51 | fixedTest(0);
52 | fixedTest(50);
53 | fixedTest(100);
54 | });
55 |
56 | describe('relative', function () {
57 | relativeTest(0);
58 | relativeTest(0.5);
59 | relativeTest(1.0);
60 | relativeTest(2.0);
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.hbs]
16 | insert_final_newline = false
17 |
18 | [*.{diff,md}]
19 | trim_trailing_whitespace = false
20 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.eslintignore:
--------------------------------------------------------------------------------
1 | # unconventional js
2 | /blueprints/*/files/
3 | /vendor/
4 |
5 | # compiled output
6 | /dist/
7 | /tmp/
8 |
9 | # dependencies
10 | /bower_components/
11 | /node_modules/
12 |
13 | # misc
14 | /coverage/
15 | !.*
16 | .*/
17 | .eslintcache
18 |
19 | # ember-try
20 | /.node_modules.ember-try/
21 | /bower.json.ember-try
22 | /package.json.ember-try
23 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | root: true,
5 | parser: '@babel/eslint-parser',
6 | parserOptions: {
7 | ecmaVersion: 2018,
8 | sourceType: 'module',
9 | requireConfigFile: false,
10 | ecmaFeatures: {
11 | legacyDecorators: true
12 | }
13 | },
14 | plugins: ['ember'],
15 | extends: [
16 | 'eslint:recommended',
17 | 'plugin:ember/recommended',
18 | 'plugin:prettier/recommended'
19 | ],
20 | env: {
21 | browser: true,
22 | node: true
23 | },
24 | rules: {},
25 | overrides: [
26 | // node files
27 | {
28 | files: [
29 | './.eslintrc.js',
30 | './.prettierrc.js',
31 | './.template-lintrc.js',
32 | './ember-cli-build.js',
33 | './index.js',
34 | './testem.js',
35 | './blueprints/*/index.js',
36 | './config/**/*.js',
37 | './tests/dummy/config/**/*.js'
38 | ],
39 | parserOptions: {
40 | sourceType: 'script'
41 | },
42 | env: {
43 | browser: false,
44 | node: true
45 | },
46 | plugins: ['node'],
47 | extends: ['plugin:node/recommended']
48 | },
49 | {
50 | // Test files:
51 | files: ['tests/**/*-test.{js,ts}'],
52 | extends: ['plugin:qunit/recommended']
53 | }
54 | ]
55 | };
56 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist/
5 | /tmp/
6 |
7 | # dependencies
8 | /bower_components/
9 | /node_modules/
10 |
11 | # misc
12 | /.env*
13 | /.pnp*
14 | /.sass-cache
15 | /.eslintcache
16 | /connect.lock
17 | /coverage/
18 | /libpeerconnection.log
19 | /npm-debug.log*
20 | /testem.log
21 | /yarn-error.log
22 |
23 | # ember-try
24 | /.node_modules.ember-try/
25 | /bower.json.ember-try
26 | /package.json.ember-try
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.npmignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /tmp/
4 |
5 | # dependencies
6 | /bower_components/
7 |
8 | # misc
9 | /.bowerrc
10 | /.editorconfig
11 | /.ember-cli
12 | /.env*
13 | /.eslintcache
14 | /.eslintignore
15 | /.eslintrc.js
16 | /.git/
17 | /.gitignore
18 | /.prettierignore
19 | /.prettierrc.js
20 | /.template-lintrc.js
21 | /.travis.yml
22 | /.watchmanconfig
23 | /bower.json
24 | /config/ember-try.js
25 | /CONTRIBUTING.md
26 | /ember-cli-build.js
27 | /testem.js
28 | /tests/
29 | /yarn-error.log
30 | /yarn.lock
31 | .gitkeep
32 |
33 | # ember-try
34 | /.node_modules.ember-try/
35 | /bower.json.ember-try
36 | /package.json.ember-try
37 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.prettierignore:
--------------------------------------------------------------------------------
1 | # unconventional js
2 | /blueprints/*/files/
3 | /vendor/
4 |
5 | # compiled output
6 | /dist/
7 | /tmp/
8 |
9 | # dependencies
10 | /bower_components/
11 | /node_modules/
12 |
13 | # misc
14 | /coverage/
15 | !.*
16 | .eslintcache
17 |
18 | # ember-try
19 | /.node_modules.ember-try/
20 | /bower.json.ember-try
21 | /package.json.ember-try
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.prettierrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | singleQuote: true,
5 | trailingComma: 'none'
6 | };
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.template-lintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | extends: 'recommended',
5 | };
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp", "dist", "tests/recordings"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/addon/-private/preconfigure.js:
--------------------------------------------------------------------------------
1 | import { Polly } from '@pollyjs/core';
2 | import XHRAdapter from '@pollyjs/adapter-xhr';
3 | import FetchAdapter from '@pollyjs/adapter-fetch';
4 | import RESTPersister from '@pollyjs/persister-rest';
5 | import LocalStoragePersister from '@pollyjs/persister-local-storage';
6 |
7 | Polly.register(XHRAdapter);
8 | Polly.register(FetchAdapter);
9 | Polly.register(RESTPersister);
10 | Polly.register(LocalStoragePersister);
11 |
12 | Polly.on('create', (polly) => {
13 | polly.configure({
14 | adapters: ['xhr', 'fetch'],
15 | persister: 'rest',
16 | /**
17 | * @pollyjs/ember mounts the express middleware onto to the running
18 | * testem and ember-cli express server and a relative host (an empty `host`)
19 | * is preferred here. Can be overridden at runtime in cases where the
20 | * Polly server is externally hosted.
21 | */
22 | persisterOptions: { rest: { host: '' } }
23 | });
24 | });
25 |
26 | export default Polly;
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/blueprints/@pollyjs/ember/files/config/polly.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | 'use strict';
4 |
5 | module.exports = function (env) {
6 | return {
7 | enabled: env !== 'production',
8 | server: {
9 | apiNamespace: '/polly',
10 | recordingsDir: 'recordings'
11 | }
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/blueprints/@pollyjs/ember/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | description: 'Setup @pollyjs/ember',
5 | normalizeEntityName() {},
6 | afterInstall() {
7 | return this.addPackagesToProject([
8 | { name: '@pollyjs/adapter-fetch' },
9 | { name: '@pollyjs/adapter-xhr' },
10 | { name: '@pollyjs/core' },
11 | { name: '@pollyjs/node-server' },
12 | { name: '@pollyjs/persister-local-storage' },
13 | { name: '@pollyjs/persister-rest' }
14 | ]);
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function () {
4 | return {};
5 | };
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/config/polly.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | 'use strict';
4 |
5 | module.exports = function (env) {
6 | // See: https://netflix.github.io/pollyjs/#/frameworks/ember-cli?id=configuration
7 | return {
8 | enabled: env !== 'production',
9 | server: {
10 | apiNamespace: '/polly',
11 | recordingsDir: 'recordings'
12 | }
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
4 |
5 | module.exports = function (defaults) {
6 | let app = new EmberAddon(defaults, {
7 | // Add options here
8 | });
9 |
10 | /*
11 | This build file specifies the options for the dummy test app of this
12 | addon, located in `/tests/dummy`
13 | This build file does *not* influence how the addon or the app using it
14 | behave. You most likely want to be modifying `./index.js` or app's build file
15 | */
16 |
17 | const { maybeEmbroider } = require('@embroider/test-setup');
18 | return maybeEmbroider(app, {
19 | skipBabel: [
20 | {
21 | package: 'qunit'
22 | }
23 | ]
24 | });
25 | };
26 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/testem.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | test_page: 'tests/index.html?hidepassed',
5 | disable_watching: true,
6 | launch_in_ci: ['Chrome'],
7 | launch_in_dev: ['Chrome'],
8 | browser_start_timeout: 120,
9 | browser_args: {
10 | Chrome: {
11 | ci: [
12 | // --no-sandbox is needed when running Chrome inside a container
13 | process.env.CI ? '--no-sandbox' : null,
14 | '--headless',
15 | '--disable-dev-shm-usage',
16 | '--disable-software-rasterizer',
17 | '--mute-audio',
18 | '--remote-debugging-port=0',
19 | '--window-size=1440,900'
20 | ].filter(Boolean)
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Application from '@ember/application';
2 | import Resolver from 'ember-resolver';
3 | import loadInitializers from 'ember-load-initializers';
4 | import config from 'dummy/config/environment';
5 |
6 | export default class App extends Application {
7 | modulePrefix = config.modulePrefix;
8 | podModulePrefix = config.podModulePrefix;
9 | Resolver = Resolver;
10 | }
11 |
12 | loadInitializers(App, config.modulePrefix);
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/components/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/controllers/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/helpers/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy
7 |
8 |
9 |
10 | {{content-for "head"}}
11 |
12 |
13 |
14 |
15 | {{content-for "head-footer"}}
16 |
17 |
18 | {{content-for "body"}}
19 |
20 |
21 |
22 |
23 | {{content-for "body-footer"}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/models/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import EmberRouter from '@ember/routing/router';
2 | import config from 'dummy/config/environment';
3 |
4 | export default class Router extends EmberRouter {
5 | location = config.locationType;
6 | rootURL = config.rootURL;
7 | }
8 |
9 | Router.map(function () {});
10 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/routes/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/dummy/app/styles/app.css
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 | {{page-title "Dummy"}}
2 |
3 | Welcome to Ember
4 |
5 | {{outlet}}
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/config/ember-cli-update.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": "1.0.0",
3 | "packages": [
4 | {
5 | "name": "ember-cli",
6 | "version": "3.28.4",
7 | "blueprints": [
8 | {
9 | "name": "addon",
10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output",
11 | "codemodsSource": "ember-addon-codemods-manifest@1",
12 | "isBaseBlueprint": true,
13 | "options": []
14 | }
15 | ]
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (environment) {
4 | let ENV = {
5 | modulePrefix: 'dummy',
6 | environment,
7 | rootURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
13 | },
14 | EXTEND_PROTOTYPES: {
15 | // Prevent Ember Data from overriding Date.parse.
16 | Date: false
17 | }
18 | },
19 |
20 | APP: {
21 | // Here you can pass flags/options to your application instance
22 | // when it is created
23 | }
24 | };
25 |
26 | if (environment === 'development') {
27 | // ENV.APP.LOG_RESOLVER = true;
28 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
29 | // ENV.APP.LOG_TRANSITIONS = true;
30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
31 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
32 | }
33 |
34 | if (environment === 'test') {
35 | // Testem prefers this...
36 | ENV.locationType = 'none';
37 |
38 | // keep test console output quieter
39 | ENV.APP.LOG_ACTIVE_GENERATION = false;
40 | ENV.APP.LOG_VIEW_LOOKUPS = false;
41 |
42 | ENV.APP.rootElement = '#ember-testing';
43 | ENV.APP.autoboot = false;
44 | }
45 |
46 | if (environment === 'production') {
47 | // here you can enable a production-specific feature
48 | }
49 |
50 | return ENV;
51 | };
52 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/config/optional-features.json:
--------------------------------------------------------------------------------
1 | {
2 | "application-template-wrapper": false,
3 | "default-async-observers": true,
4 | "jquery-integration": false,
5 | "template-only-glimmer-components": true
6 | }
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/config/targets.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const browsers = [
4 | 'last 1 Chrome versions',
5 | 'last 1 Firefox versions',
6 | 'last 1 Safari versions'
7 | ];
8 |
9 | // Ember's browser support policy is changing, and IE11 support will end in
10 | // v4.0 onwards.
11 | //
12 | // See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy
13 | //
14 | // If you need IE11 support on a version of Ember that still offers support
15 | // for it, uncomment the code block below.
16 | //
17 | // const isCI = Boolean(process.env.CI);
18 | // const isProduction = process.env.EMBER_ENV === 'production';
19 | //
20 | // if (isCI || isProduction) {
21 | // browsers.push('ie 11');
22 | // }
23 |
24 | module.exports = {
25 | browsers
26 | };
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/helpers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/helpers/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for "head"}}
11 | {{content-for "test-head"}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for "head-footer"}}
18 | {{content-for "test-head-footer"}}
19 |
20 |
21 | {{content-for "body"}}
22 | {{content-for "test-body"}}
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {{content-for "body-footer"}}
38 | {{content-for "test-body-footer"}}
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/integration/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/tests/integration/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import Application from 'dummy/app';
2 | import config from 'dummy/config/environment';
3 | import * as QUnit from 'qunit';
4 | import { setApplication } from '@ember/test-helpers';
5 | import { setup } from 'qunit-dom';
6 | import { start } from 'ember-qunit';
7 |
8 | setApplication(Application.create(config.APP));
9 |
10 | setup(QUnit.assert);
11 |
12 | start();
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/ember/vendor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/packages/@pollyjs/ember/vendor/.gitkeep
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/node-server",
3 | "version": "6.0.6",
4 | "description": "Standalone node server and express integration for @pollyjs",
5 | "main": "dist/cjs/pollyjs-node-server.js",
6 | "module": "dist/es/pollyjs-node-server.js",
7 | "types": "types.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "types.d.ts"
12 | ],
13 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/node-server",
14 | "license": "Apache-2.0",
15 | "contributors": [
16 | {
17 | "name": "Jason Mitchell",
18 | "email": "jason.mitchell.w@gmail.com"
19 | },
20 | {
21 | "name": "Offir Golan",
22 | "email": "offirgolan@gmail.com"
23 | }
24 | ],
25 | "keywords": [
26 | "polly",
27 | "pollyjs",
28 | "server",
29 | "record",
30 | "replay",
31 | "express"
32 | ],
33 | "publishConfig": {
34 | "access": "public"
35 | },
36 | "scripts": {
37 | "build": "rollup -c",
38 | "watch": "yarn build -w",
39 | "watch-all": "yarn build"
40 | },
41 | "dependencies": {
42 | "@pollyjs/utils": "^6.0.6",
43 | "body-parser": "^1.19.0",
44 | "cors": "^2.8.5",
45 | "express": "^4.17.1",
46 | "fs-extra": "^10.0.0",
47 | "http-graceful-shutdown": "^3.1.5",
48 | "morgan": "^1.10.0",
49 | "nocache": "^3.0.1"
50 | },
51 | "devDependencies": {
52 | "rollup": "^1.14.6"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/rollup.config.js:
--------------------------------------------------------------------------------
1 | import createNodeConfig from '../../../scripts/rollup/node.config';
2 |
3 | export default createNodeConfig({
4 | external: ['path']
5 | });
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/src/api.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | import fs from 'fs-extra';
4 | import { assert } from '@pollyjs/utils';
5 |
6 | export default class API {
7 | constructor(options = {}) {
8 | const { recordingsDir } = options;
9 |
10 | assert(
11 | `Invalid recordings directory provided. Expected string, received: "${typeof recordingsDir}".`,
12 | typeof recordingsDir === 'string'
13 | );
14 |
15 | this.recordingsDir = recordingsDir;
16 | }
17 |
18 | getRecording(recording) {
19 | const recordingFilename = this.filenameFor(recording);
20 |
21 | if (fs.existsSync(recordingFilename)) {
22 | return this.respond(200, fs.readJsonSync(recordingFilename));
23 | }
24 |
25 | return this.respond(204);
26 | }
27 |
28 | saveRecording(recording, data) {
29 | fs.outputJsonSync(this.filenameFor(recording), data, {
30 | spaces: 2
31 | });
32 |
33 | return this.respond(201);
34 | }
35 |
36 | deleteRecording(recording) {
37 | const recordingFilename = this.filenameFor(recording);
38 |
39 | if (fs.existsSync(recordingFilename)) {
40 | fs.removeSync(recordingFilename);
41 | }
42 |
43 | return this.respond(200);
44 | }
45 |
46 | filenameFor(recording) {
47 | return path.join(this.recordingsDir, recording, 'recording.har');
48 | }
49 |
50 | respond(status, body) {
51 | return { status, body };
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/src/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | port: 3000,
3 | quiet: false,
4 | recordingSizeLimit: '50mb',
5 | recordingsDir: 'recordings',
6 | apiNamespace: '/polly'
7 | };
8 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/src/express/register-api.js:
--------------------------------------------------------------------------------
1 | import bodyParser from 'body-parser';
2 | import express from 'express';
3 | import nocache from 'nocache';
4 |
5 | import API from '../api';
6 | import DefaultConfig from '../config';
7 |
8 | function prependSlash(slash = '') {
9 | if (slash.startsWith('/')) {
10 | return slash;
11 | }
12 |
13 | return `/${slash}`;
14 | }
15 |
16 | export default function registerAPI(app, config) {
17 | config = { ...DefaultConfig, ...config };
18 | config.apiNamespace = prependSlash(config.apiNamespace);
19 |
20 | const router = express.Router();
21 | const api = new API({ recordingsDir: config.recordingsDir });
22 |
23 | router.use(nocache());
24 |
25 | router.get('/:recording', function (req, res) {
26 | const { recording } = req.params;
27 | const { status, body } = api.getRecording(recording);
28 |
29 | res.status(status);
30 |
31 | if (status === 200) {
32 | res.json(body);
33 | } else {
34 | res.end();
35 | }
36 | });
37 |
38 | router.post(
39 | '/:recording',
40 | bodyParser.json({ limit: config.recordingSizeLimit }),
41 | function (req, res) {
42 | const { recording } = req.params;
43 | const { status, body } = api.saveRecording(recording, req.body);
44 |
45 | res.status(status).send(body);
46 | }
47 | );
48 |
49 | router.delete('/:recording', function (req, res) {
50 | const { recording } = req.params;
51 | const { status, body } = api.deleteRecording(recording);
52 |
53 | res.status(status).send(body);
54 | });
55 |
56 | app.use(config.apiNamespace, router);
57 | }
58 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as API } from './api';
2 | export { default as Server } from './server';
3 | export { default as Defaults } from './config';
4 | export { default as registerExpressAPI } from './express/register-api';
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/src/server.js:
--------------------------------------------------------------------------------
1 | /* global process */
2 |
3 | import cors from 'cors';
4 | import morgan from 'morgan';
5 | import express from 'express';
6 | import gracefulShutdown from 'http-graceful-shutdown';
7 |
8 | import registerAPI from './express/register-api';
9 | import DefaultConfig from './config';
10 |
11 | export default class Server {
12 | constructor(config = {}) {
13 | this.config = { ...DefaultConfig, ...config };
14 | this.app = express();
15 | this.app.use(cors(this.config.corsOptions));
16 |
17 | if (!this.config.quiet) {
18 | this.app.use(morgan('dev'));
19 | }
20 |
21 | // Return 200 on root GET & HEAD to pass health checks
22 | this.app.get('/', (req, res) => res.sendStatus(200));
23 | this.app.head('/', (req, res) => res.sendStatus(200));
24 |
25 | registerAPI(this.app, {
26 | recordingsDir: this.config.recordingsDir,
27 | apiNamespace: this.config.apiNamespace
28 | });
29 | }
30 |
31 | listen(port, host) {
32 | if (this.server) {
33 | return;
34 | }
35 |
36 | port = port || this.config.port;
37 | host = host || this.config.host;
38 |
39 | this.server = this.app
40 | .listen(port, host)
41 | .on('listening', () => {
42 | if (!this.config.quiet) {
43 | console.log(`Listening on http://${host || 'localhost'}:${port}/\n`);
44 | }
45 | })
46 | .on('error', (e) => {
47 | if (e.code === 'EADDRINUSE') {
48 | console.error(`Port ${port} already in use.`);
49 | process.exit(1);
50 | } else {
51 | console.error(e);
52 | }
53 | });
54 |
55 | gracefulShutdown(this.server);
56 |
57 | return this.server;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/@pollyjs/node-server/types.d.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 | import * as express from 'express';
3 | import * as cors from 'cors';
4 |
5 | export interface Config {
6 | port: number;
7 | quiet: boolean;
8 | recordingSizeLimit: string;
9 | recordingsDir: string;
10 | apiNamespace: string;
11 | }
12 |
13 | export interface ServerConfig extends Config {
14 | corsOptions?: cors.CorsOptions | undefined;
15 | }
16 |
17 | export const Defaults: Config;
18 |
19 | export interface APIResponse {
20 | status: number;
21 | body?: any;
22 | }
23 |
24 | export class API {
25 | constructor(options: Pick);
26 | getRecordings(recording: string): APIResponse;
27 | saveRecording(recording: string, data: any): APIResponse;
28 | deleteRecording(recording: string): APIResponse;
29 | filenameFor(recording: string): string;
30 | respond(status: number, data?: any): APIResponse;
31 | }
32 |
33 | export class Server {
34 | config: ServerConfig;
35 | app: express.Express;
36 | server?: http.Server | undefined;
37 |
38 | constructor(options?: Partial);
39 | listen(port?: number, host?: string): http.Server;
40 | }
41 |
42 | export function registerExpressAPI(
43 | app: express.Express,
44 | config: Partial
45 | ): void;
46 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: false,
4 | node: true
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/persister-fs",
3 | "version": "6.0.6",
4 | "description": "File system persister for @pollyjs",
5 | "main": "dist/cjs/pollyjs-persister-fs.js",
6 | "module": "dist/es/pollyjs-persister-fs.js",
7 | "types": "types.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "types.d.ts"
12 | ],
13 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/persister-fs",
14 | "license": "Apache-2.0",
15 | "contributors": [
16 | {
17 | "name": "Jason Mitchell",
18 | "email": "jason.mitchell.w@gmail.com"
19 | },
20 | {
21 | "name": "Offir Golan",
22 | "email": "offirgolan@gmail.com"
23 | }
24 | ],
25 | "keywords": [
26 | "polly",
27 | "pollyjs",
28 | "record",
29 | "replay",
30 | "fs",
31 | "file"
32 | ],
33 | "publishConfig": {
34 | "access": "public"
35 | },
36 | "scripts": {
37 | "build": "rollup -c",
38 | "build:watch": "yarn build -w",
39 | "test:build": "rollup -c rollup.config.test.js",
40 | "test:build:watch": "rollup -c rollup.config.test.js -w",
41 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
42 | },
43 | "dependencies": {
44 | "@pollyjs/node-server": "^6.0.6",
45 | "@pollyjs/persister": "^6.0.6"
46 | },
47 | "devDependencies": {
48 | "fixturify": "^2.1.1",
49 | "rimraf": "^3.0.2",
50 | "rollup": "^1.14.6"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/rollup.config.js:
--------------------------------------------------------------------------------
1 | import createNodeConfig from '../../../scripts/rollup/node.config';
2 |
3 | export default createNodeConfig();
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 |
3 | export default createNodeTestConfig({
4 | external: ['rimraf', 'fixturify']
5 | });
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/src/index.js:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 | import { API, Defaults } from '@pollyjs/node-server';
3 |
4 | const { parse } = JSON;
5 |
6 | export default class FSPersister extends Persister {
7 | constructor() {
8 | super(...arguments);
9 | this.api = new API(this.options);
10 | }
11 |
12 | static get id() {
13 | return 'fs';
14 | }
15 |
16 | get defaultOptions() {
17 | return {
18 | recordingsDir: Defaults.recordingsDir
19 | };
20 | }
21 |
22 | onFindRecording(recordingId) {
23 | return this.api.getRecording(recordingId).body || null;
24 | }
25 |
26 | onSaveRecording(recordingId, data) {
27 | /*
28 | Pass the data through the base persister's stringify method so
29 | the output will be consistent with the rest of the persisters.
30 | */
31 | this.api.saveRecording(recordingId, parse(this.stringify(data)));
32 | }
33 |
34 | onDeleteRecording(recordingId) {
35 | this.api.deleteRecording(recordingId);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-fs/types.d.ts:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | export default class FSPersister extends Persister<{
4 | recordingsDir?: string;
5 | }> {}
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-in-memory/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/persister-in-memory",
3 | "version": "6.0.6",
4 | "private": true,
5 | "description": "In memory storage persister for @pollyjs",
6 | "main": "dist/cjs/pollyjs-persister-in-memory.js",
7 | "module": "dist/es/pollyjs-persister-in-memory.js",
8 | "browser": "dist/umd/pollyjs-persister-in-memory.js",
9 | "types": "types.d.ts",
10 | "files": [
11 | "src",
12 | "dist",
13 | "types.d.ts"
14 | ],
15 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/persister-in-memory",
16 | "license": "Apache-2.0",
17 | "contributors": [
18 | {
19 | "name": "Jason Mitchell",
20 | "email": "jason.mitchell.w@gmail.com"
21 | },
22 | {
23 | "name": "Offir Golan",
24 | "email": "offirgolan@gmail.com"
25 | }
26 | ],
27 | "keywords": [
28 | "polly",
29 | "pollyjs",
30 | "record",
31 | "replay",
32 | "persister"
33 | ],
34 | "publishConfig": {
35 | "access": "private"
36 | },
37 | "scripts": {
38 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
39 | "test:build": "rollup -c rollup.config.test.js",
40 | "test:build:watch": "rollup -c rollup.config.test.js -w",
41 | "build:watch": "yarn build -w",
42 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
43 | },
44 | "dependencies": {
45 | "@pollyjs/persister": "^6.0.6"
46 | },
47 | "devDependencies": {
48 | "rollup": "^1.14.6"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-in-memory/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
3 |
4 | export default [createNodeTestConfig(), createBrowserTestConfig()];
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-in-memory/src/index.js:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | const store = new Map();
4 |
5 | export default class InMemoryPersister extends Persister {
6 | static get id() {
7 | return 'in-memory-persister';
8 | }
9 |
10 | onFindRecording(recordingId) {
11 | return store.get(recordingId) || null;
12 | }
13 |
14 | onSaveRecording(recordingId, data) {
15 | store.set(recordingId, data);
16 | }
17 |
18 | onDeleteRecording(recordingId) {
19 | store.delete(recordingId);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-in-memory/types.d.ts:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | export default class InMemoryPersister extends Persister {}
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-local-storage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/persister-local-storage",
3 | "version": "6.0.6",
4 | "description": "Local storage persister for @pollyjs",
5 | "main": "dist/cjs/pollyjs-persister-local-storage.js",
6 | "module": "dist/es/pollyjs-persister-local-storage.js",
7 | "browser": "dist/umd/pollyjs-persister-local-storage.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/persister-local-storage",
15 | "license": "Apache-2.0",
16 | "contributors": [
17 | {
18 | "name": "Jason Mitchell",
19 | "email": "jason.mitchell.w@gmail.com"
20 | },
21 | {
22 | "name": "Offir Golan",
23 | "email": "offirgolan@gmail.com"
24 | }
25 | ],
26 | "keywords": [
27 | "polly",
28 | "pollyjs",
29 | "record",
30 | "replay",
31 | "local-storage",
32 | "persister"
33 | ],
34 | "publishConfig": {
35 | "access": "public"
36 | },
37 | "scripts": {
38 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
39 | "build:watch": "yarn build -w",
40 | "test:build": "rollup -c rollup.config.test.js",
41 | "test:build:watch": "rollup -c rollup.config.test.js -w",
42 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
43 | },
44 | "dependencies": {
45 | "@pollyjs/persister": "^6.0.6"
46 | },
47 | "devDependencies": {
48 | "rollup": "^1.14.6"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-local-storage/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
2 |
3 | export default createBrowserTestConfig();
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-local-storage/src/index.js:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | const { parse } = JSON;
4 |
5 | export default class LocalStoragePersister extends Persister {
6 | static get id() {
7 | return 'local-storage';
8 | }
9 |
10 | get defaultOptions() {
11 | return {
12 | key: 'pollyjs',
13 | context: global
14 | };
15 | }
16 |
17 | get localStorage() {
18 | const { context } = this.options;
19 |
20 | this.assert(
21 | `Could not find "localStorage" on the given context "${context}".`,
22 | context && context.localStorage
23 | );
24 |
25 | return context.localStorage;
26 | }
27 |
28 | get db() {
29 | const items = this.localStorage.getItem(this.options.key);
30 |
31 | return items ? parse(items) : {};
32 | }
33 |
34 | set db(db) {
35 | this.localStorage.setItem(this.options.key, this.stringify(db));
36 | }
37 |
38 | onFindRecording(recordingId) {
39 | return this.db[recordingId] || null;
40 | }
41 |
42 | onSaveRecording(recordingId, data) {
43 | const { db } = this;
44 |
45 | db[recordingId] = data;
46 | this.db = db;
47 | }
48 |
49 | onDeleteRecording(recordingId) {
50 | const { db } = this;
51 |
52 | delete db[recordingId];
53 | this.db = db;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-local-storage/types.d.ts:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | export default class LocalStoragePersister extends Persister<{
4 | context?: any;
5 | key?: string;
6 | }> {}
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-rest/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/persister-rest",
3 | "version": "6.0.6",
4 | "description": "REST persister for @pollyjs",
5 | "main": "dist/cjs/pollyjs-persister-rest.js",
6 | "module": "dist/es/pollyjs-persister-rest.js",
7 | "browser": "dist/umd/pollyjs-persister-rest.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/persister-rest",
15 | "license": "Apache-2.0",
16 | "contributors": [
17 | {
18 | "name": "Jason Mitchell",
19 | "email": "jason.mitchell.w@gmail.com"
20 | },
21 | {
22 | "name": "Offir Golan",
23 | "email": "offirgolan@gmail.com"
24 | }
25 | ],
26 | "keywords": [
27 | "polly",
28 | "pollyjs",
29 | "record",
30 | "replay",
31 | "rest",
32 | "persister"
33 | ],
34 | "publishConfig": {
35 | "access": "public"
36 | },
37 | "scripts": {
38 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
39 | "test:build": "rollup -c rollup.config.test.js",
40 | "test:build:watch": "rollup -c rollup.config.test.js -w",
41 | "build:watch": "yarn build -w",
42 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
43 | },
44 | "dependencies": {
45 | "@pollyjs/persister": "^6.0.6",
46 | "@pollyjs/utils": "^6.0.6"
47 | },
48 | "devDependencies": {
49 | "rollup": "^1.14.6"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-rest/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
2 |
3 | export default createBrowserTestConfig();
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-rest/src/ajax.js:
--------------------------------------------------------------------------------
1 | const { keys } = Object;
2 | const REQUEST_ASYNC =
3 | !('navigator' in global) || !/PhantomJS/.test(global.navigator.userAgent);
4 | const NativeXMLHttpRequest = global.XMLHttpRequest;
5 |
6 | export default function ajax(url, options = {}) {
7 | return new Promise((resolve, reject) => {
8 | const xhr = new NativeXMLHttpRequest();
9 |
10 | xhr.open(options.method || 'GET', url, REQUEST_ASYNC);
11 |
12 | keys(options.headers || {}).forEach((k) =>
13 | xhr.setRequestHeader(k, options.headers[k])
14 | );
15 |
16 | xhr.send(options.body);
17 |
18 | if (REQUEST_ASYNC) {
19 | xhr.onreadystatechange = () => {
20 | if (xhr.readyState === NativeXMLHttpRequest.DONE) {
21 | handleResponse(xhr, resolve, reject);
22 | }
23 | };
24 |
25 | xhr.onerror = () => reject(xhr);
26 | } else {
27 | handleResponse(xhr, resolve, reject);
28 | }
29 | });
30 | }
31 |
32 | function handleResponse(xhr, resolve, reject) {
33 | let body = xhr.response || xhr.responseText;
34 |
35 | if (body && typeof body === 'string') {
36 | try {
37 | body = JSON.parse(body);
38 | } catch (e) {
39 | if (!(e instanceof SyntaxError)) {
40 | console.error(e);
41 | }
42 | }
43 | }
44 |
45 | return xhr.status >= 200 && xhr.status < 300
46 | ? resolve({ body, xhr })
47 | : reject(xhr);
48 | }
49 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-rest/src/index.js:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 | import { buildUrl } from '@pollyjs/utils';
3 |
4 | import ajax from './ajax';
5 |
6 | export default class RestPersister extends Persister {
7 | static get id() {
8 | return 'rest';
9 | }
10 |
11 | get defaultOptions() {
12 | return {
13 | host: 'http://localhost:3000',
14 | apiNamespace: '/polly'
15 | };
16 | }
17 |
18 | ajax(url, ...args) {
19 | const { host, apiNamespace } = this.options;
20 |
21 | return ajax(buildUrl(host, apiNamespace, url), ...args);
22 | }
23 |
24 | async onFindRecording(recordingId) {
25 | const response = await this.ajax(`/${encodeURIComponent(recordingId)}`, {
26 | Accept: 'application/json; charset=utf-8'
27 | });
28 |
29 | return this._normalize(response);
30 | }
31 |
32 | async onSaveRecording(recordingId, data) {
33 | await this.ajax(`/${encodeURIComponent(recordingId)}`, {
34 | method: 'POST',
35 | body: this.stringify(data),
36 | headers: {
37 | 'Content-Type': 'application/json; charset=utf-8',
38 | Accept: 'application/json; charset=utf-8'
39 | }
40 | });
41 | }
42 |
43 | async onDeleteRecording(recordingId) {
44 | await this.ajax(`/${encodeURIComponent(recordingId)}`, {
45 | method: 'DELETE'
46 | });
47 | }
48 |
49 | _normalize({ xhr, body }) {
50 | /**
51 | * 204 - No Content. Polly uses this status code in place of 404
52 | * when interacting with our Rest server to prevent throwing
53 | * request errors in consumer's stdout (console.log)
54 | */
55 | if (xhr.status === 204) {
56 | /* return null when a record was not found */
57 | return null;
58 | }
59 |
60 | return body;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister-rest/types.d.ts:
--------------------------------------------------------------------------------
1 | import Persister from '@pollyjs/persister';
2 |
3 | export default class RESTPersister extends Persister<{
4 | host?: string;
5 | apiNamespace?: string;
6 | }> {}
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/persister",
3 | "version": "6.0.6",
4 | "description": "Extendable base persister class used by @pollyjs",
5 | "main": "dist/cjs/pollyjs-persister.js",
6 | "module": "dist/es/pollyjs-persister.js",
7 | "browser": "dist/umd/pollyjs-persister.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/persister",
15 | "scripts": {
16 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
17 | "test:build": "rollup -c rollup.config.test.js",
18 | "test:build:watch": "rollup -c rollup.config.test.js -w",
19 | "build:watch": "yarn build -w",
20 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
21 | },
22 | "keywords": [
23 | "polly",
24 | "pollyjs",
25 | "persister"
26 | ],
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "contributors": [
31 | {
32 | "name": "Jason Mitchell",
33 | "email": "jason.mitchell.w@gmail.com"
34 | },
35 | {
36 | "name": "Offir Golan",
37 | "email": "offirgolan@gmail.com"
38 | }
39 | ],
40 | "license": "Apache-2.0",
41 | "dependencies": {
42 | "@pollyjs/utils": "^6.0.6",
43 | "@types/set-cookie-parser": "^2.4.1",
44 | "bowser": "^2.4.0",
45 | "fast-json-stable-stringify": "^2.1.0",
46 | "lodash-es": "^4.17.21",
47 | "set-cookie-parser": "^2.4.8",
48 | "utf8-byte-length": "^1.0.4"
49 | },
50 | "devDependencies": {
51 | "har-validator": "^5.1.5",
52 | "rollup": "^1.14.6"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
3 |
4 | export default [createNodeTestConfig(), createBrowserTestConfig()];
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/entry.js:
--------------------------------------------------------------------------------
1 | import Request from './request';
2 | import Response from './response';
3 |
4 | const { keys } = Object;
5 |
6 | function totalTime(timings = {}) {
7 | return keys(timings).reduce(
8 | (total, k) => (timings[k] > 0 ? (total += timings[k]) : total),
9 | 0
10 | );
11 | }
12 |
13 | export default class Entry {
14 | constructor(request) {
15 | this._id = request.id;
16 | this._order = request.order;
17 | this.startedDateTime = request.timestamp;
18 | this.request = new Request(request);
19 | this.response = new Response(request.response);
20 | this.cache = {};
21 | this.timings = {
22 | blocked: -1,
23 | dns: -1,
24 | connect: -1,
25 | send: 0,
26 | wait: request.responseTime,
27 | receive: 0,
28 | ssl: -1
29 | };
30 | this.time = totalTime(this.timings);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/index.js:
--------------------------------------------------------------------------------
1 | import Log from './log';
2 |
3 | export default class HAR {
4 | constructor(opts = {}) {
5 | this.log = new Log(opts.log);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/log.js:
--------------------------------------------------------------------------------
1 | import uniqWith from 'lodash-es/uniqWith';
2 | import Bowser from 'bowser';
3 |
4 | const bowser =
5 | global.navigator && global.navigator.userAgent
6 | ? Bowser.getParser(global.navigator.userAgent).getBrowser()
7 | : null;
8 | const browser =
9 | bowser && bowser.name && bowser.version
10 | ? { name: bowser.name, version: bowser.version }
11 | : null;
12 |
13 | export default class Log {
14 | constructor(opts = {}) {
15 | // eslint-disable-next-line no-restricted-properties
16 | Object.assign(
17 | this,
18 | {
19 | version: '1.2',
20 | entries: [],
21 | pages: []
22 | },
23 | opts
24 | );
25 |
26 | if (!this.browser && browser) {
27 | this.browser = browser;
28 | }
29 | }
30 |
31 | addEntries(entries = []) {
32 | this.entries = uniqWith(
33 | // Add the new entries to the front so they take priority
34 | [...entries, ...this.entries],
35 | (a, b) => a._id === b._id && a._order === b._order
36 | );
37 | }
38 |
39 | sortEntries() {
40 | this.entries = this.entries.sort(
41 | (a, b) => new Date(a.startedDateTime) - new Date(b.startedDateTime)
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/request.js:
--------------------------------------------------------------------------------
1 | import getByteLength from 'utf8-byte-length';
2 | import setCookies from 'set-cookie-parser';
3 |
4 | import toNVPairs from './utils/to-nv-pairs';
5 | import getFirstHeader from './utils/get-first-header';
6 |
7 | function headersSize(request) {
8 | const keys = [];
9 | const values = [];
10 |
11 | request.headers.forEach(({ name, value }) => {
12 | keys.push(name);
13 | values.push(value);
14 | });
15 |
16 | const headersString =
17 | request.method + request.url + keys.join() + values.join();
18 |
19 | // startline: [method] [url] HTTP/1.1\r\n = 12
20 | // endline: \r\n = 2
21 | // every header + \r\n = * 2
22 | // add 2 for each combined header
23 | return getByteLength(headersString) + keys.length * 2 + 2 + 12 + 2;
24 | }
25 |
26 | export default class Request {
27 | constructor(request) {
28 | this.httpVersion = 'HTTP/1.1';
29 | this.url = request.absoluteUrl;
30 | this.method = request.method;
31 | this.headers = toNVPairs(request.headers);
32 | this.headersSize = headersSize(this);
33 | this.queryString = toNVPairs(request.query);
34 | this.cookies = setCookies.parse(request.getHeader('Set-Cookie'));
35 |
36 | if (request.body) {
37 | this.postData = {
38 | mimeType: getFirstHeader(request, 'Content-Type') || 'text/plain',
39 | params: []
40 | };
41 |
42 | if (typeof request.body === 'string') {
43 | this.postData.text = request.body;
44 | }
45 | }
46 |
47 | const contentLength = getFirstHeader(request, 'Content-Length');
48 |
49 | if (contentLength) {
50 | this.bodySize = parseInt(contentLength, 10);
51 | } else {
52 | this.bodySize =
53 | this.postData && this.postData.text
54 | ? getByteLength(this.postData.text)
55 | : 0;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/response.js:
--------------------------------------------------------------------------------
1 | import getByteLength from 'utf8-byte-length';
2 | import setCookies from 'set-cookie-parser';
3 |
4 | import toNVPairs from './utils/to-nv-pairs';
5 | import getFirstHeader from './utils/get-first-header';
6 |
7 | function headersSize(response) {
8 | const keys = [];
9 | const values = [];
10 |
11 | response.headers.forEach(({ name, value }) => {
12 | keys.push(name);
13 | values.push(value);
14 | });
15 |
16 | const headersString = keys.join() + values.join();
17 |
18 | // endline: \r\n = 2
19 | // every header + \r\n = * 2
20 | // add 2 for each combined header
21 | return getByteLength(headersString) + keys.length * 2 + 2 + 2;
22 | }
23 |
24 | export default class Response {
25 | constructor(response) {
26 | this.httpVersion = 'HTTP/1.1';
27 | this.status = response.statusCode;
28 | this.statusText = response.statusText;
29 | this.headers = toNVPairs(response.headers);
30 | this.headersSize = headersSize(this);
31 | this.cookies = setCookies.parse(response.getHeader('Set-Cookie'));
32 | this.redirectURL = getFirstHeader(response, 'Location') || '';
33 |
34 | this.content = {
35 | mimeType: getFirstHeader(response, 'Content-Type') || 'text/plain'
36 | };
37 |
38 | if (response.body && typeof response.body === 'string') {
39 | this.content.text = response.body;
40 |
41 | if (response.encoding) {
42 | this.content.encoding = response.encoding;
43 | }
44 | }
45 |
46 | const contentLength = getFirstHeader(response, 'Content-Length');
47 |
48 | if (contentLength) {
49 | this.content.size = parseInt(contentLength, 10);
50 | } else {
51 | this.content.size = this.content.text
52 | ? getByteLength(this.content.text)
53 | : 0;
54 | }
55 |
56 | this.bodySize = this.content.size;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/utils/get-first-header.js:
--------------------------------------------------------------------------------
1 | const { isArray } = Array;
2 |
3 | /**
4 | * Get the value of the given header name. If the value is an array,
5 | * get the first value.
6 | *
7 | * @export
8 | * @param {PollyRequest | PollyResponse} r
9 | * @param {string} name
10 | * @returns {string | undefined}
11 | */
12 | export default function getFirstHeader(r, name) {
13 | const value = r.getHeader(name);
14 |
15 | if (isArray(value)) {
16 | return value.length > 0 ? value[0] : '';
17 | }
18 |
19 | return value;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/@pollyjs/persister/src/har/utils/to-nv-pairs.js:
--------------------------------------------------------------------------------
1 | const { keys } = Object;
2 | const { isArray } = Array;
3 |
4 | export default function toNVPairs(o) {
5 | return keys(o || {}).reduce((pairs, name) => {
6 | const value = o[name];
7 |
8 | if (isArray(value)) {
9 | // _fromType is used to convert the stored header back into the
10 | // type it originally was. Take the following example:
11 | // { 'content-type': ['text/htm'] }
12 | // => { name: 'content-type', value: 'text/html', _fromType: 'array }
13 | // => { 'content-type': ['text/htm'] }
14 | pairs.push(...value.map((v) => ({ name, value: v, _fromType: 'array' })));
15 | } else {
16 | pairs.push({ name, value });
17 | }
18 |
19 | return pairs;
20 | }, []);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Record, Replay, and Stub HTTP Interactions
5 |
6 | [](https://travis-ci.com/Netflix/pollyjs)
7 | [](https://badge.fury.io/js/%40pollyjs%2Futils)
8 | [](http://www.apache.org/licenses/LICENSE-2.0)
9 |
10 | The `@pollyjs/utils` package provides utilities and constants for other @pollyjs packages.
11 |
12 | ## Installation
13 |
14 | _Note that you must have node (and npm) installed._
15 |
16 | ```bash
17 | npm install @pollyjs/utils -D
18 | ```
19 |
20 | If you want to install it with [yarn](https://yarnpkg.com):
21 |
22 | ```bash
23 | yarn add @pollyjs/utils -D
24 | ```
25 |
26 | ## License
27 |
28 | Copyright (c) 2018 Netflix, Inc.
29 |
30 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
31 |
32 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
33 |
34 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
35 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pollyjs/utils",
3 | "version": "6.0.6",
4 | "description": "Shared utilities and constants between @pollyjs packages",
5 | "main": "dist/cjs/pollyjs-utils.js",
6 | "module": "dist/es/pollyjs-utils.js",
7 | "browser": "dist/umd/pollyjs-utils.js",
8 | "types": "types.d.ts",
9 | "files": [
10 | "src",
11 | "dist",
12 | "types.d.ts"
13 | ],
14 | "repository": "https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/utils",
15 | "scripts": {
16 | "build": "rollup -c ../../../scripts/rollup/default.config.js",
17 | "test:build": "rollup -c rollup.config.test.js",
18 | "test:build:watch": "rollup -c rollup.config.test.js -w",
19 | "build:watch": "yarn build -w",
20 | "watch-all": "npm-run-all --parallel build:watch test:build:watch"
21 | },
22 | "keywords": [
23 | "polly",
24 | "pollyjs",
25 | "utils"
26 | ],
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "contributors": [
31 | {
32 | "name": "Jason Mitchell",
33 | "email": "jason.mitchell.w@gmail.com"
34 | },
35 | {
36 | "name": "Offir Golan",
37 | "email": "offirgolan@gmail.com"
38 | }
39 | ],
40 | "license": "Apache-2.0",
41 | "dependencies": {
42 | "qs": "^6.10.1",
43 | "url-parse": "^1.5.3"
44 | },
45 | "devDependencies": {
46 | "rollup": "^1.14.6"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/rollup.config.test.js:
--------------------------------------------------------------------------------
1 | import createNodeTestConfig from '../../../scripts/rollup/node.test.config';
2 | import createBrowserTestConfig from '../../../scripts/rollup/browser.test.config';
3 |
4 | export default [createNodeTestConfig(), createBrowserTestConfig()];
5 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/constants/actions.js:
--------------------------------------------------------------------------------
1 | export default {
2 | RECORD: 'record',
3 | REPLAY: 'replay',
4 | INTERCEPT: 'intercept',
5 | PASSTHROUGH: 'passthrough'
6 | };
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/constants/expiry-strategies.js:
--------------------------------------------------------------------------------
1 | export default {
2 | RECORD: 'record',
3 | WARN: 'warn',
4 | ERROR: 'error'
5 | };
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/constants/http-methods.js:
--------------------------------------------------------------------------------
1 | export default [
2 | 'GET',
3 | 'PUT',
4 | 'POST',
5 | 'DELETE',
6 | 'PATCH',
7 | 'MERGE',
8 | 'HEAD',
9 | 'OPTIONS'
10 | ];
11 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/constants/http-status-codes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 100: 'Continue',
3 | 101: 'Switching Protocols',
4 | 200: 'OK',
5 | 201: 'Created',
6 | 202: 'Accepted',
7 | 203: 'Non-Authoritative Information',
8 | 204: 'No Content',
9 | 205: 'Reset Content',
10 | 206: 'Partial Content',
11 | 207: 'Multi-Status',
12 | 300: 'Multiple Choice',
13 | 301: 'Moved Permanently',
14 | 302: 'Found',
15 | 303: 'See Other',
16 | 304: 'Not Modified',
17 | 305: 'Use Proxy',
18 | 307: 'Temporary Redirect',
19 | 400: 'Bad Request',
20 | 401: 'Unauthorized',
21 | 402: 'Payment Required',
22 | 403: 'Forbidden',
23 | 404: 'Not Found',
24 | 405: 'Method Not Allowed',
25 | 406: 'Not Acceptable',
26 | 407: 'Proxy Authentication Required',
27 | 408: 'Request Timeout',
28 | 409: 'Conflict',
29 | 410: 'Gone',
30 | 411: 'Length Required',
31 | 412: 'Precondition Failed',
32 | 413: 'Request Entity Too Large',
33 | 414: 'Request-URI Too Long',
34 | 415: 'Unsupported Media Type',
35 | 416: 'Requested Range Not Satisfiable',
36 | 417: 'Expectation Failed',
37 | 422: 'Unprocessable Entity',
38 | 500: 'Internal Server Error',
39 | 501: 'Not Implemented',
40 | 502: 'Bad Gateway',
41 | 503: 'Service Unavailable',
42 | 504: 'Gateway Timeout',
43 | 505: 'HTTP Version Not Supported'
44 | };
45 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/constants/modes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | RECORD: 'record',
3 | REPLAY: 'replay',
4 | PASSTHROUGH: 'passthrough',
5 | STOPPED: 'stopped'
6 | };
7 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as MODES } from './constants/modes';
2 | export { default as ACTIONS } from './constants/actions';
3 | export { default as HTTP_METHODS } from './constants/http-methods';
4 | export { default as HTTP_STATUS_CODES } from './constants/http-status-codes';
5 | export { default as EXPIRY_STRATEGIES } from './constants/expiry-strategies';
6 |
7 | export { default as assert } from './utils/assert';
8 | export { default as timeout } from './utils/timeout';
9 | export { default as timestamp } from './utils/timestamp';
10 | export { default as buildUrl } from './utils/build-url';
11 |
12 | export { default as PollyError } from './utils/polly-error';
13 | export { default as Serializers } from './utils/serializers';
14 |
15 | export { default as URL } from './utils/url';
16 |
17 | export { default as isBufferUtf8Representable } from './utils/is-buffer-utf8-representable';
18 | export { default as cloneArrayBuffer } from './utils/clone-arraybuffer';
19 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/assert.js:
--------------------------------------------------------------------------------
1 | import PollyError from './polly-error';
2 |
3 | export default function (msg, condition) {
4 | if (!condition) {
5 | throw new PollyError(msg);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/build-url.js:
--------------------------------------------------------------------------------
1 | import URL from './url';
2 |
3 | export default function buildUrl(...paths) {
4 | const url = new URL(
5 | paths
6 | .map((p) => p && (p + '').trim()) // Trim each string
7 | .filter(Boolean) // Remove empty strings or other falsy paths
8 | .join('/')
9 | );
10 |
11 | // Replace 2+ consecutive slashes with 1. (e.g. `///` --> `/`)
12 | url.set('pathname', url.pathname.replace(/\/{2,}/g, '/'));
13 |
14 | return url.href;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/clone-arraybuffer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Clone an array buffer
3 | *
4 | * @param {ArrayBuffer} arrayBuffer
5 | */
6 | export default function cloneArrayBuffer(arrayBuffer) {
7 | const clonedArrayBuffer = new ArrayBuffer(arrayBuffer.byteLength);
8 |
9 | new Uint8Array(clonedArrayBuffer).set(new Uint8Array(arrayBuffer));
10 |
11 | return clonedArrayBuffer;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/is-buffer-utf8-representable.js:
--------------------------------------------------------------------------------
1 | import { Buffer } from 'buffer';
2 |
3 | /**
4 | * Determine if the given buffer is utf8.
5 | * @param {Buffer} buffer
6 | */
7 | export default function isBufferUtf8Representable(buffer) {
8 | const utfEncodedBuffer = buffer.toString('utf8');
9 | const reconstructedBuffer = Buffer.from(utfEncodedBuffer, 'utf8');
10 |
11 | return reconstructedBuffer.equals(buffer);
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/polly-error.js:
--------------------------------------------------------------------------------
1 | export default class PollyError extends Error {
2 | constructor(message, ...args) {
3 | super(`[Polly] ${message}`, ...args);
4 |
5 | // Maintains proper stack trace for where our error was thrown (only available on V8)
6 | if (Error.captureStackTrace) {
7 | Error.captureStackTrace(this, PollyError);
8 | }
9 |
10 | this.name = 'PollyError';
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/serializers/blob.js:
--------------------------------------------------------------------------------
1 | export const supportsBlob = (() => {
2 | try {
3 | return !!new Blob();
4 | } catch (e) {
5 | return false;
6 | }
7 | })();
8 |
9 | export function readBlob(blob) {
10 | return new Promise((resolve, reject) => {
11 | const reader = new FileReader();
12 |
13 | reader.onend = reject;
14 | reader.onabort = reject;
15 | reader.onload = () => resolve(reader.result);
16 | reader.readAsDataURL(new Blob([blob], { type: blob.type }));
17 | });
18 | }
19 |
20 | export async function serialize(body) {
21 | if (supportsBlob && body instanceof Blob) {
22 | return await readBlob(body);
23 | }
24 |
25 | return body;
26 | }
27 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/serializers/buffer.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | export const supportsBuffer = typeof Buffer !== 'undefined';
4 | export const supportsArrayBuffer = typeof ArrayBuffer !== 'undefined';
5 |
6 | export function serialize(body) {
7 | if (supportsBuffer && body) {
8 | let buffer;
9 |
10 | if (Buffer.isBuffer(body)) {
11 | buffer = body;
12 | } else if (Array.isArray(body) && body.some((c) => Buffer.isBuffer(c))) {
13 | // Body is a chunked array
14 | const chunks = body.map((c) => Buffer.from(c));
15 |
16 | buffer = Buffer.concat(chunks);
17 | } else if (`${body}` === '[object ArrayBuffer]') {
18 | buffer = Buffer.from(body);
19 | } else if (supportsArrayBuffer && ArrayBuffer.isView(body)) {
20 | buffer = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
21 | }
22 |
23 | if (Buffer.isBuffer(buffer)) {
24 | return buffer.toString('base64');
25 | }
26 | }
27 |
28 | return body;
29 | }
30 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/serializers/form-data.js:
--------------------------------------------------------------------------------
1 | import { supportsBlob, readBlob } from './blob';
2 |
3 | export const supportsFormData = typeof FormData !== 'undefined';
4 |
5 | export async function serialize(body) {
6 | if (supportsFormData && body instanceof FormData) {
7 | const data = [];
8 |
9 | for (const [key, value] of body.entries()) {
10 | if (supportsBlob && value instanceof Blob) {
11 | const blobContent = await readBlob(value);
12 |
13 | data.push(`${key}=${blobContent}`);
14 | } else {
15 | data.push(`${key}=${value}`);
16 | }
17 | }
18 |
19 | return data.join('\r\n');
20 | }
21 |
22 | return body;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/serializers/index.js:
--------------------------------------------------------------------------------
1 | import { serialize as blob } from './blob';
2 | import { serialize as formData } from './form-data';
3 | import { serialize as buffer } from './buffer';
4 |
5 | export default { blob, formData, buffer };
6 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/timeout.js:
--------------------------------------------------------------------------------
1 | export default function timeout(time) {
2 | const ms = parseInt(time, 10);
3 |
4 | return new Promise((resolve) =>
5 | ms > 0 ? setTimeout(resolve, ms) : resolve()
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/src/utils/timestamp.js:
--------------------------------------------------------------------------------
1 | export default function timestamp() {
2 | return new Date().toISOString();
3 | }
4 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/browser/unit/utils/serializers/blob.js:
--------------------------------------------------------------------------------
1 | import File from '@pollyjs-tests/helpers/file';
2 |
3 | import { serialize } from '../../../../../src/utils/serializers/blob';
4 | import serializerTests from '../../../../serializer-tests';
5 |
6 | describe('Unit | Utils | Serializers | blob', function () {
7 | serializerTests(serialize);
8 |
9 | it('should noop if Blob is not found', function () {
10 | const Blob = Blob;
11 | const blob = new Blob(['blob'], { type: 'text/plain' });
12 |
13 | global.Blob = undefined;
14 | expect(serialize(blob)).to.be.equal(blob);
15 | global.Blob = Blob;
16 | });
17 |
18 | it('should handle blobs', async function () {
19 | expect(
20 | await serialize(new Blob(['blob'], { type: 'text/plain' }))
21 | ).to.equal(`data:text/plain;base64,${btoa('blob')}`);
22 | });
23 |
24 | it('should handle files', async function () {
25 | expect(
26 | await serialize(
27 | new File(['file'], 'file.txt', {
28 | type: 'text/plain'
29 | })
30 | )
31 | ).to.equal(`data:text/plain;base64,${btoa('file')}`);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/browser/unit/utils/serializers/form-data.js:
--------------------------------------------------------------------------------
1 | import File from '@pollyjs-tests/helpers/file';
2 |
3 | import { serialize } from '../../../../../src/utils/serializers/form-data';
4 | import serializerTests from '../../../../serializer-tests';
5 |
6 | describe('Unit | Utils | Serializers | form-data', function () {
7 | serializerTests(serialize);
8 |
9 | it('should noop if FormData is not found', function () {
10 | const FormData = FormData;
11 | const formData = new FormData();
12 |
13 | global.FormData = undefined;
14 | expect(serialize(formData)).to.be.equal(formData);
15 | global.FormData = FormData;
16 | });
17 |
18 | it('should handle form-data', async function () {
19 | const formData = new FormData();
20 |
21 | formData.append('string', 'string');
22 | formData.append('array', [1, 2]);
23 | formData.append('blob', new Blob(['blob'], { type: 'text/plain' }));
24 | formData.append(
25 | 'file',
26 | new File(['file'], 'file.txt', { type: 'text/plain' })
27 | );
28 |
29 | const data = await serialize(formData);
30 |
31 | expect(data).to.include('string=string');
32 | expect(data).to.include('array=1,2');
33 | expect(data).to.include(`blob=data:text/plain;base64,${btoa('blob')}`);
34 | expect(data).to.include(`file=data:text/plain;base64,${btoa('file')}`);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/serializer-tests.js:
--------------------------------------------------------------------------------
1 | export default function serializerTests(serialize) {
2 | it('should exist', function () {
3 | expect(serialize).to.be.a('function');
4 | });
5 |
6 | it('should handle empty argument', async function () {
7 | expect(await serialize()).to.be.undefined;
8 | expect(await serialize(null)).to.be.null;
9 | });
10 |
11 | it('should handle strings', async function () {
12 | expect(await serialize('')).to.be.equal('');
13 | expect(await serialize('foo')).to.be.equal('foo');
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/unit/utils/assert-test.js:
--------------------------------------------------------------------------------
1 | import assert from '../../../src/utils/assert';
2 | import PollyError from '../../../src/utils/polly-error';
3 |
4 | describe('Unit | Utils | assert', function () {
5 | it('should exist', function () {
6 | expect(assert).to.be.a('function');
7 | });
8 |
9 | it('should throw with a false condition', function () {
10 | expect(() => assert('Test', false)).to.throw(PollyError, /Test/);
11 | });
12 |
13 | it('should throw without a condition', function () {
14 | expect(() => assert('Test')).to.throw(PollyError, /Test/);
15 | });
16 |
17 | it('should not throw with a true condition', function () {
18 | expect(() => assert('Test', true)).to.not.throw();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/unit/utils/build-url-test.js:
--------------------------------------------------------------------------------
1 | import buildUrl from '../../../src/utils/build-url';
2 |
3 | const origin = (global.location && global.location.origin) || '';
4 |
5 | describe('Unit | Utils | buildUrl', function () {
6 | it('should exist', function () {
7 | expect(buildUrl).to.be.a('function');
8 | });
9 |
10 | it('should remove consecutive slashes', function () {
11 | expect(buildUrl('http://foo.com///bar/baz/')).to.equal(
12 | 'http://foo.com/bar/baz/'
13 | );
14 | });
15 |
16 | it('should remove empty fragments of the url', function () {
17 | expect(buildUrl('http://foo///bar/////baz')).to.equal('http://foo/bar/baz');
18 | });
19 |
20 | it('should remove empty fragments of the url', function () {
21 | expect(buildUrl('/foo/bar/baz')).to.equal(`${origin}/foo/bar/baz`);
22 | });
23 |
24 | it('should concat multiple paths together', function () {
25 | expect(buildUrl('/foo', '/bar', null, undefined, false, '/baz')).to.equal(
26 | `${origin}/foo/bar/baz`
27 | );
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/unit/utils/polly-error-test.js:
--------------------------------------------------------------------------------
1 | import PollyError from '../../../src/utils/polly-error';
2 |
3 | describe('Unit | Utils | PollyError', function () {
4 | it('should exist', function () {
5 | expect(PollyError).to.be.a('function');
6 | });
7 |
8 | it('should set the name to PollyError', function () {
9 | const error = new PollyError('Test');
10 |
11 | expect(error.name).to.equal('PollyError');
12 | });
13 |
14 | it('should prefix the message with [Polly]', function () {
15 | const error = new PollyError('Test');
16 |
17 | expect(error.message).to.equal('[Polly] Test');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/unit/utils/timeout-test.js:
--------------------------------------------------------------------------------
1 | import timeout from '../../../src/utils/timeout';
2 |
3 | describe('Unit | Utils | timeout', function () {
4 | it('should exist', function () {
5 | expect(timeout).to.be.a('function');
6 | });
7 |
8 | it('should return a promise', async function () {
9 | const promise = timeout(10);
10 |
11 | expect(promise).to.be.a('promise');
12 |
13 | await promise;
14 | });
15 |
16 | it('should timeout for the correct amount of ms', async function () {
17 | this.timeout(110);
18 |
19 | const promise = timeout(100);
20 | let resolved = false;
21 |
22 | promise.then(() => (resolved = true));
23 |
24 | setTimeout(() => expect(resolved).to.be.false, 50);
25 | setTimeout(() => expect(resolved).to.be.true, 101);
26 |
27 | await promise;
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/tests/unit/utils/timestamp-test.js:
--------------------------------------------------------------------------------
1 | import timestamp from '../../../src/utils/timestamp';
2 |
3 | describe('Unit | Utils | timestamp', function () {
4 | it('should exist', function () {
5 | expect(timestamp).to.be.a('function');
6 | });
7 |
8 | it('should return a string', function () {
9 | expect(timestamp()).to.be.a('string');
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/packages/@pollyjs/utils/types.d.ts:
--------------------------------------------------------------------------------
1 | export enum MODES {
2 | RECORD = 'record',
3 | REPLAY = 'replay',
4 | PASSTHROUGH = 'passthrough',
5 | STOPPED = 'stopped'
6 | }
7 |
8 | export enum ACTIONS {
9 | RECORD = 'record',
10 | REPLAY = 'replay',
11 | INTERCEPT = 'intercept',
12 | PASSTHROUGH = 'passthrough'
13 | }
14 |
15 | export enum EXPIRY_STRATEGIES {
16 | RECORD = 'record',
17 | WARN = 'warn',
18 | ERROR = 'error'
19 | }
20 |
--------------------------------------------------------------------------------
/scripts/require-clean-work-tree.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | require_clean_work_tree () {
4 | git rev-parse --verify HEAD >/dev/null || exit 1
5 | git update-index -q --ignore-submodules --refresh
6 |
7 | # Disallow unstaged changes in the working tree
8 | if ! git diff-files --quiet --ignore-submodules
9 | then
10 | echo "There are unstaged changes."
11 | git diff-files --name-status -r --ignore-submodules --
12 | exit 1
13 | fi
14 |
15 | # Disallow uncommitted changes in the index
16 | if ! git diff-index --cached --quiet --ignore-submodules HEAD --
17 | then
18 | echo "The index contains uncommitted changes."
19 | git diff-index --cached --name-status -r --ignore-submodules HEAD --
20 | exit 1
21 | fi
22 | }
23 |
24 | require_clean_work_tree
25 |
--------------------------------------------------------------------------------
/scripts/require-test-build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ ! -f "./packages/@pollyjs/node-server/dist/cjs/pollyjs-node-server.js" ]; then
4 | echo "Test server build not found. Run either '$ yarn watch' or '$ yarn build:server'"
5 | exit 1
6 | fi
7 |
8 | if [ ! -f "./packages/@pollyjs/core/dist/cjs/pollyjs-core.js" ]; then
9 | echo "Build not found. Run either '$ yarn watch' or '$ yarn build'"
10 | exit 1
11 | fi
12 |
13 | if [ ! -f "./packages/@pollyjs/core/build/node/test-bundle.cjs.js" ]; then
14 | echo "Test build not found. Run either '$ yarn watch' or '$ yarn test:build'"
15 | exit 1
16 | fi
17 |
--------------------------------------------------------------------------------
/scripts/rollup/browser.test.config.js:
--------------------------------------------------------------------------------
1 | import deepmerge from 'deepmerge';
2 | import multiEntry from 'rollup-plugin-multi-entry';
3 | import alias from 'rollup-plugin-alias';
4 |
5 | import createBrowserConfig from './browser.config';
6 | import { pkg, testsPath } from './utils';
7 |
8 | export default function createBrowserTestConfig(options = {}) {
9 | return deepmerge(
10 | createBrowserConfig(
11 | {
12 | input: 'tests/!(node|jest)/**/*-test.js',
13 | output: {
14 | format: 'es',
15 | name: `${pkg.name}-tests`,
16 | file: `./build/browser/test-bundle.es.js`,
17 | intro: `
18 | 'use strict'
19 | describe('${pkg.name}', function() {
20 | `,
21 | outro: '});'
22 | },
23 | plugins: [alias({ '@pollyjs-tests': testsPath }), multiEntry()]
24 | },
25 | /* target override */
26 | {
27 | browsers: ['last 2 Chrome versions']
28 | }
29 | ),
30 | options
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/rollup/default.config.js:
--------------------------------------------------------------------------------
1 | import createBrowserConfig from './browser.config';
2 | import createNodeConfig from './node.config';
3 |
4 | export default [createBrowserConfig(), createNodeConfig()];
5 |
--------------------------------------------------------------------------------
/scripts/rollup/jest.test.config.js:
--------------------------------------------------------------------------------
1 | import deepmerge from 'deepmerge';
2 |
3 | import createNodeTestConfig from './node.test.config';
4 | import { pkg } from './utils';
5 |
6 | export default function createJestTestConfig(options = {}) {
7 | return deepmerge(
8 | createNodeTestConfig({
9 | input: 'tests/jest/**/*-test.js',
10 | output: {
11 | format: 'cjs',
12 | name: `${pkg.name}-tests`,
13 | file: `./build/jest/test-bundle.cjs.js`,
14 | intro: `describe('${pkg.name}', function() {`,
15 | outro: '});'
16 | }
17 | }),
18 | options
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/scripts/rollup/node.config.js:
--------------------------------------------------------------------------------
1 | import deepmerge from 'deepmerge';
2 | import json from 'rollup-plugin-json';
3 | import babel from 'rollup-plugin-babel';
4 | import { terser } from 'rollup-plugin-terser';
5 | import commonjs from 'rollup-plugin-commonjs';
6 | import resolve from 'rollup-plugin-node-resolve';
7 |
8 | import { input, output, pkg, minify } from './utils';
9 |
10 | const external = Object.keys(pkg.dependencies || {});
11 |
12 | export default function createNodeConfig(options = {}) {
13 | return deepmerge(
14 | {
15 | input,
16 | output: [output('cjs'), output('es')],
17 | external,
18 | plugins: [
19 | json(),
20 | resolve({ preferBuiltins: true }),
21 | commonjs(),
22 | babel({
23 | babelrc: false,
24 | runtimeHelpers: true,
25 | exclude: ['../../../node_modules/**'],
26 | presets: [
27 | [
28 | '@babel/preset-env',
29 | {
30 | modules: false,
31 | targets: {
32 | node: '12.0.0'
33 | }
34 | }
35 | ]
36 | ],
37 | plugins: [
38 | '@babel/plugin-external-helpers',
39 | ['@babel/plugin-transform-runtime', { corejs: 2 }],
40 | ['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }]
41 | ]
42 | }),
43 | minify && terser()
44 | ]
45 | },
46 | options
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/scripts/rollup/node.test.config.js:
--------------------------------------------------------------------------------
1 | import deepmerge from 'deepmerge';
2 | import multiEntry from 'rollup-plugin-multi-entry';
3 | import alias from 'rollup-plugin-alias';
4 |
5 | import createNodeConfig from './node.config';
6 | import { pkg, testsPath } from './utils';
7 |
8 | const pollyDependencies = Object.keys(pkg.devDependencies || {}).filter((d) =>
9 | d.startsWith('@pollyjs')
10 | );
11 |
12 | export default function createNodeTestConfig(options = {}) {
13 | return deepmerge(
14 | createNodeConfig({
15 | input: 'tests/!(browser|jest)/**/*-test.js',
16 | output: {
17 | format: 'cjs',
18 | name: `${pkg.name}-tests`,
19 | file: `./build/node/test-bundle.cjs.js`,
20 | intro: `describe('${pkg.name}', function() {`,
21 | outro: '});'
22 | },
23 | plugins: [alias({ '@pollyjs-tests': testsPath }), multiEntry()],
24 | external: [...pollyDependencies, 'node-fetch', 'chai']
25 | }),
26 | options
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/scripts/rollup/utils.js:
--------------------------------------------------------------------------------
1 | /* globals require process */
2 |
3 | import path from 'path';
4 |
5 | export const pkg = require(path.resolve(process.cwd(), './package.json'));
6 | export const production = process.env.NODE_ENV === 'production';
7 | export const minify = process.env.MINIFY === 'true';
8 |
9 | const banner = `/**
10 | * ${pkg.name} v${pkg.version}
11 | *
12 | * https://github.com/netflix/pollyjs
13 | *
14 | * Released under the ${pkg.license} License.
15 | */`;
16 |
17 | export const input = './src/index.js';
18 | export const output = (format) => {
19 | return {
20 | format,
21 | file: `./dist/${format}/${pkg.name.replace('@pollyjs/', 'pollyjs-')}.${
22 | minify ? 'min.js' : 'js'
23 | }`,
24 | sourcemap: production,
25 | banner
26 | };
27 | };
28 |
29 | export const testsPath = path.resolve(process.cwd(), '../../../tests');
30 |
--------------------------------------------------------------------------------
/testem.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | const attachMiddleware = require('./tests/middleware');
3 |
4 | module.exports = {
5 | port: 4000,
6 | fail_on_zero_tests: true,
7 | test_page: 'tests/index.mustache',
8 | launch_in_ci: ['Chrome', 'Node', 'Jest', 'Ember', 'ESLint'],
9 | launch_in_dev: ['Chrome', 'Node', 'Jest', 'Ember', 'ESLint'],
10 | watch_files: [
11 | './scripts/rollup/*',
12 | './packages/@pollyjs/*/build/**/*',
13 | './packages/@pollyjs/*/dist/**/*'
14 | ],
15 | serve_files: ['./packages/@pollyjs/*/build/browser/*.js'],
16 | browser_args: {
17 | Chrome: {
18 | ci: [
19 | // --no-sandbox is needed when running Chrome inside a container
20 | process.env.CI ? '--no-sandbox' : null,
21 | '--headless',
22 | '--disable-gpu',
23 | '--disable-dev-shm-usage',
24 | '--disable-software-rasterizer',
25 | '--mute-audio',
26 | '--remote-debugging-port=0',
27 | '--window-size=1440,900'
28 | ].filter(Boolean)
29 | }
30 | },
31 | middleware: [attachMiddleware],
32 | launchers: {
33 | 'Node:debug': {
34 | command: 'mocha --inspect-brk'
35 | },
36 | Node: {
37 | command: 'mocha --reporter tap',
38 | protocol: 'tap'
39 | },
40 | Jest: {
41 | command: 'jest',
42 | protocol: 'tap'
43 | },
44 | Ember: {
45 | command: 'yarn workspace @pollyjs/ember run test',
46 | protocol: 'tap'
47 | },
48 | ESLint: {
49 | command: 'yarn lint --format tap',
50 | protocol: 'tap'
51 | }
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/tests/assets/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Netflix/pollyjs/d031861625f58423169b396e5afc8a0110d1b232/tests/assets/32x32.png
--------------------------------------------------------------------------------
/tests/helpers/file.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Special thanks to the FormData project.
3 | * Full credit: https://github.com/jimmywarting/FormData (MIT)
4 | */
5 |
6 | const { defineProperties, defineProperty } = Object;
7 | const stringTag = Symbol && Symbol.toStringTag;
8 |
9 | let _File;
10 |
11 | try {
12 | new File([], '');
13 | _File = File;
14 | } catch (e) {
15 | /**
16 | * @see http://www.w3.org/TR/FileAPI/#dfn-file
17 | * @param {!Array=} chunks
18 | * @param {string=} filename
19 | * @param {{type: (string|undefined), lastModified: (number|undefined)}=}
20 | * opts
21 | * @constructor
22 | * @extends {Blob}
23 | */
24 | _File = function File(chunks, filename, opts = {}) {
25 | const _this = new Blob(chunks, opts);
26 | const modified =
27 | opts && opts.lastModified !== undefined
28 | ? new Date(opts.lastModified)
29 | : new Date();
30 |
31 | defineProperties(_this, {
32 | name: {
33 | value: filename
34 | },
35 | lastModifiedDate: {
36 | value: modified
37 | },
38 | lastModified: {
39 | value: +modified
40 | },
41 | toString: {
42 | value() {
43 | return '[object File]';
44 | }
45 | }
46 | });
47 |
48 | if (stringTag) {
49 | defineProperty(_this, Symbol.toStringTag, {
50 | value: 'File'
51 | });
52 | }
53 |
54 | return _this;
55 | };
56 | }
57 |
58 | export default _File;
59 |
--------------------------------------------------------------------------------
/tests/helpers/global-node-fetch.js:
--------------------------------------------------------------------------------
1 | import fetch, { Response, Request, Headers } from 'node-fetch';
2 |
3 | global.fetch = fetch;
4 | global.Request = Request;
5 | global.Response = Response;
6 | global.Headers = Headers;
7 |
--------------------------------------------------------------------------------
/tests/helpers/setup-fetch-record.js:
--------------------------------------------------------------------------------
1 | const defaultOptions = {
2 | host: '',
3 | fetch() {
4 | return global.fetch(...arguments);
5 | }
6 | };
7 |
8 | function setupFetchRecord(options) {
9 | setupFetchRecord.beforeEach(options);
10 | setupFetchRecord.afterEach();
11 | }
12 |
13 | setupFetchRecord.beforeEach = function (options = {}) {
14 | options = { ...defaultOptions, ...options };
15 |
16 | beforeEach(function () {
17 | const { host, fetch } = options;
18 |
19 | this.fetch = fetch;
20 |
21 | this.relativeFetch = (url, options) => this.fetch(`${host}${url}`, options);
22 |
23 | this.recordUrl = () =>
24 | `${host}/api/db/${encodeURIComponent(this.polly.recordingId)}`;
25 |
26 | this.fetchRecord = (...args) => this.fetch(this.recordUrl(), ...args);
27 | });
28 | };
29 |
30 | setupFetchRecord.afterEach = function () {
31 | afterEach(async function () {
32 | // Note: test setup could fail, so we cannot assume this.polly
33 | // was setup before accessing it.
34 | if (this.polly) {
35 | this.polly.pause();
36 | await this.fetchRecord({ method: 'DELETE' });
37 | this.polly.play();
38 | }
39 | });
40 | };
41 |
42 | export default setupFetchRecord;
43 |
--------------------------------------------------------------------------------
/tests/helpers/setup-persister.js:
--------------------------------------------------------------------------------
1 | function setupPersister() {
2 | setupPersister.beforeEach();
3 | setupPersister.afterEach();
4 | }
5 |
6 | setupPersister.beforeEach = function () {};
7 |
8 | setupPersister.afterEach = function () {
9 | afterEach(async function () {
10 | await this.polly.persister.deleteRecording(this.polly.recordingId);
11 | });
12 | };
13 |
14 | export default setupPersister;
15 |
--------------------------------------------------------------------------------
/tests/index.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Polly.JS Tests
5 |
6 |
7 | {{#css_files}}
8 |
9 | {{/css_files}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 | {{#serve_files}}
25 |
26 | {{/serve_files}}
27 |
28 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tests/integration/adapter-node-tests.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | export default function adapterNodeTests() {
4 | it('should handle recording requests posting a Buffer', async function () {
5 | const { server, recordingName } = this.polly;
6 | const buffer = Buffer.from(recordingName);
7 | const url = `http://example.com/upload`;
8 |
9 | server.post(url).intercept((req, res) => {
10 | const body = req.identifiers.body;
11 |
12 | // Make sure the buffer exists in the identifiers
13 | expect(body).to.include(buffer.toString());
14 |
15 | res.sendStatus(200);
16 | });
17 |
18 | const res = await this.fetch(url, { method: 'POST', body: buffer });
19 |
20 | expect(res.status).to.equal(200);
21 | });
22 |
23 | it('should handle recording requests posting an ArrayBuffer', async function () {
24 | const { server } = this.polly;
25 | const buffer = new ArrayBuffer(8);
26 | const url = `http://example.com/upload`;
27 |
28 | server.post(url).intercept((req, res) => {
29 | const body = req.identifiers.body;
30 |
31 | // Make sure the buffer exists in the identifiers
32 | expect(body).to.include(Buffer.from(buffer).toString());
33 |
34 | res.sendStatus(200);
35 | });
36 |
37 | const res = await this.fetch(url, { method: 'POST', body: buffer });
38 |
39 | expect(res.status).to.equal(200);
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/tests/integration/adapter-polly-tests.js:
--------------------------------------------------------------------------------
1 | export default function pollyTests() {
2 | it('should not handle any requests when paused', async function () {
3 | const { server } = this.polly;
4 | const requests = [];
5 |
6 | server.any().on('request', (req) => requests.push(req));
7 |
8 | await this.fetchRecord();
9 | await this.fetchRecord();
10 |
11 | this.polly.pause();
12 | await this.fetchRecord();
13 | await this.fetchRecord();
14 |
15 | this.polly.play();
16 | await this.fetchRecord();
17 |
18 | expect(requests.length).to.equal(3);
19 | expect(this.polly._requests.length).to.equal(3);
20 | expect(requests.map((r) => r.order)).to.deep.equal([0, 1, 2]);
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/tests/middleware.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | const path = require('path');
3 |
4 | const bodyParser = require('body-parser');
5 | const compression = require('compression');
6 |
7 | const { registerExpressAPI } = require('../packages/@pollyjs/node-server');
8 |
9 | const DB = {};
10 |
11 | module.exports = function attachMiddleware(app) {
12 | registerExpressAPI(app, {
13 | recordingsDir: path.join(__dirname, 'recordings')
14 | });
15 |
16 | app.get('/assets/:name', (req, res) => {
17 | res.sendFile(path.join(__dirname, 'assets', req.params.name));
18 | });
19 |
20 | app.use(bodyParser.json());
21 |
22 | app.get('/echo', (req, res) => {
23 | const status = req.query.status;
24 |
25 | if (status === '204') {
26 | res.status(204).send();
27 | } else {
28 | res.sendStatus(req.query.status);
29 | }
30 | });
31 |
32 | app.post('/compress', compression({ filter: () => true }), (req, res) => {
33 | res.write(JSON.stringify(req.body));
34 | res.end();
35 | });
36 |
37 | app.get('/api', (req, res) => {
38 | res.sendStatus(200);
39 | });
40 |
41 | app.get('/api/db/:id', (req, res) => {
42 | const { id } = req.params;
43 |
44 | if (DB[id]) {
45 | res.status(200).json(DB[id]);
46 | } else {
47 | res.status(404).end();
48 | }
49 | });
50 |
51 | app.post('/api/db/:id', (req, res) => {
52 | const { id } = req.params;
53 |
54 | DB[id] = req.body;
55 | res.status(200).json(DB[id]);
56 | });
57 |
58 | app.delete('/api/db/:id', (req, res) => {
59 | const { id } = req.params;
60 |
61 | delete DB[id];
62 | res.status(200).end();
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/tests/node-setup.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | global.expect = require('chai').expect;
4 |
--------------------------------------------------------------------------------