Repository: Netflix/pollyjs Branch: master Commit: d031861625f5 Files: 393 Total size: 1.1 MB Directory structure: gitextract_9hhv16l2/ ├── .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/ │ │ └── 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/ │ │ │ ├── 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/ │ │ ├── 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 │ ├── 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 │ ├── 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/ │ │ ├── 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 │ ├── 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 │ └── 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/ ├── 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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .commitlintrc.js ================================================ /* eslint-env node */ module.exports = { extends: [ '@commitlint/config-lerna-scopes', '@commitlint/config-conventional' ], rules: { 'subject-case': [2, 'always', ['sentence-case']] } }; ================================================ FILE: .eslintignore ================================================ /packages/@pollyjs/ember/tests/**/index.html CHANGELOG.md package.json node_modules tmp build dist ================================================ FILE: .eslintrc.js ================================================ /* eslint-env node */ module.exports = { root: true, parserOptions: { ecmaVersion: 2018, sourceType: 'module' }, plugins: ['import'], extends: ['eslint:recommended', 'plugin:prettier/recommended'], globals: { global: true }, env: { browser: true, es6: true }, rules: { 'no-console': 'off', 'prefer-const': 'error', 'getter-return': 'error', 'padding-line-between-statements': [ 'error', // require blank lines before all return statements, { blankLine: 'always', prev: '*', next: 'return' }, // Require blank lines after every sequence of variable declarations { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] } ], 'no-restricted-properties': [ 2, { object: 'Object', property: 'assign', message: 'Please use the spread operator instead.' } ], // Require that imports occur at the top of the file 'import/first': 'error', // Require imports to be grouped and ordered consistently 'import/order': [ 'error', { 'newlines-between': 'always' } ] }, overrides: [ // test files { files: ['tests/**/*.js', '**/*/tests/**/*.js'], env: { mocha: true }, globals: { chai: true, expect: true } }, { files: ['**/*/tests/node/**/*.js'], env: { browser: false } }, { files: ['**/*/tests/jest/**/*.js'], env: { jest: true, mocha: false } } ] }; ================================================ FILE: .github/issue_template.md ================================================ ## Prerequisites - We realize there is a lot of data requested here. We ask only that you do your best to provide as much information as possible so we can better help you. - Read the [contributing guidelines](https://github.com/Netflix/pollyjs/blob/master/CONTRIBUTING.md). - Ensure the issue isn't already reported. - Should be reproducible with the latest @pollyjs package versions. > _Delete the above section and the instructions in the sections below before submitting_ ## Description If this is a feature request, explain why it should be added. Specific use-cases are best. For bug reports, please provide as much _relevant_ info as possible. ### Shareable Source ```js // Avoid posting hundreds of lines of source code. // Edit to just the relevant portions. ``` ### Error Message & Stack Trace ``` COPY THE ERROR MESSAGE, INCLUDING STACK TRACE HERE ``` ### Config Copy the config used to setup the Polly instance: ```js new Polly('Recording Name', { // config... }); ``` ### Dependencies Copy the @pollyjs dependencies from `package.json`: ```json { "@pollyjs/core": "latest", "@pollyjs/adapter-x": "latest", "@pollyjs/persister-x": "latest" } ``` ## Relevant Links - If your project is public, link to the repo so we can investigate directly. - **BONUS POINTS:** Create a [minimal reproduction](http://stackoverflow.com/help/mcve) and upload it to GitHub. This will get you the fastest support. ## Environment Tell us which operating system you are using, as well as which versions of Node.js and npm/yarn. If applicable, include the browser and the corresponding version. Run the following to get it quickly: ``` node -e "var os=require('os');console.log('Node.js ' + process.version + '\n' + os.platform() + ' ' + os.release())" npm --version yarn --version ``` ================================================ FILE: .github/pull_request_template.md ================================================ ## Description ## Motivation and Context ## Types of Changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist - [ ] I have added tests to cover my changes. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] My code follows the code style of this project. - [ ] My commits and the title of this PR follow the [Conventional Commits Specification](https://www.conventionalcommits.org). - [ ] I have read the [contributing guidelines](https://github.com/Netflix/pollyjs/blob/master/CONTRIBUTING.md). ================================================ FILE: .gitignore ================================================ .DS_Store node_modules package-lock.json lerna-debug.log packages/**/dist/ yarn-error.log tmp build dist *.lerna_backup .npmrc # Test recordings can write be written here if the test job did not # get a chance to run to completion. The test will cleans these files up afterwards. /recordings # Examples examples/**/*/yarn.lock # IDE .vscode/ .tool-versions ================================================ FILE: .husky/commit-msg ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" yarn commitlint --edit "$1" ================================================ FILE: .husky/pre-commit ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" yarn lint-staged ================================================ FILE: .mocharc.js ================================================ module.exports = { spec: './packages/@pollyjs/*/build/node/*.js', ui: 'bdd', require: 'tests/node-setup.js' }; ================================================ FILE: .prettierrc.js ================================================ 'use strict'; module.exports = { singleQuote: true, trailingComma: 'none' }; ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - '12' - '14' - '16' addons: chrome: stable cache: yarn: true before_install: - curl -o- -L https://yarnpkg.com/install.sh | bash - export PATH=$HOME/.yarn/bin:$PATH install: - yarn install --frozen-lockfile --non-interactive script: - commitlint-travis - yarn run test:ci - ./scripts/require-clean-work-tree.sh ================================================ FILE: CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [6.0.7](https://github.com/netflix/pollyjs/compare/v6.0.6...v6.0.7) (2025-05-31) ### Bug Fixes * Undeprecating fetch for node because node supports fetch now ([#506](https://github.com/netflix/pollyjs/issues/506)) ([be0bd6c](https://github.com/netflix/pollyjs/commit/be0bd6ca0035565a1c29770bfc87f0b0754fec27)) ## [6.0.6](https://github.com/netflix/pollyjs/compare/v6.0.5...v6.0.6) (2023-07-20) **Note:** Version bump only for package pollyjs ## [6.0.5](https://github.com/netflix/pollyjs/compare/v6.0.4...v6.0.5) (2022-04-04) ### Bug Fixes * **persister:** `requests` -> `request` in `HarEntry` declaration ([#441](https://github.com/netflix/pollyjs/issues/441)) ([6466810](https://github.com/netflix/pollyjs/commit/6466810677b6ac2c6a7496335bf40e043ab843e1)) ## [6.0.4](https://github.com/netflix/pollyjs/compare/v6.0.3...v6.0.4) (2021-12-10) ### Bug Fixes * Update types for class methods ([#438](https://github.com/netflix/pollyjs/issues/438)) ([b88655a](https://github.com/netflix/pollyjs/commit/b88655ac1b4ca7348afd45e9aeaa50e998ea68d7)) ## [6.0.3](https://github.com/netflix/pollyjs/compare/v6.0.2...v6.0.3) (2021-12-08) ### Bug Fixes * A few more type fixes ([#437](https://github.com/netflix/pollyjs/issues/437)) ([5e837a2](https://github.com/netflix/pollyjs/commit/5e837a25d28393b764cb66bcae0b29e9273eabe8)) ## [6.0.2](https://github.com/netflix/pollyjs/compare/v6.0.1...v6.0.2) (2021-12-07) ### Bug Fixes * **core:** Fix types for registering adapters and persisters ([#435](https://github.com/netflix/pollyjs/issues/435)) ([cc2fa19](https://github.com/netflix/pollyjs/commit/cc2fa197a5c0a5fdef4602c4a207d31f3e677897)) ## [6.0.1](https://github.com/netflix/pollyjs/compare/v6.0.0...v6.0.1) (2021-12-06) ### Bug Fixes * **ember:** Bump peer dependencies to 6.x ([#432](https://github.com/netflix/pollyjs/issues/432)) ([1529226](https://github.com/netflix/pollyjs/commit/152922688744c8a2f8d89f793dcecf3c2bc40033)) * **types:** add types.d.ts to package.files ([#431](https://github.com/netflix/pollyjs/issues/431)) ([113ee89](https://github.com/netflix/pollyjs/commit/113ee898bcf0467c5c48c15b53fc9198e2e91cb1)) # [6.0.0](https://github.com/netflix/pollyjs/compare/v5.2.0...v6.0.0) (2021-11-30) ### Bug Fixes * **persister:** Only treat status codes >= 400 as failed ([#430](https://github.com/netflix/pollyjs/issues/430)) ([7658952](https://github.com/netflix/pollyjs/commit/765895232746cd560da6bd566de8a312045b1703)) * fix!: Upgrade url-parse (#426) ([c21ed04](https://github.com/netflix/pollyjs/commit/c21ed048ff9d87a3773458dcfb9758e4fa6582bf)), closes [#426](https://github.com/netflix/pollyjs/issues/426) * feat!: Cleanup adapter and persister APIs (#429) ([06499fc](https://github.com/netflix/pollyjs/commit/06499fc2d85254b3329db2bec770d173ed32bca0)), closes [#429](https://github.com/netflix/pollyjs/issues/429) * feat!: Improve logging and add logLevel (#427) ([bef3ee3](https://github.com/netflix/pollyjs/commit/bef3ee39f71dfc2fa4dbeb522dfba16d01243e9f)), closes [#427](https://github.com/netflix/pollyjs/issues/427) * chore!: Upgrade package dependencies (#421) ([dd23334](https://github.com/netflix/pollyjs/commit/dd23334fa9b64248e4c49c3616237bdc2f12f682)), closes [#421](https://github.com/netflix/pollyjs/issues/421) * feat!: Use base64 instead of hex encoding for binary data (#420) ([6bb9b36](https://github.com/netflix/pollyjs/commit/6bb9b36522d73f9c079735d9006a12376aee39ea)), closes [#420](https://github.com/netflix/pollyjs/issues/420) * feat(ember)!: Upgrade to ember octane (#415) ([8559ef8](https://github.com/netflix/pollyjs/commit/8559ef8c600aefaec629870eac5f5c8953e18b16)), closes [#415](https://github.com/netflix/pollyjs/issues/415) ### Features * **adapter-node-http:** Upgrade nock to v13 ([#424](https://github.com/netflix/pollyjs/issues/424)) ([2d5b59e](https://github.com/netflix/pollyjs/commit/2d5b59ee0c33ea53a64321249246fcae0a616a3f)) * **node-server:** Upgrade dependencies ([#417](https://github.com/netflix/pollyjs/issues/417)) ([246a2f2](https://github.com/netflix/pollyjs/commit/246a2f29a88de9c25fc0739ea5e53a0130a58573)) ### BREAKING CHANGES * Upgrade url-parse to 1.5.0+ to fix CVE-2021-27515. This change could alter the final url generated for a request. * Adapter - `passthroughRequest` renamed to `onFetchResponse` - `respondToRequest` renamed to `onRespond` * Persister - `findRecording` renamed to `onFindRecording` - `saveRecording` renamed to `onSaveRecording` - `deleteRecording` renamed to `onDeleteRecording` * The `logging` configuration option has now been replaced with `logLevel`. This allows for more fine-grain control over what should be logged as well as silencing logs altogether. * Recording file name will no longer have trailing dashes * Use the standard `encoding` field on the generated har file instead of `_isBinary` and use `base64` encoding instead of `hex` to reduce the payload size. * Although backwards compatibility is not guaranteed, you can replace all occurrences of `"_isBinary": true` with `"encoding": "hex"` in the recorded HAR files. * @pollyjs dependencies for @pollyjs/ember have been moved to peer dependencies # [5.2.0](https://github.com/netflix/pollyjs/compare/v5.1.1...v5.2.0) (2021-11-24) ### Features * **ember:** Upgrade ember-cli-babel to ^7.26.6 ([#411](https://github.com/netflix/pollyjs/issues/411)) ([4352881](https://github.com/netflix/pollyjs/commit/4352881)) ## [5.1.1](https://github.com/netflix/pollyjs/compare/v5.1.0...v5.1.1) (2021-06-02) ### Bug Fixes * Handle failed arraybuffer instanceof checks ([#393](https://github.com/netflix/pollyjs/issues/393)) ([247be0a](https://github.com/netflix/pollyjs/commit/247be0a)) # [5.1.0](https://github.com/netflix/pollyjs/compare/v5.0.2...v5.1.0) (2020-12-12) ### Bug Fixes * **adapter-puppeteer:** Add prependListener puppeteer@4.0.0 removed ([#368](https://github.com/netflix/pollyjs/issues/368)) ([6c35fd3](https://github.com/netflix/pollyjs/commit/6c35fd3)) ### Features * **core:** Add `overrideRecordingName` and `configure` to public API ([#372](https://github.com/netflix/pollyjs/issues/372)) ([cdbf513](https://github.com/netflix/pollyjs/commit/cdbf513)) ## [5.0.2](https://github.com/netflix/pollyjs/compare/v5.0.1...v5.0.2) (2020-12-06) ### Bug Fixes * **adapter-node-http:** Remove module monkey patching on disconnect ([#369](https://github.com/netflix/pollyjs/issues/369)) ([0cec43a](https://github.com/netflix/pollyjs/commit/0cec43a)) ## [5.0.1](https://github.com/netflix/pollyjs/compare/v5.0.0...v5.0.1) (2020-09-25) ### Bug Fixes * **adapter-xhr:** Only modify the `responseType` if it was changed ([#363](https://github.com/netflix/pollyjs/issues/363)) ([cff4e2d](https://github.com/netflix/pollyjs/commit/cff4e2d)) # [5.0.0](https://github.com/netflix/pollyjs/compare/v4.3.0...v5.0.0) (2020-06-23) ### Bug Fixes * **adapter-fetch:** Add statusText to the response ([#341](https://github.com/netflix/pollyjs/issues/341)) ([0d45953](https://github.com/netflix/pollyjs/commit/0d45953)) * **core:** Compute order based on id and recording name ([#342](https://github.com/netflix/pollyjs/issues/342)) ([42004d2](https://github.com/netflix/pollyjs/commit/42004d2)) ### Features * Remove deprecated Persister.name and Adapter.name ([#343](https://github.com/netflix/pollyjs/issues/343)) ([1223ba0](https://github.com/netflix/pollyjs/commit/1223ba0)) ### BREAKING CHANGES * Persister.name and Adapter.name have been replaced with Persister.id and Adapter.id * **core:** A request's order is will now be computed based on its id and the recording name it will be persisted to. # [4.3.0](https://github.com/netflix/pollyjs/compare/v4.2.1...v4.3.0) (2020-05-18) ### Features * **adapter-fetch:** Add support for handling binary data ([#332](https://github.com/netflix/pollyjs/issues/332)) ([111bebf](https://github.com/netflix/pollyjs/commit/111bebf)) * **adapter-xhr:** Add support for handling binary data ([#333](https://github.com/netflix/pollyjs/issues/333)) ([48ea1d7](https://github.com/netflix/pollyjs/commit/48ea1d7)) * **core:** Add `flushRequestsOnStop` configuration option ([#335](https://github.com/netflix/pollyjs/issues/335)) ([ab4a2e1](https://github.com/netflix/pollyjs/commit/ab4a2e1)) ## [4.2.1](https://github.com/netflix/pollyjs/compare/v4.2.0...v4.2.1) (2020-04-30) ### Bug Fixes * **adapter-node-http:** Improve binary response body handling ([#329](https://github.com/netflix/pollyjs/issues/329)) ([9466989](https://github.com/netflix/pollyjs/commit/9466989)) # [4.2.0](https://github.com/netflix/pollyjs/compare/v4.1.0...v4.2.0) (2020-04-29) ### Features * **node-server:** Pass options to the CORS middleware via `corsOptions` ([3d991f5](https://github.com/netflix/pollyjs/commit/3d991f5)) # [4.1.0](https://github.com/netflix/pollyjs/compare/v4.0.4...v4.1.0) (2020-04-23) ### Bug Fixes * Improve abort handling ([#320](https://github.com/netflix/pollyjs/issues/320)) ([cc46bb4](https://github.com/netflix/pollyjs/commit/cc46bb4)) * Legacy persisters and adapters should register ([#325](https://github.com/netflix/pollyjs/issues/325)) ([8fd4d19](https://github.com/netflix/pollyjs/commit/8fd4d19)) ### Features * **persister:** Add `disableSortingHarEntries` option ([#321](https://github.com/netflix/pollyjs/issues/321)) ([0003c0e](https://github.com/netflix/pollyjs/commit/0003c0e)) ## [4.0.4](https://github.com/netflix/pollyjs/compare/v4.0.3...v4.0.4) (2020-03-21) ### Bug Fixes * Deprecates adapter & persister `name` in favor of `id` ([#310](https://github.com/netflix/pollyjs/issues/310)) ([41dd093](https://github.com/netflix/pollyjs/commit/41dd093)) * **adapter-node-http:** Bump nock version ([#319](https://github.com/netflix/pollyjs/issues/319)) ([7d361a6](https://github.com/netflix/pollyjs/commit/7d361a6)) ## [4.0.3](https://github.com/netflix/pollyjs/compare/v4.0.2...v4.0.3) (2020-01-30) ### Bug Fixes * **adapter-node-http:** Use requestBodyBuffers to parse body ([#304](https://github.com/netflix/pollyjs/issues/304)) ([113fec5](https://github.com/netflix/pollyjs/commit/113fec5)) ## [4.0.2](https://github.com/netflix/pollyjs/compare/v4.0.1...v4.0.2) (2020-01-29) ### Bug Fixes * **core:** Strict null query param handling ([#302](https://github.com/netflix/pollyjs/issues/302)) ([5cf70aa](https://github.com/netflix/pollyjs/commit/5cf70aa)) ## [4.0.1](https://github.com/netflix/pollyjs/compare/v4.0.0...v4.0.1) (2020-01-25) ### Bug Fixes * **ember:** Config read from project root ([7d6da38](https://github.com/netflix/pollyjs/commit/7d6da38)) # [4.0.0](https://github.com/netflix/pollyjs/compare/v3.0.2...v4.0.0) (2020-01-13) ### Bug Fixes * **adapter:** Clone the recording entry before mutating it ([#294](https://github.com/netflix/pollyjs/issues/294)) ([d7e1303](https://github.com/netflix/pollyjs/commit/d7e1303)) * **core:** Disconnect from all adapters when `pause` is called ([#291](https://github.com/netflix/pollyjs/issues/291)) ([5c655bf](https://github.com/netflix/pollyjs/commit/5c655bf)) ### chore * Drop node 8 support ([#292](https://github.com/netflix/pollyjs/issues/292)) ([4448be5](https://github.com/netflix/pollyjs/commit/4448be5)) ### Features * **core:** Provide the request as an argument to matchRequestsBy methods ([#293](https://github.com/netflix/pollyjs/issues/293)) ([4e3163f](https://github.com/netflix/pollyjs/commit/4e3163f)) * **core:** Remove deprecated `recordIfExpired` option ([#295](https://github.com/netflix/pollyjs/issues/295)) ([5fe991d](https://github.com/netflix/pollyjs/commit/5fe991d)) ### BREAKING CHANGES * **core:** `recordIfExpired` is no longer supported, please use `expiryStrategy` instead. * Drop support for Node 8 as it is now EOL * **core:** Calling `polly.pause()` will now disconnect from all connected adapters instead of setting the mode to passthrough. Calling `polly.play()` will reconnect to the disconnected adapters before pause was called. ## [3.0.2](https://github.com/netflix/pollyjs/compare/v3.0.1...v3.0.2) (2020-01-08) ### Bug Fixes * **adapter-node-http:** Bump nock version to correctly handle re… ([#289](https://github.com/netflix/pollyjs/issues/289)) ([8d0ae97](https://github.com/netflix/pollyjs/commit/8d0ae97)), closes [#278](https://github.com/netflix/pollyjs/issues/278) ## [3.0.1](https://github.com/netflix/pollyjs/compare/v3.0.0...v3.0.1) (2019-12-25) ### Bug Fixes * **adapter-fetch:** Fix "failed to construct Request" issue ([#287](https://github.com/netflix/pollyjs/issues/287)) ([d17ab9b](https://github.com/netflix/pollyjs/commit/d17ab9b)), closes [#286](https://github.com/netflix/pollyjs/issues/286) # [3.0.0](https://github.com/netflix/pollyjs/compare/v2.7.0...v3.0.0) (2019-12-18) ### Bug Fixes * **ember:** loads polly config for ember by its own module ([#277](https://github.com/netflix/pollyjs/issues/277)) ([0569040](https://github.com/netflix/pollyjs/commit/0569040)) ### BREAKING CHANGES * **ember:** moves location of polly configuration # [2.7.0](https://github.com/netflix/pollyjs/compare/v2.6.3...v2.7.0) (2019-11-21) ### Bug Fixes * **adapter-node-http:** Correctly handle uploading binary data ([#257](https://github.com/netflix/pollyjs/issues/257)) ([31f0e0a](https://github.com/netflix/pollyjs/commit/31f0e0a)) ### Features * **adapter-node-http:** Upgrade nock to v11.x ([#273](https://github.com/netflix/pollyjs/issues/273)) ([5d42cbd](https://github.com/netflix/pollyjs/commit/5d42cbd)) ## [2.6.3](https://github.com/netflix/pollyjs/compare/v2.6.2...v2.6.3) (2019-09-30) ### Bug Fixes * use watch strategy ([#236](https://github.com/netflix/pollyjs/issues/236)) ([5b4edf3](https://github.com/netflix/pollyjs/commit/5b4edf3)) * **adapter-fetch:** Correctly handle Request instance passed into fetch ([#259](https://github.com/netflix/pollyjs/issues/259)) ([593ecb9](https://github.com/netflix/pollyjs/commit/593ecb9)) ## [2.6.2](https://github.com/netflix/pollyjs/compare/v2.6.1...v2.6.2) (2019-08-05) ### Bug Fixes * Bowser.getParser empty string UserAgent error ([#246](https://github.com/netflix/pollyjs/issues/246)) ([2c9c4b9](https://github.com/netflix/pollyjs/commit/2c9c4b9)) ### Features * Adds an in-memory persister to test polly internals ([#237](https://github.com/netflix/pollyjs/issues/237)) ([5a6fda6](https://github.com/netflix/pollyjs/commit/5a6fda6)) ## [2.6.1](https://github.com/netflix/pollyjs/compare/v2.6.0...v2.6.1) (2019-08-01) ### Bug Fixes * **persister:** Default to empty string if userAgent is empty ([#242](https://github.com/netflix/pollyjs/issues/242)) ([c46d65c](https://github.com/netflix/pollyjs/commit/c46d65c)) # [2.6.0](https://github.com/netflix/pollyjs/compare/v2.5.0...v2.6.0) (2019-07-17) ### Bug Fixes * **adapter-fetch:** Handle `Request` objects as URLs ([#220](https://github.com/netflix/pollyjs/issues/220)) ([bb28d54](https://github.com/netflix/pollyjs/commit/bb28d54)) ### Features * **core:** Improved logging ([#217](https://github.com/netflix/pollyjs/issues/217)) ([3e876a8](https://github.com/netflix/pollyjs/commit/3e876a8)) * PollyError and improved adapter error handling ([#234](https://github.com/netflix/pollyjs/issues/234)) ([23a2127](https://github.com/netflix/pollyjs/commit/23a2127)) # [2.5.0](https://github.com/netflix/pollyjs/compare/v2.4.0...v2.5.0) (2019-06-06) ### Features * **adapter-xhr:** Support `context` option ([65b3c38](https://github.com/netflix/pollyjs/commit/65b3c38)) # [2.4.0](https://github.com/netflix/pollyjs/compare/v2.3.2...v2.4.0) (2019-04-27) ### Features * **core:** Improved control flow with `times` and `stopPropagation` ([#202](https://github.com/netflix/pollyjs/issues/202)) ([2c8231e](https://github.com/netflix/pollyjs/commit/2c8231e)) ## [2.3.2](https://github.com/netflix/pollyjs/compare/v2.3.1...v2.3.2) (2019-04-09) ### Bug Fixes * **adapter-puppeteer:** Remove other resource type matching ([#197](https://github.com/netflix/pollyjs/issues/197)) ([ea6bfcc](https://github.com/netflix/pollyjs/commit/ea6bfcc)) ## [2.3.1](https://github.com/netflix/pollyjs/compare/v2.3.0...v2.3.1) (2019-03-06) ### Bug Fixes * **adapter-fetch:** Correctly handle key/value pairs headers ([dc0323d](https://github.com/netflix/pollyjs/commit/dc0323d)) # [2.3.0](https://github.com/netflix/pollyjs/compare/v2.2.0...v2.3.0) (2019-02-27) ### Features * **core:** Filter requests matched by a route handler ([#189](https://github.com/netflix/pollyjs/issues/189)) ([5d57c32](https://github.com/netflix/pollyjs/commit/5d57c32)) # [2.2.0](https://github.com/netflix/pollyjs/compare/v2.1.0...v2.2.0) (2019-02-20) ### Features * Add `error` event and improve error handling ([#185](https://github.com/netflix/pollyjs/issues/185)) ([3694ebc](https://github.com/netflix/pollyjs/commit/3694ebc)) # [2.1.0](https://github.com/netflix/pollyjs/compare/v2.0.0...v2.1.0) (2019-02-04) ### Bug Fixes * **adapter:** Log information if request couldn't be found in recording ([#172](https://github.com/netflix/pollyjs/issues/172)) ([8dcdf7b](https://github.com/netflix/pollyjs/commit/8dcdf7b)) * **adapter-xhr:** Xhr.send should not be an async method ([#173](https://github.com/netflix/pollyjs/issues/173)) ([eb3a6eb](https://github.com/netflix/pollyjs/commit/eb3a6eb)) * Correctly handle array header values ([#179](https://github.com/netflix/pollyjs/issues/179)) ([fb7dbb4](https://github.com/netflix/pollyjs/commit/fb7dbb4)) ### Features * **core:** Add removeHeader, removeHeaders, and allow empty headers ([#176](https://github.com/netflix/pollyjs/issues/176)) ([1dfae5a](https://github.com/netflix/pollyjs/commit/1dfae5a)) # [2.0.0](https://github.com/netflix/pollyjs/compare/v1.4.2...v2.0.0) (2019-01-29) * feat(adapter-node-http): Use `nock` under the hood instead of custom implementation (#166) ([62374f4](https://github.com/netflix/pollyjs/commit/62374f4)), closes [#166](https://github.com/netflix/pollyjs/issues/166) ### Bug Fixes * **adapter:** Test for navigator before accessing ([#165](https://github.com/netflix/pollyjs/issues/165)) ([7200255](https://github.com/netflix/pollyjs/commit/7200255)) * **ember:** Remove Node 6 from supported versions ([#169](https://github.com/netflix/pollyjs/issues/169)) ([07b2b4e](https://github.com/netflix/pollyjs/commit/07b2b4e)) * **persister:** Only persist post data if a request has a body ([#171](https://github.com/netflix/pollyjs/issues/171)) ([f62d318](https://github.com/netflix/pollyjs/commit/f62d318)) ### chore * Remove support for Node 6 ([#167](https://github.com/netflix/pollyjs/issues/167)) ([a08a8cf](https://github.com/netflix/pollyjs/commit/a08a8cf)) ### Features * Make PollyRequest.respond accept a response object ([#168](https://github.com/netflix/pollyjs/issues/168)) ([5b07b26](https://github.com/netflix/pollyjs/commit/5b07b26)) * Simplify adapter implementation ([#154](https://github.com/netflix/pollyjs/issues/154)) ([12c8601](https://github.com/netflix/pollyjs/commit/12c8601)) ### BREAKING CHANGES * The node-http adapter no longer accepts the `transports` option * Any adapters calling `pollyRequest.respond` should pass it a response object instead of the previous 3 arguments (statusCode, headers, body). * Polly will no longer actively support Node 6 * Changes to the base adapter implementation and external facing API ## [1.4.2](https://github.com/netflix/pollyjs/compare/v1.4.1...v1.4.2) (2019-01-16) ### Bug Fixes * **adapter-node-http:** Fix unhandled rejection if connection fails ([#160](https://github.com/netflix/pollyjs/issues/160)) ([12fcfa7](https://github.com/netflix/pollyjs/commit/12fcfa7)) * **adapter-node-http:** Pause socket on original request ([#162](https://github.com/netflix/pollyjs/issues/162)) ([8f0c56c](https://github.com/netflix/pollyjs/commit/8f0c56c)) ### Features * Lint other filetypes with prettier ([#152](https://github.com/netflix/pollyjs/issues/152)) ([78d1af8](https://github.com/netflix/pollyjs/commit/78d1af8)) ## [1.4.1](https://github.com/netflix/pollyjs/compare/v1.4.0...v1.4.1) (2018-12-13) ### Bug Fixes * **utils:** Support arrays & nested objects in query params ([#148](https://github.com/netflix/pollyjs/issues/148)) ([7e846b0](https://github.com/netflix/pollyjs/commit/7e846b0)) # [1.4.0](https://github.com/netflix/pollyjs/compare/v1.3.2...v1.4.0) (2018-12-07) ### Bug Fixes * **adapter-fetch:** Deprecate usage in Node in favor of node-http ([#146](https://github.com/netflix/pollyjs/issues/146)) ([001ccdd](https://github.com/netflix/pollyjs/commit/001ccdd)) ### Features * Node HTTP Adapter ([#128](https://github.com/netflix/pollyjs/issues/128)) ([fa059a4](https://github.com/netflix/pollyjs/commit/fa059a4)) ## [1.3.2](https://github.com/netflix/pollyjs/compare/v1.3.1...v1.3.2) (2018-11-29) **Note:** Version bump only for package pollyjs ## [1.3.1](https://github.com/netflix/pollyjs/compare/v1.2.0...v1.3.1) (2018-11-28) ### Bug Fixes * Support URL objects ([#139](https://github.com/netflix/pollyjs/issues/139)) ([cf0d755](https://github.com/netflix/pollyjs/commit/cf0d755)) * **core:** Handle trailing slashes when generating route names ([#142](https://github.com/netflix/pollyjs/issues/142)) ([19147f7](https://github.com/netflix/pollyjs/commit/19147f7)) * **core:** Ignore `context` options from being deep merged ([#144](https://github.com/netflix/pollyjs/issues/144)) ([2123d83](https://github.com/netflix/pollyjs/commit/2123d83)) * **core:** Support multiple handlers for same paths ([#141](https://github.com/netflix/pollyjs/issues/141)) ([79e04b8](https://github.com/netflix/pollyjs/commit/79e04b8)) ### Features * **core:** Support custom functions in matchRequestsBy config options ([#138](https://github.com/netflix/pollyjs/issues/138)) ([626a84c](https://github.com/netflix/pollyjs/commit/626a84c)) * Add an onIdentifyRequest hook to allow adapter level serialization ([#140](https://github.com/netflix/pollyjs/issues/140)) ([548002c](https://github.com/netflix/pollyjs/commit/548002c)) # 1.2.0 (2018-09-16) ### Bug Fixes * **adapter-puppeteer:** Do not intercept CORS preflight requests ([#90](https://github.com/netflix/pollyjs/issues/90)) ([53ad433](https://github.com/netflix/pollyjs/commit/53ad433)) * Changes self to global, rollup-plugin-node-globals makes isomorphic ([#54](https://github.com/netflix/pollyjs/issues/54)) ([3811e9d](https://github.com/netflix/pollyjs/commit/3811e9d)) * **core:** Freeze request after emitting afterResponse. ([66a2b64](https://github.com/netflix/pollyjs/commit/66a2b64)) * Allow 204 responses without a body ([#101](https://github.com/netflix/pollyjs/issues/101)) ([20b4125](https://github.com/netflix/pollyjs/commit/20b4125)) * Browser (UMD) build now bundles corejs ([#106](https://github.com/netflix/pollyjs/issues/106)) ([ec62fc0](https://github.com/netflix/pollyjs/commit/ec62fc0)) * Bumping core within Ember ([af4faa1](https://github.com/netflix/pollyjs/commit/af4faa1)) * Config expiresIn can contain periods. i.e, 1.5 weeks ([e9c7aaa](https://github.com/netflix/pollyjs/commit/e9c7aaa)) * Correctly normalize relative URLs ([b9b23cd](https://github.com/netflix/pollyjs/commit/b9b23cd)) * Creator cleanup and persister assertion ([#67](https://github.com/netflix/pollyjs/issues/67)) ([19fee5a](https://github.com/netflix/pollyjs/commit/19fee5a)) * Do not display node server listening banner in quiet mode ([1be57a7](https://github.com/netflix/pollyjs/commit/1be57a7)) * Ensure polly's middleware goes before ember-cli's ([#36](https://github.com/netflix/pollyjs/issues/36)) ([43db361](https://github.com/netflix/pollyjs/commit/43db361)) * Improve support for relative URLs ([#78](https://github.com/netflix/pollyjs/issues/78)) ([2c0083e](https://github.com/netflix/pollyjs/commit/2c0083e)), closes [#76](https://github.com/netflix/pollyjs/issues/76) * Loosen up global XHR native check ([#69](https://github.com/netflix/pollyjs/issues/69)) ([79cdd96](https://github.com/netflix/pollyjs/commit/79cdd96)) * Proxy route.params onto the request instead of mutating req ([5bcd4f9](https://github.com/netflix/pollyjs/commit/5bcd4f9)) * Puppeteer 1.7.0 support ([#100](https://github.com/netflix/pollyjs/issues/100)) ([e208b38](https://github.com/netflix/pollyjs/commit/e208b38)) * Puppeteer CORS request matching ([#110](https://github.com/netflix/pollyjs/issues/110)) ([7831115](https://github.com/netflix/pollyjs/commit/7831115)) * Rest server on Windows ([be5c473](https://github.com/netflix/pollyjs/commit/be5c473)) * **core:** Set `url` on the fetch Response object ([#44](https://github.com/netflix/pollyjs/issues/44)) ([f5980cf](https://github.com/netflix/pollyjs/commit/f5980cf)), closes [#43](https://github.com/netflix/pollyjs/issues/43) * **ember:** Fix auto-register and add tests to cover ([24c15bd](https://github.com/netflix/pollyjs/commit/24c15bd)) * **persister:** Handle concurrent find requests ([#88](https://github.com/netflix/pollyjs/issues/88)) ([0e02414](https://github.com/netflix/pollyjs/commit/0e02414)) ### Features * Abort and passthrough from an intercept ([#57](https://github.com/netflix/pollyjs/issues/57)) ([4ebacb8](https://github.com/netflix/pollyjs/commit/4ebacb8)) * Class events and EventEmitter ([#52](https://github.com/netflix/pollyjs/issues/52)) ([0a3d591](https://github.com/netflix/pollyjs/commit/0a3d591)) * Cleanup event handler logic + rename some event names ([78dbb5d](https://github.com/netflix/pollyjs/commit/78dbb5d)) * Convert recordings to be HAR compliant ([#45](https://github.com/netflix/pollyjs/issues/45)) ([e622640](https://github.com/netflix/pollyjs/commit/e622640)) * Custom persister support ([8bb313c](https://github.com/netflix/pollyjs/commit/8bb313c)) * Fetch adapter support for `context` provided via adapterOptions ([#66](https://github.com/netflix/pollyjs/issues/66)) ([82ebd09](https://github.com/netflix/pollyjs/commit/82ebd09)) * Improved adapter and persister registration ([#62](https://github.com/netflix/pollyjs/issues/62)) ([164dbac](https://github.com/netflix/pollyjs/commit/164dbac)) * Keyed persister & adapter options ([#60](https://github.com/netflix/pollyjs/issues/60)) ([29ed8e1](https://github.com/netflix/pollyjs/commit/29ed8e1)) * Make recording size limit configurable ([#40](https://github.com/netflix/pollyjs/issues/40)) ([d4be431](https://github.com/netflix/pollyjs/commit/d4be431)) * Move more response methods to shared base class ([#74](https://github.com/netflix/pollyjs/issues/74)) ([4f845e5](https://github.com/netflix/pollyjs/commit/4f845e5)) * Node File System Persister ([#61](https://github.com/netflix/pollyjs/issues/61)) ([0a0eeca](https://github.com/netflix/pollyjs/commit/0a0eeca)) * Presets persisterOptions.host to the node server default ([0b47838](https://github.com/netflix/pollyjs/commit/0b47838)) * Puppeteer Adapter ([#64](https://github.com/netflix/pollyjs/issues/64)) ([f902c6d](https://github.com/netflix/pollyjs/commit/f902c6d)) * Use status code 204 in place of 404. ([#5](https://github.com/netflix/pollyjs/issues/5)) ([930c492](https://github.com/netflix/pollyjs/commit/930c492)) * **core:** Add `json` property to `Request` ([bb8e1cb](https://github.com/netflix/pollyjs/commit/bb8e1cb)), closes [#7](https://github.com/netflix/pollyjs/issues/7) * **core:** Default `Response` status code to 200 ([f42a281](https://github.com/netflix/pollyjs/commit/f42a281)), closes [#6](https://github.com/netflix/pollyjs/issues/6) * Wait for all handled requests to resolve via `.flush()` ([#75](https://github.com/netflix/pollyjs/issues/75)) ([a3113b7](https://github.com/netflix/pollyjs/commit/a3113b7)) * **core:** Normalize headers by lower-casing all keys ([#42](https://github.com/netflix/pollyjs/issues/42)) ([02a4767](https://github.com/netflix/pollyjs/commit/02a4767)) * **core:** Server level configuration ([#80](https://github.com/netflix/pollyjs/issues/80)) ([0f32d9b](https://github.com/netflix/pollyjs/commit/0f32d9b)) * **node-server:** Add cors support to express server to pass-through all requests ([223ce4e](https://github.com/netflix/pollyjs/commit/223ce4e)) * **persister:** Add `keepUnusedRequests` config option ([#108](https://github.com/netflix/pollyjs/issues/108)) ([3f5f5b2](https://github.com/netflix/pollyjs/commit/3f5f5b2)) * **persister:** Cache recordings ([#31](https://github.com/netflix/pollyjs/issues/31)) ([a04d7a7](https://github.com/netflix/pollyjs/commit/a04d7a7)) ### Reverts * "Update commitlint.config.js" ([65e6996](https://github.com/netflix/pollyjs/commit/65e6996)) * Add `json` property to `Request` ([4ea50e8](https://github.com/netflix/pollyjs/commit/4ea50e8)) * Revert "Update commitlint.config.js" ([6624cb5](https://github.com/netflix/pollyjs/commit/6624cb5)) * Revert Use docsify GA plugin ([35ace6f](https://github.com/netflix/pollyjs/commit/35ace6f)) * Use docsify GA plugin ([cf5f1c5](https://github.com/netflix/pollyjs/commit/cf5f1c5)) ### BREAKING CHANGES * __Adapters__ ```js import { XHRAdapter, FetchAdapter } from '@pollyjs/core'; // Register the xhr adapter so its accessible by all future polly instances Polly.register(XHRAdapter); polly.configure({ adapters: ['xhr', FetchAdapter] }); ``` __Persister__ ```js import { LocalStoragePersister, RESTPersister } from '@pollyjs/core'; // Register the local-storage persister so its accessible by all future polly instances Polly.register(LocalStoragePersister); polly.configure({ persister: 'local-storage' }); polly.configure({ persister: RESTPersister }); ``` * Recordings now produce HAR compliant json. Please delete existing recordings. * **core:** With this change, request ids will resolve to a different hash meaning that users will have to rerecord. * Relative URLs will have different hashes and will require to re-record. # Changelog ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/) ## Getting Started Install required global dependencies: ```bash npm install -g yarn ``` Check out the code and go into the pollyjs directory: ```bash git clone https://github.com/netflix/pollyjs.git cd pollyjs ``` Install the dependencies and bootstrap the monorepo: ```bash yarn ``` The code for individual packages of this monorepo are in `packages/@pollyjs/*`. Within any of the packages in this monorepo you'll generally use the npm package scripts to manage the project, E.g. `yarn run test` or `yarn run lint`. Run `yarn run` for a list of available commands. ## Running Tests ### Full Suite To run the full test suite, from the root directory run: ```bash yarn test ``` This will perform a full bootstrap, clean and build on all of the sub-packages and test suite, stand up the node server, run the test suite and then terminate. ### Running only changed tests While developing, it may become cumbersome to run the entire suite after each change. In one terminal tab, run the following: ```bash yarn watch ``` This will build all of the sub-packages and test suite, watch for any changes, and perform incremental builds when the suite or packages are changed. Wait until the build settles (i.e. build output stops scrolling). Then, in another tab: ```bash yarn test:watch ``` This will launch an interactive test runner (`testem`), which will automatically detect and re-run changed tests. To manually re-run the suite, hit `enter`. To exit, hit `q` and then `ctrl-c` your watch process. For more information, look at the [testem docs](https://github.com/testem/testem). ## Running Node Tests with Chrome Inspector To run the node test suite with node inspector support, run from the root directory: ```bash yarn test:ci -l Node:debug ``` Next, attach Chrome to the running process by visiting [chrome://inspect/#devices](chrome://inspect/#devices) ## Running Docs All the documentation can be found in the root level `docs` directory. Running the following command will stand up the docs server which will watch for changes. ```bash yarn docs:serve ``` ## Conventional Commits Lerna depends on the use of the [Conventional Commits Specification](https://conventionalcommits.org/) to determine the version bump and generate CHANGELOG.md files. Make sure your commits and the title of your PRs follow the spec. A pre-commit hook and CI test have been added to further enforce this requirement. ## Tips for Getting Your Pull Request Accepted 1. Make sure all new features are tested and the tests pass. 2. Bug fixes must include a test case demonstrating the error that it fixes. 3. Open an issue first and seek advice for your change before submitting a pull request. Large features which have never been discussed are unlikely to be accepted. **You have been warned.** ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 Netflix Inc and @pollyjs contributors 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 http://www.apache.org/licenses/LICENSE-2.0 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. ================================================ FILE: OSSMETADATA ================================================ osslifecycle=active ================================================ FILE: README.md ================================================

Polly.JS

Record, Replay, and Stub HTTP Interactions

[![Build Status](https://travis-ci.com/Netflix/pollyjs.svg?branch=master)](https://travis-ci.com/Netflix/pollyjs) [![license](https://img.shields.io/github/license/Netflix/pollyjs.svg)](http://www.apache.org/licenses/LICENSE-2.0) Polly.JS is a standalone, framework-agnostic JavaScript library that enables recording, replaying, and stubbing of HTTP interactions. By tapping into multiple request APIs across both Node & the browser, Polly.JS is able to mock requests and responses with little to no configuration while giving you the ability to take full control of each request with a simple, powerful, and intuitive API. > Interested in contributing or just seeing Polly in action? Head over to [CONTRIBUTING.md](CONTRIBUTING.md) to learn how to spin up the project! ## Why Polly? Keeping fixtures and factories in parity with your APIs can be a time consuming process. Polly alleviates this process by recording and maintaining actual server responses while also staying flexible. - Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests. - Use Polly's client-side server to modify or intercept requests and responses to simulate different application states (e.g. loading, error, etc.). ## Features - 🚀 Node & Browser Support - ⚡️️ Simple, Powerful, & Intuitive API - 💎 First Class Mocha & QUnit Test Helpers - 🔥 Intercept, Pass-Through, and Attach Events - 📼 Record to Disk or Local Storage - ⏱ Slow Down or Speed Up Time ## Getting Started Check out the [Quick Start](https://netflix.github.io/pollyjs/#/quick-start) documentation to get started. ## Usage Let's take a look at what an example test case would look like using Polly. ```js import { Polly } from '@pollyjs/core'; import XHRAdapter from '@pollyjs/adapter-xhr'; import FetchAdapter from '@pollyjs/adapter-fetch'; import RESTPersister from '@pollyjs/persister-rest'; /* Register the adapters and persisters we want to use. This way all future polly instances can access them by name. */ Polly.register(XHRAdapter); Polly.register(FetchAdapter); Polly.register(RESTPersister); describe('Netflix Homepage', function () { it('should be able to sign in', async function () { /* Create a new polly instance. Connect Polly to both fetch and XHR browser APIs. By default, it will record any requests that it hasn't yet seen while replaying ones it has already recorded. */ const polly = new Polly('Sign In', { adapters: ['xhr', 'fetch'], persister: 'rest' }); const { server } = polly; /* Intercept all Google Analytic requests and respond with a 200 */ server .get('/google-analytics/*path') .intercept((req, res) => res.sendStatus(200)); /* Pass-through all GET requests to /coverage */ server.get('/coverage').passthrough(); /* start: pseudo test code */ await visit('/login'); await fillIn('email', 'polly@netflix.com'); await fillIn('password', '@pollyjs'); await submit(); /* end: pseudo test code */ expect(location.pathname).to.equal('/browse'); /* Calling `stop` will persist requests as well as disconnect from any connected browser APIs (e.g. fetch or XHR). */ await polly.stop(); }); }); ``` The above test case would generate the following [HAR](http://www.softwareishard.com/blog/har-12-spec/) file which Polly will use to replay the sign-in response when the test is rerun: ```json { "log": { "_recordingName": "Sign In", "browser": { "name": "Chrome", "version": "67.0" }, "creator": { "name": "Polly.JS", "version": "0.5.0", "comment": "persister:rest" }, "entries": [ { "_id": "06f06e6d125cbb80896c41786f9a696a", "_order": 0, "cache": {}, "request": { "bodySize": 51, "cookies": [], "headers": [ { "name": "content-type", "value": "application/json; charset=utf-8" } ], "headersSize": 97, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json; charset=utf-8", "text": "{\"email\":\"polly@netflix.com\",\"password\":\"@pollyjs\"}" }, "queryString": [], "url": "https://netflix.com/api/v1/login" }, "response": { "bodySize": 0, "content": { "mimeType": "text/plain; charset=utf-8", "size": 0 }, "cookies": [], "headers": [], "headersSize": 0, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, "startedDateTime": "2018-06-29T17:31:55.348Z", "time": 11, "timings": { "blocked": -1, "connect": -1, "dns": -1, "receive": 0, "send": 0, "ssl": -1, "wait": 11 } } ], "pages": [], "version": "1.2" } } ``` ## Prior Art The "Client Server" API of Polly is heavily influenced by the very popular mock server library [pretender](https://github.com/pretenderjs/pretender). Pretender supports XHR and Fetch stubbing and is a great lightweight alternative to Polly if your project does not require persisting capabilities or Node adapters. Thank you to all contributors especially the maintainers: [trek](https://github.com/trek), [stefanpenner](https://github.com/stefanpenner), and [xg-wang](https://github.com/xg-wang). ## Contributors [//]: contributor-faces [//]: contributor-faces ## License Copyright (c) 2018 Netflix, Inc. 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 [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 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. ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/_coverpage.md ================================================ ![logo](assets/images/wordmark-logo-alt.png) > Record, replay, and stub HTTP interactions. - 🚀 Node & Browser Support - ⚡️️ Simple, Powerful, & Intuitive API - 💎 First Class Mocha & QUnit Test Helpers - 🔥 Intercept, Pass-Through, and Attach Events - 📼 Record to Disk or Local Storage - ⏱ Slow Down or Speed Up Time
GitHub Get Started
![color](#ffffff) ================================================ FILE: docs/_sidebar.md ================================================ - Getting Started - [Overview](README.md) - [Quick Start](quick-start.md) - [Examples](examples.md) - Test Frameworks - [Mocha](test-frameworks/mocha.md) - [QUnit](test-frameworks/qunit.md) - [Jest & Jasmine](test-frameworks/jest-jasmine.md) - Frameworks - [Ember CLI](frameworks/ember-cli.md) - Adapters - [Fetch](adapters/fetch.md) - [Node HTTP](adapters/node-http.md) - [Playwright](adapters/playwright.md) - [Puppeteer](adapters/puppeteer.md) - [XHR](adapters/xhr.md) - [Custom](adapters/custom.md) - Persisters - [File System](persisters/fs.md) - [Local Storage](persisters/local-storage.md) - [REST](persisters/rest.md) - [Custom](persisters/custom.md) - Client Server - [Overview](server/overview.md) - [API](server/api.md) - [Events & Middleware](server/events-and-middleware.md) - [Route Handler](server/route-handler.md) - [Request](server/request.md) - [Response](server/response.md) - [Event](server/event.md) - Node Server - [Overview](node-server/overview.md) - [Express Integrations](node-server/express-integrations.md) - CLI - [Overview](cli/overview.md) - [Commands](cli/commands.md) - Reference - [API](api.md) - [Configuration](configuration.md) - [Contributing](CONTRIBUTING.md) ================================================ FILE: docs/adapters/custom.md ================================================ # Custom Adapter If you need to create your own adapter or modify an pre-existing one, you've come to the right page! ## Creating a Custom Adapter The `@pollyjs/adapter` package provides an extendable base adapter class that contains core logic dependent on by the [Fetch](adapters/fetch) & [XHR](adapters/xhr) adapters. ### Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter -D ``` ### Usage ```js import Adapter from '@pollyjs/adapter'; class CustomAdapter extends Adapter { static get id() { return 'custom'; } onConnect() { /* Do something when the adapter is connect to */ } onDisconnect() { /* Do something when the adapter is disconnected from */ } async onFetchResponse(pollyRequest) { /* Do something when the adapter is connect to */ } /* optional */ async onRespond(pollyRequest) { const { statusCode, body, headers } = pollyRequest.response; /* Deliver the response to the user */ } } ``` The `Adapter` class provides the `handleRequest()` method which can be called from `onConnect`. It accepts request parameters and returns a PollyRequest object with a `response` property. The `onFetchResponse` method takes a PollyRequest object, makes a real HTTP request and returns the response as a `{ statusCode, headers, body }` object, where `body` is a string. The `onRespond()` method makes sure that the response has been delivered to the user. `pollyjs.flush()` will wait for all `onResponds()` calls to finish. You can omit the implementation of this method if no asynchronous delivery is required. ### Simple Fetch adapter example The following is a simple example of implementing an adapter for `fetch`. For full examples, please refer to the source code for the [Fetch](https://github.com/Netflix/pollyjs/blob/master/packages/@pollyjs/adapter-fetch/src/index.js) & [XHR](https://github.com/Netflix/pollyjs/blob/master/packages/%40pollyjs/adapter-xhr/src/index.js) adapters. ```js class FetchAdapter extends Adapter { static get id() { return 'fetch'; } onConnect() { this.originalFetch = window.fetch; window.fetch = async (url, options = {}) => { const { response } = await this.handleRequest({ url, method: options.method, headers: options.headers, body: options.body }); return new Response(response.body, { status: response.statusCode, statusText: response.statusText, headers: response.headers }); }; } onDisconnect() { window.fetch = this.originalFetch; } async onFetchResponse(pollyRequest) { const response = await this.originalFetch([ pollyRequest.url, { method: pollyRequest.method, headers: pollyRequest.headers, body: pollyRequest.body } ]); return { statusCode: response.status, headers: serializeHeaders(response.headers), body: await response.text() }; } } ``` ## Extending from an Existing Adapter The `@pollyjs/core` package exports the `XHRAdapter` and `FetchAdapter` classes, allowing you to modify them as needed. ```js import XHRAdapter from '@pollyjs/adapter-xhr'; import FetchAdapter from '@pollyjs/adapter-fetch'; class CustomXHRAdapter extends XHRAdapter {} class CustomFetchAdapter extends FetchAdapter {} ``` ## Registering & Connecting to a Custom Adapter You can register and connect to a custom adapter by passing an array to the `adapters` config where the first element is the name of your adapter and the second is the adapter class. ```js // Register and connect to a custom adapter: new Polly('Custom Adapter', { adapters: [MyCustomAdapterClass] }); // Register and connect to a custom adapter via .configure(): const polly = new Polly('Custom Adapter'); polly.configure({ adapters: [MyCustomAdapterClass] }); ``` ================================================ FILE: docs/adapters/fetch.md ================================================ # Fetch Adapter The fetch adapter wraps the global fetch method for seamless recording and replaying of requests. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter-fetch -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter-fetch -D ``` ## Usage Use the [configure](api#configure), [connectTo](api#connectto), and [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the adapter. ```js import { Polly } from '@pollyjs/core'; import FetchAdapter from '@pollyjs/adapter-fetch'; // Register the fetch adapter so its accessible by all future polly instances Polly.register(FetchAdapter); const polly = new Polly('', { adapters: ['fetch'] }); // Disconnect using the `configure` API polly.configure({ adapters: [] }); // Reconnect using the `connectTo` API polly.connectTo('fetch'); // Disconnect using the `disconnectFrom` API polly.disconnectFrom('fetch'); ``` ## Options ### context _Type_: `Object` _Default_: `global|self|window` The context object which contains the fetch API. Typically this is `window` or `self` in the browser and `global` in node. **Example** ```js polly.configure({ adapters: ['fetch'], adapterOptions: { fetch: { context: window } } }); ``` ================================================ FILE: docs/adapters/node-http.md ================================================ # Node HTTP Adapter 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. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter-node-http -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter-node-http -D ``` ## Usage Use the [configure](api#configure), [connectTo](api#connectto), and [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the adapter. ```js import { Polly } from '@pollyjs/core'; import NodeHttpAdapter from '@pollyjs/adapter-node-http'; // Register the node http adapter so its accessible by all future polly instances Polly.register(NodeHttpAdapter); const polly = new Polly('', { adapters: ['node-http'] }); // Disconnect using the `configure` API polly.configure({ adapters: [] }); // Reconnect using the `connectTo` API polly.connectTo('node-http'); // Disconnect using the `disconnectFrom` API polly.disconnectFrom('node-http'); ``` ================================================ FILE: docs/adapters/playwright.md ================================================ # Playwright Adapter 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. [README.md](https://raw.githubusercontent.com/redabacha/polly-adapter-playwright/master/README.md ':include :type=markdown') ================================================ FILE: docs/adapters/puppeteer.md ================================================ # Puppeteer Adapter The [Puppeteer](https://pptr.dev/) adapter attaches events to a given [page](https://pptr.dev/#?product=Puppeteer&show=api-class-page) instance allowing you to get the full power of Polly and Puppeteer. ## Installation ?> **NOTE** If you're using Puppeteer 1.7 or 1.8, you'll experience issues using passthrough requests. Please upgrade to the latest version of Puppeteer or use a version prior to 1.7. _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter-puppeteer -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter-puppeteer -D ``` ## Usage Use the [configure](api#configure), [connectTo](api#connectto), and [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the adapter. ```js import { Polly } from '@pollyjs/core'; import PuppeteerAdapter from '@pollyjs/adapter-puppeteer'; // Register the puppeteer adapter so its accessible by all future polly instances Polly.register(PuppeteerAdapter); const browser = await puppeteer.launch(); const page = await this.browser.newPage(); await page.setRequestInterception(true); const polly = new Polly('', { adapters: ['puppeteer'], adapterOptions: { puppeteer: { page } } }); // Disconnect using the `configure` API polly.configure({ adapters: [] }); // Reconnect using the `connectTo` API polly.connectTo('puppeteer'); // Disconnect using the `disconnectFrom` API polly.disconnectFrom('puppeteer'); ``` ## Options ### page _Type_: [Page](https://pptr.dev/#?product=Puppeteer&show=api-class-page) _Default_: `null` !> **NOTE:** This is a _required_ option. The Puppeteer page instance Polly should attach events to in order to intercept requests. **Example** ```js const browser = await puppeteer.launch(); const page = await this.browser.newPage(); await page.setRequestInterception(true); polly.configure({ adapters: ['puppeteer'], adapterOptions: { puppeteer: { page } } }); await page.goto('http://netflix.com'); ``` ### requestResourceTypes _Type_: `Array` _Default_: `['xhr', 'fetch']` The request [resource types](https://pptr.dev/#?product=Puppeteer&show=api-requestresourcetype) to intercept. **Example** ```js polly.configure({ adapterOptions: { puppeteer { requestResourceTypes: ['xhr'] } } }); ``` ================================================ FILE: docs/adapters/xhr.md ================================================ # XHR Adapter The XHR adapter uses Sinon's [Nise](https://github.com/sinonjs/nise) library to fake the global `XMLHttpRequest` object while wrapping the native one to allow for seamless recording and replaying of requests. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter-xhr -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter-xhr -D ``` ## Usage Use the [configure](api#configure), [connectTo](api#connectto), and [disconnectFrom](api#disconnectfrom) APIs to connect or disconnect from the adapter. ```js import { Polly } from '@pollyjs/core'; import XHRAdapter from '@pollyjs/adapter-xhr'; // Register the xhr adapter so its accessible by all future polly instances Polly.register(XHRAdapter); const polly = new Polly('', { adapters: ['xhr'] }); // Disconnect using the `configure` API polly.configure({ adapters: [] }); // Reconnect using the `connectTo` API polly.connectTo('xhr'); // Disconnect using the `disconnectFrom` API polly.disconnectFrom('xhr'); ``` ## Options ### context _Type_: `Object` _Default_: `global|self|window` The context object which contains the XMLHttpRequest object. Typically this is `window` or `self` in the browser and `global` in node. **Example** ```js polly.configure({ adapters: ['xhr'], adapterOptions: { xhr: { context: window } } }); ``` ================================================ FILE: docs/api.md ================================================ # API ## Constructor Create a new Polly instance. | Param | Type | Description | | ------------- | -------- | ------------------------------------------------------------------------- | | recordingName | `String` | Name of the [recording](api#recordingName) to store the recordings under. | | config | `Object` | [Configuration](configuration) object | | **Returns** | `Polly` |   | **Example** ```js new Polly('', { /* ... */ }); ``` ## Events ### create Emitted when a Polly instance gets created. !> This is a synchronous event. **Example** ```js const listener = polly => { /* Do Something */ }; Polly.on('create', listener); Polly.off('create', listener); Polly.once('create', polly => { /* Do Something Once */ }); ``` ### stop Emitted when a Polly instance has successfully stopped. **Example** ```js const listener = polly => { /* Do Something */ }; Polly.on('stop', listener); Polly.off('stop', listener); Polly.once('stop', polly => { /* Do Something Once */ }); ``` ## Properties ### recordingName _Type_: `String` _Default_: `null` The recording name the recordings will be stored under. The provided name is sanitized as well as postfixed with a GUID. **Example** ```js new Polly('Wants a Cracker', { /* ... */ }); ``` Will save recordings to the following file: ```text recordings └── Wants-a-Cracker_1234 └── recording.json ``` **Example** ?> A recording can also have slashes to better organize recordings. ```js new Polly('Wants a Cracker/Cheddar'); ``` Will save recordings to the following file: ```text recordings └── Wants-a-Cracker_1234 └── Cheddar_5678 └── recording.json ``` ### mode _Type_: `String` _Default_: `'replay'` The current [mode](configuration#mode) polly is in. **Example** ```js const polly = new Polly(); polly.mode; // → 'replay' polly.record(); polly.mode; // → 'record' ``` ### persister _Type_: `Persister` _Default_: `null` The persister used to find and save recordings. ### server _Type_: `Server` _Default_: `Server` Every polly instance has a reference to a [client-side server](server/overview) which you can leverage to gain full control of all HTTP interactions as well as dictate how the Polly instance should handle them. ```js const { server } = polly; server.get('/movies').passthrough(); server.get('/series').intercept((req, res) => res.sendStatus(200)); ``` ## Methods ### configure Configure polly with the given configuration object. | Param | Type | Description | | ------ | -------- | ------------------------------------- | | config | `Object` | [Configuration](configuration) object | **Example** ```js polly.configure({ recordIfMissing: false }); ``` ### record Puts polly in recording mode. All requests going forward will be sent to the server and their responses will be recorded. **Example** ```js polly.record(); ``` ### replay Puts polly in replay mode. All requests going forward will be played back from a saved recording. **Example** ```js polly.replay(); ``` ### passthrough Puts polly in pass-through mode. All requests going forward will pass-through directly to the server without being recorded or replayed. **Example** ```js polly.passthrough(); ``` ### pause Disconnects the polly instance from all connected adapters. This ensures that no requests will be handled by the polly instance until calling [play](api#play) or manually connecting to a new adapter via [connectTo](api#connectTo). The previously connected adapters will be saved and can be restored by calling [play](api#play). ?> If using the [Puppeteer Adapter](adapters/puppeteer), you'll need to also disable request interception via `await page.setRequestInterception(false)`. **Example** ```js await fetch('/api/not-a-secret'); polly.pause(); // This and all subsequent requests will no longer be handled by polly await fetch('/api/secret'); ``` ### play Reconnects to the adapters that were disconnected when [pause](api#pause) was called. ?> If using the [Puppeteer Adapter](adapters/puppeteer), you'll need to also enable request interception via `await page.setRequestInterception(true)`. **Example** ```js await fetch('/api/not-a-secret'); polly.pause(); // This and all subsequent requests will no longer be handled by polly await fetch('/api/secret'); polly.play(); // This and all subsequent requests will again be handled by polly await fetch('/api/not-a-secret'); ``` ### stop Persist all recordings and disconnect from all adapters. !> This method is `async` and will resolve once all recordings have persisted and the instance has successfully torn down. | Param | Type | Description | | ------- | --------- | ----------- | | Returns | `Promise` |   | **Example** ```js await polly.stop(); ``` ### connectTo Connect to an adapter. | Param | Type | Description | | ----- | ----------------- | --------------------------------------- | | name | `String|Function` | The adapter name of class to connect to | **Example** ```js polly.connectTo('xhr'); polly.connectTo(XHRAdapter); ``` ### disconnectFrom Disconnect from an adapter. | Param | Type | Description | | ----- | ----------------- | -------------------------------------------- | | name | `String|Function` | The adapter name of class to disconnect from | **Example** ```js polly.disconnectFrom('xhr'); polly.disconnectFrom(XHRAdapter); ``` ### disconnect Disconnect from all connected adapters. **Example** ```js polly.disconnect(); ``` ### flush Returns a Promise that resolves once all requests handled by Polly have resolved. | Param | Type | Description | | ------- | --------- | ----------- | | Returns | `Promise` |   | **Example** ```js await polly.flush(); ``` ================================================ FILE: docs/assets/styles.css ================================================ :root { --theme-color: #e50914; --theme-color-dark: #b20710; --text-color-base: #2e2e46; --text-color-secondary: #646473; --text-color-tertiary: #81818e; } body { font-size: 100%; line-height: 1.5; color: var(--text-color-base); } * { text-decoration: none !important; } a { transition: all 0.3s linear; } .github-corner { z-index: 5; } /****** Cover Page ******/ section.cover { padding-bottom: 112px; /* fixed footer (Netflix) height */ height: auto; min-height: 100vh; /** * Intended to defeat this inline style on initial boot that flashes when on page load: * https://github.com/docsifyjs/docsify/blob/8352a1e489abc2a7b6361fe02d696e1891a031cd/src/core/render/tpl.js#L56-L70 */ background: #fff !important; } section.cover .cover-main { display: flex; justify-content: center; align-items: center; flex-direction: column; margin: 0; padding: 32px 16px 0; } section.cover img { width: 400px; } section.cover h1 { margin: 0.625rem 0 1rem; } section.cover blockquote, section.cover blockquote p { margin: 0; } section.cover ul { font-size: 1.25rem; line-height: 2rem; display: grid; text-align: left; grid-column-gap: 16px; grid-row-gap: 20px; grid-template-columns: repeat(2, 50%); list-style: none; max-width: unset; margin: 1.5em 0; } section.cover ul li { white-space: nowrap; } section.cover.show ~ .sidebar, section.cover.show ~ .sidebar-toggle { display: none; } .cover-main .netflix-logo { position: fixed; display: flex; align-items: center; background: #ffffff; bottom: 0; left: 0; width: 100%; padding: 40px; z-index: 5; } .cover-main .netflix-logo .logo { width: 125px; height: 32px; background: url('images/Netflix_Logo.png'); background-size: 100%; background-repeat: no-repeat; background-position: center center; } .cover-main .buttons { width: 100%; } .cover-main .buttons a { font-weight: 700; position: relative; display: inline-block; padding: 12px 25px; font-size: 14px; text-align: center; line-height: 18px; color: #221f1f; background: #fff; outline: none; border: none; background-color: #fff; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; margin: 0 1rem; color: var(--theme-color); overflow: hidden; transition: color 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); vertical-align: baseline; text-transform: uppercase; } .cover-main .buttons a:before, .cover-main .buttons a:after { content: ''; display: block; position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: 2px solid var(--theme-color); box-sizing: border-box; } .cover-main .buttons a:after { background: var(--theme-color); transform: translateX(-101%); transition: all 0.2s cubic-bezier(0.215, 0.61, 0.355, 1); } .cover-main .buttons a:hover { color: white; box-shadow: 0 5px 16px rgba(229, 9, 20, 0.3); } .cover-main .buttons a:hover:after { transform: translateX(0); } .cover-main .buttons a span { position: relative; z-index: 1; } @media (max-width: 850px) { section.cover ul { grid-template-columns: 100%; padding: 0; } section.cover ul li { text-align: center; } } @media (max-width: 450px) { section.cover ul li { white-space: normal; } .cover-main .buttons a { width: 100%; margin: 0.2rem 0; } } /****** Sidebar ******/ .sidebar .app-name-link img { height: 150px; } .sidebar ul li a { font-size: 15px; } .sidebar ul li a:hover { color: var(--theme-color); } .app-sub-sidebar li:before { display: none; } .sidebar .search .clear-button { cursor: pointer; } /****** Sidebar Toggle ******/ .sidebar-toggle { cursor: pointer; } body .sidebar-toggle { background: none; top: 1.5rem; left: calc(300px + 1.5rem); cursor: pointer; width: 1.5rem; height: 1.5rem; padding: 0; transition: left 0.25s ease-out; } body .sidebar-toggle span { background-color: var(--theme-color); height: 0.2rem; width: 1.5rem; position: absolute; left: 0; margin: 0; transform-origin: 0; border-radius: 1px; } body.close .sidebar-toggle { transition: left 0.25s ease-out; width: 1.5rem; height: 1.5rem; left: 1.5rem; } body.close .sidebar-toggle span { transform-origin: center; } body .sidebar-toggle span:nth-child(1) { top: 0; } body .sidebar-toggle span:nth-child(2) { top: 0.5rem; } body .sidebar-toggle span:nth-child(3) { top: 1rem; } .sidebar-toggle:hover { opacity: 0.8; } .sidebar-toggle .sidebar-toggle-button:hover { opacity: 1; } @media screen and (max-width: 768px) { body .sidebar-toggle { left: 1rem; } body.close .sidebar-toggle { left: calc(300px + 1.5rem); } } /****** Markdown General ******/ .markdown-section { padding: 30px 30px 40px; } .markdown-section a { text-decoration: none; border-bottom: 0.1rem solid var(--theme-color); transition: all 0.3s ease; } .markdown-section a:hover { border-color: var(--theme-color-dark); color: var(--theme-color-dark); } .markdown-section a.anchor { border: none; } /****** Markdown Table ******/ .markdown-section table { display: table; } .markdown-section table tr { border-width: 0.15rem 0; border-style: solid; border-color: #f1f1f2; } .markdown-section table thead tr { text-transform: uppercase; font-size: 90%; border-top: none; } .markdown-section table tbody tr:last-of-type { border-bottom: none; } .markdown-section table tr:nth-child(2n) { background-color: transparent; } .markdown-section table td, .markdown-section table th { border: none; padding: 1.1rem 0.5rem; text-align: left; } .markdown-section table td p { margin: 0; } .markdown-section blockquote { margin: 1em 0; } .markdown-section blockquote > p { font-weight: 500; } .markdown-section em, .markdown-section blockquote { color: var(--text-color-tertiary); } /****** CODE HIGHLIGHTING ******/ .token.string { color: #42b983; } .token.boolean, .token.number { color: var(--theme-color); } .lang-bash .token.function, .lang-json .token.property { color: #e96900; } /****** COPY TO CLIPBOARD ******/ .docsify-copy-code-button { font-size: 0.7em !important; } ================================================ FILE: docs/cli/commands.md ================================================ # Commands As of right now, the Polly CLI only knows one command but expect to see more in the near future! ## listen Start up a node server and listen for Polly requests via the [REST Persister](persisters/rest) to be able to record and replay recordings to and from disk. ### Usage ```text Usage: polly listen|l [options] start the server and listen for requests Options: -H, --host host -p, --port port number (default: 3000) -n, --api-namespace api namespace (default: polly) -d, --recordings-dir recordings directory (default: recordings) -q, --quiet disable the logging -h, --help output usage information ``` ================================================ FILE: docs/cli/overview.md ================================================ # Overview The `@pollyjs/cli` package provides a standalone CLI to quickly get you setup and ready to go. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/cli -g ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn global add @pollyjs/cli ``` ## Usage ```text Usage: polly [options] [command] Options: -v, --version output the version number -h, --help output usage information Commands: listen|l [options] start the server and listen for requests ``` ================================================ FILE: docs/configuration.md ================================================ # Configuration A Polly instance can be configured by passing a configuration object to the constructor's 2nd argument: ```js new Polly('', { recordIfMissing: false }); ``` Or via the [configure](api#configure) method on the instance: ```js const polly = new Polly(''); polly.configure({ recordIfMissing: false }); ``` ## Defaults [config.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/packages/@pollyjs/core/src/defaults/config.js ':include :type=code') ## logLevel _Type_: `'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'` _Default_: `'warn'` Set the log level for the polly instance. **Example** ```js polly.configure({ logLevel: 'info' }); ``` ## recordIfMissing _Type_: `Boolean` _Default_: `true` If a request's recording is not found, pass-through to the server and record the response. **Example** ```js polly.configure({ recordIfMissing: true }); ``` ## recordFailedRequests _Type_: `Boolean` _Default_: `false` If `false`, Polly will throw when attempting to persist any failed requests. A request is considered to be a failed request when its response's status code is `≥ 400`. **Example** ```js polly.configure({ recordFailedRequests: true }); ``` ## flushRequestsOnStop _Type_: `Boolean` _Default_: `false` Await any unresolved requests handled by the polly instance (via [flush](api#flush)) when [stop](api#stop) is called. **Example** ```js polly.configure({ flushRequestsOnStop: true }); ``` ## expiresIn _Type_: `String` _Default_: `null` After how long the recorded request will be considered expired from the time it was persisted. A recorded request is considered expired if the recording's `startedDateTime` plus the current `expiresIn` duration is in the past. **Example** ```js polly.configure({ expiresIn: '30d5h10m' // expires in 30 days, 5 hours, and 10 minutes }); polly.configure({ expiresIn: '5 min 10 seconds 100 milliseconds' // expires in 5 minutes, 10 seconds, and 100 milliseconds }); ``` ## expiryStrategy _Type_: `'warn' | 'error' | 'record'` _Default_: `'warn'` The strategy for what should occur when Polly tries to use an expired recording in `replay` mode. Can be one of the following: - `warn`: Log a console warning about the expired recording. - `error`: Throw an error. - `record`: Re-record by making a new network request. **Example** ```js polly.configure({ expiryStrategy: 'error' }); ``` ## mode _Type_: `String` _Default_: `'replay'` The Polly mode. Can be one of the following: - `replay`: Replay responses from recordings. - `record`: Force Polly to record all requests. This will overwrite recordings that already exist. - `passthrough`: Passes all requests through directly to the server without recording or replaying. **Example** ```js polly.configure({ mode: 'record' }); ``` ## adapters _Type_: `Array[String|Function]` _Default_: `[]` The adapter(s) polly will hook into. **Example** ```js import XHRAdapter from '@pollyjs/adapter-xhr'; import FetchAdapter from '@pollyjs/adapter-fetch'; // Register the xhr adapter so its accessible by all future polly instances Polly.register(XHRAdapter); polly.configure({ adapters: ['xhr', FetchAdapter] }); ``` ## adapterOptions _Type_: `Object` _Default_: `{}` Options to be passed into the adapters keyed by the adapter name. ?> **NOTE:** Check out the appropriate documentation pages for each adapter for more details. **Example** ```js polly.configure({ adapterOptions: { fetch: { context: win } } }); ``` ## persister _Type_: `String|Function` _Default_: `null` The persister to use for recording and replaying requests. **Example** ```js import RESTPersister from '@pollyjs/persister-rest'; import LocalStoragePersister from '@pollyjs/persister-local-storage'; // Register the local-storage persister so its accessible by all future polly instances Polly.register(LocalStoragePersister); polly.configure({ persister: 'local-storage' }); polly.configure({ persister: RESTPersister }); ``` ## persisterOptions _Type_: `Object` _Default_: `{}` Options to be passed into the persister keyed by the persister name. ?> **NOTE:** Check out the appropriate documentation pages for each persister for more details. **Example** ```js polly.configure({ persisterOptions: { rest: { apiNamespace: '/polly' } } }); ``` ### keepUnusedRequests _Type_: `Boolean` _Default_: `false` When disabled, requests that have not been captured by the running Polly instance will be removed from any previous recording. This ensures that a recording will only contain the requests that were made during the lifespan of the Polly instance. When enabled, new requests will be appended to the recording file. **Example** ```js polly.configure({ persisterOptions: { keepUnusedRequests: true } }); ``` ### disableSortingHarEntries _Type_: `Boolean` _Default_: `false` When disabled, entries in the the final HAR will be sorted by the request's timestamp. This is done by default to satisfy the HAR 1.2 spec but can be enabled to improve diff readability when committing recordings to git. **Example** ```js polly.configure({ persisterOptions: { disableSortingHarEntries: true } }); ``` ## timing _Type_: `Function` _Default_: `Timing.fixed(0)` The timeout delay strategy used when replaying requests. **Example** ```js import { Timing } from '@pollyjs/core'; polly.configure({ // Replay requests at 300% the original speed to simulate a 3g connection timing: Timing.relative(3.0) }); polly.configure({ // Replay requests with a 200ms delay timing: Timing.fixed(200) }); ``` ## matchRequestsBy _Type_: `Object` _Default_: ```js matchRequestsBy: { method: true, headers: true, body: true, order: true, url: { protocol: true, username: true, password: true, hostname: true, port: true, pathname: true, query: true, hash: false } } ``` Request matching configuration. Each of these options are used to generate a GUID for the request. - ### method _Type_: `Boolean | Function` _Default_: `true` The request method (e.g. `GET`, `POST`) **Example** ```js polly.configure({ matchRequestsBy: { method: false } }); polly.configure({ matchRequestsBy: { method(method, req) { return method.toLowerCase(); } } }); ``` - ### headers _Type_: `Boolean | Function | Object` _Default_: `true` The request headers. **Example** ```js polly.configure({ matchRequestsBy: { headers: false } }); polly.configure({ matchRequestsBy: { headers(headers, req) { delete headers['X-AUTH-TOKEN']; return headers; } } }); ``` Specific headers can also be excluded with the following: **Example** ```js polly.configure({ matchRequestsBy: { headers: { exclude: ['X-AUTH-TOKEN'] } } }); ``` - ### body _Type_: `Boolean | Function` _Default_: `true` The request body. !> Please make sure you do not modify the passed in body. If you need to make changes, create a copy of it first. The body function receives the actual request body — any modifications to it will result with it being sent out with the request. **Example** ```js polly.configure({ matchRequestsBy: { body: false } }); polly.configure({ matchRequestsBy: { body(body, req) { const json = JSON.parse(body); delete json.email; return JSON.stringify(json); } } }); ``` - ### order _Type_: `Boolean` _Default_: `true` The order the request came in. Take the following scenario: ```js // Retrieve our model let model = await fetch('/models/1').then((res) => res.json()); // Modify the model model.foo = 'bar'; // Save the model with our new change await fetch('/models/1', { method: 'POST', body: JSON.stringify(model) }); // Get our updated model model = await fetch('/models/1').then((res) => res.json()); // Assert that our change persisted expect(model.foo).to.equal('bar'); ``` The order of the requests matter since the payload for the first and last fetch are different. **Example** ```js polly.configure({ matchRequestsBy: { order: false } }); ``` - ### url _Type_: `Boolean | Function | Object` _Default_: `{ protocol: true, username: true, ... }` The request url. **Example** ```js polly.configure({ matchRequestsBy: { url: false } }); polly.configure({ matchRequestsBy: { url(url, req) { return url.replace('test', ''); } } }); polly.configure({ matchRequestsBy: { url: { protocol(protocol) { return 'https:'; }, query: false } } }); ``` - ### url.protocol _Type_: `Boolean | Function` _Default_: `true` The request url protocol (e.g. `http:`). **Example** ```js polly.configure({ matchRequestsBy: { url: { protocol: false } } }); polly.configure({ matchRequestsBy: { url: { protocol(protocol, req) { return 'https:'; } } } }); ``` - ### url.username _Type_: `Boolean | Function` _Default_: `true` Username of basic authentication. **Example** ```js polly.configure({ matchRequestsBy: { url: { username: false } } }); polly.configure({ matchRequestsBy: { url: { username(username, req) { return 'username'; } } } }); ``` - ### url.password _Type_: `Boolean | Function` _Default_: `true` Password of basic authentication. **Example** ```js polly.configure({ matchRequestsBy: { url: { password: false } } matchRequestsBy: { url: { password(password, req) { return 'password'; } } } }); ``` - ### url.hostname _Type_: `Boolean | Function` _Default_: `true` Host name without port number. **Example** ```js polly.configure({ matchRequestsBy: { url: { hostname: false } } }); polly.configure({ matchRequestsBy: { url: { hostname(hostname, req) { return hostname.replace('.com', '.net'); } } } }); ``` - ### url.port _Type_: `Boolean | Function` _Default_: `true` Port number. **Example** ```js polly.configure({ matchRequestsBy: { url: { port: false } } }); polly.configure({ matchRequestsBy: { url: { port(port, req) { return 3000; } } } }); ``` - ### url.pathname _Type_: `Boolean | Function` _Default_: `true` URL path. **Example** ```js polly.configure({ matchRequestsBy: { url: { pathname(pathname, req) { return pathname.replace('/api/v1', '/api'); } } } }); ``` - ### url.query _Type_: `Boolean | Function` _Default_: `true` Sorted query string. **Example** ```js polly.configure({ matchRequestsBy: { url: { query: false } } }); polly.configure({ matchRequestsBy: { url: { query(query, req) { return { ...query, token: '' }; } } } }); ``` - ### url.hash _Type_: `Boolean | Function` _Default_: `false` The "fragment" portion of the URL including the pound-sign (`#`). **Example** ```js polly.configure({ matchRequestsBy: { url: { hash: true } } }); polly.configure({ matchRequestsBy: { url: { hash(hash, req) { return hash.replace(/token=[0-9]+/, ''); } } } }); ``` ================================================ FILE: docs/examples.md ================================================ # Examples ## Client Server **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/client-server)** [intercept.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/client-server/tests/intercept.test.js ':include :type=code') [events.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/client-server/tests/events.test.js ':include :type=code') ## REST Persister **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/rest-persister)** [package.json](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/rest-persister/package.json ':include :type=code') [rest-persister.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/rest-persister/tests/rest-persister.test.js ':include :type=code') ## Node Fetch **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/node-fetch)** [node-fetch.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/node-fetch/tests/node-fetch.test.js ':include :type=code') ## Puppeteer **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/puppeteer)** [index.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/puppeteer/index.js ':include :type=code') ## Jest + Node Fetch **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/jest-node-fetch)** [index.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/jest-node-fetch/__tests__/index.test.js ':include :type=code') ## TypeScript + Jest + Node Fetch **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/typescript-jest-node-fetch)** [auto-setup-polly.ts](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/typescript-jest-node-fetch/src/utils/auto-setup-polly.ts ':include :type=code') [github-api.test.ts](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/typescript-jest-node-fetch/src/github-api.test.ts ':include :type=code') ## Jest + Puppeteer **[Full Source](https://github.com/Netflix/pollyjs/tree/master/examples/jest-puppeteer)** [dummy-app.test.js](https://raw.githubusercontent.com/Netflix/pollyjs/master/examples/jest-puppeteer/__tests__/dummy-app.test.js ':include :type=code') ================================================ FILE: docs/frameworks/ember-cli.md ================================================ # Ember CLI Installing the `@pollyjs/ember` addon will import and vendor the necessary Polly.JS packages as well as register the [Express API](node-server/express-integrations) required by the [REST Persister](persisters/rest). ?> **NOTE:** By default, this addon installs and registers the [XHR](adapters/xhr) & [Fetch](adapters/fetch) adapters as well as the [REST](persisters/rest) & [Local Storage](persisters/local-storage) persisters. ## Installation ```bash ember install @pollyjs/ember ``` ## Configuration Addon and [server API configuration](node-server/overview#api-configuration) can be be specified in `/config/polly.js`. The default configuration options are shown below. ```js module.exports = function(env) { return { // Addon Configuration Options enabled: env !== 'production', // Server Configuration Options server: { apiNamespace: '/polly', recordingsDir: 'recordings' } }; }; ``` ## Usage Once installed and configured, you can import and use Polly as documented. Check out the [Quick Start](quick-start#usage) documentation to get started. ?> For an even better testing experience, check out the provided [QUnit Test Helper](test-frameworks/qunit)! ================================================ FILE: docs/index.html ================================================ Polly.JS
================================================ FILE: docs/node-server/express-integrations.md ================================================ # Express Integrations The `@pollyjs/node-server` package exports a `registerExpressAPI` method which takes in an [Express](http://expressjs.com/) app and a config to register the necessary routes to be used with the REST Persister. ```js const { registerExpressAPI } = require('@pollyjs/node-server'); registerExpressAPI(app, config); ``` ## Webpack DevServer ```js const path = require('path'); const { registerExpressAPI } = require('@pollyjs/node-server'); const config = { devServer: { before(app) { registerExpressAPI(app, config); } } }; module.exports = config; ``` ## Ember CLI See the [Ember CLI Addon](frameworks/ember-cli) documentation for more details. ================================================ FILE: docs/node-server/overview.md ================================================ # Overview The `@pollyjs/node-server` package provides a standalone node server as well as an express integration to be able to support the [REST Persister](persisters/rest) so recordings can be saved to and read from disk. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/node-server -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/node-server -D ``` ## Server This packages includes a fully working standalone node server that is pre-configured with the necessary APIs and middleware to support the [REST Persister](persisters/rest). The Server constructor accepts a configuration object that can be a combination of the below listed Server & API options. Once instantiated, you will have full access to the Express app via the `app` property. ```js const { Server } = require('@pollyjs/node-server'); const server = new Server({ quiet: true, port: 4000, apiNamespace: '/polly' }); // Add custom business logic to the express server server.app.get('/custom', () => { /* Add custom express logic */ }); // Start listening and attach extra logic to the http server server.listen().on('error', () => { /* Add http server error logic */ }); ``` ## Server Configuration ### port _Type_: `Number` _Default_: `3000` ```js new Server({ port: 4000 }); ``` ### host _Type_: `String` _Default_: `undefined` ```js new Server({ host: 'test.localhost' }); ``` ### quiet _Type_: `Boolean` _Default_: `false` Enable/Disable the logging middleware ([morgan](https://github.com/expressjs/morgan)). ```js new Server({ quiet: true }); ``` ### corsOptions _Type_: `Object | Function` _Default_: `undefined` Options passed to the ([CORS](https://github.com/expressjs/cors)) middleware. ```js new Server({ corsOptions: { origin: 'http://localhost:4000', credentials: true } }); ``` ## API Configuration ### recordingsDir _Type_: `String` _Default_: `'recordings'` The root directory to store all recordings. ```js new Server({ recordingsDir: '__recordings__' }); registerExpressAPI(app, { recordingsDir: '__recordings__' }); ``` ### apiNamespace _Type_: `String` _Default_: `'polly'` The namespace to mount the polly API on. This should really only be changed if there is a conflict with the default apiNamespace. !> If modified, you must provide the new `apiNamespace` to the client side Polly instance via the [Persister Options](persisters/rest#apinamespace) ```js new Server({ apiNamespace: '/polly' }); registerExpressAPI(app, { apiNamespace: '/polly' }); ``` ### recordingSizeLimit _Type_: `String` _Default_: `'50mb'` A recording size can not exceed 50mb by default. If your application exceeds this limit, bump this value to a reasonable limit. ```js new Server({ recordingSizeLimit: '50mb' }); registerExpressAPI(app, { recordingSizeLimit: '50mb' }); ``` ================================================ FILE: docs/persisters/custom.md ================================================ # Custom Persister If you need to create your own persister or modify an pre-existing one, you've come to the right page! ## Creating a Custom Persister The `@pollyjs/persister` package provides an extendable base persister class that contains core logic dependent on by the [REST](persisters/rest) & [Local Storage](persisters/local-storage) persisters. ### Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/persister -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/persister -D ``` ### Usage ```js import Persister from '@pollyjs/persister'; class CustomPersister extends Persister { static get id() { return 'custom'; } onFindRecording() {} onSaveRecording() {} onDeleteRecording() {} } ``` For better usage examples, please refer to the source code for the [REST](https://github.com/Netflix/pollyjs/blob/master/packages/%40pollyjs/core/src/persisters/rest/index.js) & [Local Storage](https://github.com/Netflix/pollyjs/blob/master/packages/%40pollyjs/core/src/persisters/local-storage/index.js) persisters. ## Extending from an Existing Persister The `@pollyjs/core` package exports the `RESTPersister` and `LocalStoragePersister` classes, allowing you to modify them as needed. ```js import RESTPersister from '@pollyjs/persister-rest'; import LocalStoragePersister from '@pollyjs/persister-local-storage'; class CustomRESTPersister extends RESTPersister {} class CustomLocalStoragePersister extends LocalStoragePersister {} ``` ## Registering & Connecting to a Custom Persister You can register and connect to a custom persister by passing an array to the `persister` config where the first element is the name of your persister and the second is the persister class. ```js // Register and connect to a custom persister: new Polly('Custom Persister', { persister: MyCustomPersisterClass }); // Register and connect to a custom persister via .configure(): const polly = new Polly('Custom Persister'); polly.configure({ persister: MyCustomPersisterClass }); ``` ================================================ FILE: docs/persisters/fs.md ================================================ # File System Persister Read and write recordings to and from the file system. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/persister-fs -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/persister-fs -D ``` ## Usage ```js import { Polly } from '@pollyjs/core'; import FSPersister from '@pollyjs/persister-fs'; // Register the fs persister so its accessible by all future polly instances Polly.register(FSPersister); new Polly('', { persister: 'fs' }); ``` ## Options ### recordingsDir _Type_: `String` _Default_: `'recordings'` The root directory to store all recordings. Supports both relative and absolute paths. **Example** ```js polly.configure({ persisterOptions: { fs: { recordingsDir: '__recordings__' } } }); ``` ================================================ FILE: docs/persisters/local-storage.md ================================================ # Local Storage Persister Read and write recordings to and from the browser's Local Storage. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/persister-local-storage -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/persister-local-storage -D ``` ## Usage ```js import { Polly } from '@pollyjs/core'; import LocalStoragePersister from '@pollyjs/persister-local-storage'; // Register the local-storage persister so its accessible by all future polly instances Polly.register(LocalStoragePersister); new Polly('', { persister: 'local-storage' }); ``` ## Options ### context _Type_: `Object` _Default_: `global|self|window` The context object which contains the localStorage API. Typically this is `window` or `self` in the browser and `global` in node. **Example** ```js polly.configure({ persisterOptions: { 'local-storage': { context: window } } }); ``` ### key _Type_: `String` _Default_: `'pollyjs'` The localStorage key to store the recordings data under. **Example** ```js polly.configure({ persisterOptions: { 'local-storage': { key: '__pollyjs__' } } }); ``` ================================================ FILE: docs/persisters/rest.md ================================================ # REST Persister Read and write recordings to and from the file system via a CRUD API hosted on a server. ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/persister-rest -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/persister-rest -D ``` ## Setup This library provides a fully functional [node server](node-server/overview) as well as a [CLI](cli/overview) to get you up and running. ## Usage ```js import { Polly } from '@pollyjs/core'; import RESTPersister from '@pollyjs/persister-rest'; // Register the rest persister so its accessible by all future polly instances Polly.register(RESTPersister); new Polly('', { persister: 'rest' }); ``` ## Options ### host _Type_: `String` _Default_: `'http://localhost:3000'` The host that the API exists on. **Example** ```js polly.configure({ persisterOptions: { rest: { host: 'http://localhost.com:4000' } } }); ``` ### apiNamespace _Type_: `String` _Default_: `'/polly'` The API namespace. The namespace the Polly API is mounted on. This should really only be changed if there is a conflict with the default apiNamespace. !> If modified, you must provide the new `apiNamespace` to the node server via the [Node Server apiNamespace](node-server/overview#apinamespace) configuration option. **Example** ```js polly.configure({ persisterOptions: { rest: { apiNamespace: '/polly' } } }); ``` ================================================ FILE: docs/quick-start.md ================================================ # Quick Start ## Installation _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/core -D ``` If you want to install it with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/core -D ``` ## How it Works Once instantiated, Polly will hook into native implementations (such as fetch & XHR) via adapters to intercept any outgoing requests. Depending on its current [mode](configuration#mode) as well as rules defined via the [client-side server](server/overview), the request will either be replayed, recorded, passed-through, or intercepted. ## Adapters & Persisters Before you start using Polly, you'll need to install the necessary adapters and persisters depending on your application/environment. Adapters provide functionality that allows Polly to intercept requests via different sources (e.g. XHR, fetch, Puppeteer) while Persisters provide the functionality to read & write recorded data (e.g. fs, local-storage). Check out the appropriate documentation pages for each adapter and persister for more details such as installation, usage, and available options. _Note that you must have node (and npm) installed._ ```bash npm install @pollyjs/adapter-{name} -D npm install @pollyjs/persister-{name} -D ``` If you want to install them with [yarn](https://yarnpkg.com): ```bash yarn add @pollyjs/adapter-{name} -D yarn add @pollyjs/persister-{name} -D ``` Once installed, you can register the adapters and persisters with Polly so they can easily be referenced by name later. ```js import { Polly } from '@pollyjs/core'; import FetchAdapter from '@pollyjs/adapter-fetch'; import XHRAdapter from '@pollyjs/adapter-xhr'; import LocalStoragePersister from '@pollyjs/persister-local-storage'; Polly.register(FetchAdapter); Polly.register(XHRAdapter); Polly.register(LocalStoragePersister); new Polly('', { adapters: ['fetch', 'xhr'], persister: 'local-storage' }); ``` ## Using Polly in the Browser? Polly fully supports native in-browser usage, but because browsers can't write to disk in the same way as conventional applications considerations need to be made for persisting recordings. If permanent, long-term persistence is not required then you can simply use the [Local Storage Persister](persisters/local-storage), which writes to `window.localStorage`. For conventional file system storage you will need to use the [REST Persister](persisters/rest) which runs as a separate process listening for PollyJS activity. The server can be run in 2 ways. Firstly via the provided [CLI](cli/overview)'s [listen](cli/commands#listen) command: ```bash npm install @pollyjs/cli -g polly listen ``` However, secondly there is also a convenient [Express Integration](node-server/express-integrations) that appends the REST server's endpoints to an existing server such as [Webpack's Dev Server](https://webpack.js.org/configuration/dev-server/). ## Usage Now that you've installed and setup Polly, you're ready to fly. Lets take a look at what a simple example test case would look like using Polly. ```js import { Polly } from '@pollyjs/core'; import FetchAdapter from '@pollyjs/adapter-fetch'; import LocalStoragePersister from '@pollyjs/persister-local-storage'; /* Register the adapters and persisters we want to use. This way all future polly instances can access them by name. */ Polly.register(FetchAdapter); Polly.register(LocalStoragePersister); describe('Simple Example', function () { it('fetches a post', async function () { /* Create a new polly instance. Connect Polly to fetch. By default, it will record any requests that it hasn't yet seen while replaying ones it has already recorded. */ const polly = new Polly('Simple Example', { adapters: ['fetch'], // Hook into `fetch` persister: 'local-storage', // Read/write to/from local-storage logLevel: 'info' // Log requests to console }); const response = await fetch( 'https://jsonplaceholder.typicode.com/posts/1' ); const post = await response.json(); expect(response.status).to.equal(200); expect(post.id).to.equal(1); /* Calling `stop` will persist requests as well as disconnect from any connected adapters. */ await polly.stop(); }); }); ```

See the Pen Polly.JS Simple Example on CodePen.

The first time the test runs, Polly will record the response for the `fetch('https://jsonplaceholder.typicode.com/posts/1')` request that was made. You will see the following in the console: ```text Recorded ➞ GET https://jsonplaceholder.typicode.com/posts/1 200 • 48ms ``` Once the Polly instance is [stopped](api#stop-1), the persister will generate the following [HAR](http://www.softwareishard.com/blog/har-12-spec/) file which will be used to replay the response to that request when the test is rerun: ```json { "Simple-Example_823972681": { "log": { "_recordingName": "Simple Example", "browser": { "name": "Chrome", "version": "70.0" }, "creator": { "comment": "persister:local-storage", "name": "Polly.JS", "version": "1.2.0" }, "entries": [ { "_id": "ffbc4836d419fc265c3b85cbe1b7f22e", "_order": 0, "cache": {}, "request": { "bodySize": 0, "cookies": [], "headers": [], "headersSize": 63, "httpVersion": "HTTP/1.1", "method": "GET", "queryString": [], "url": "https://jsonplaceholder.typicode.com/posts/1" }, "response": { "bodySize": 292, "content": { "mimeType": "application/json; charset=utf-8", "size": 292, "text": "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"sunt aut facere repellat provident occaecati excepturi optio reprehenderit\",\n \"body\": \"quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto\"\n}" }, "cookies": [], "headers": [ { "name": "cache-control", "value": "public, max-age=14400" }, { "name": "content-type", "value": "application/json; charset=utf-8" }, { "name": "expires", "value": "Tue, 30 Oct 2018 22:52:42 GMT" }, { "name": "pragma", "value": "no-cache" } ], "headersSize": 145, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, "startedDateTime": "2018-10-30T18:52:42.566Z", "time": 18, "timings": { "blocked": -1, "connect": -1, "dns": -1, "receive": 0, "send": 0, "ssl": -1, "wait": 18 } } ], "pages": [], "version": "1.2" } } } ``` The next time the test is run, Polly will use the recorded response instead of going out to the server to get a new one. You will see the following in the console: ```text Replayed ➞ GET https://jsonplaceholder.typicode.com/posts/1 200 • 1ms ``` ## Client-Side Server Every Polly instance has a reference to a [client-side server](server/overview) which you can leverage to gain full control of all HTTP interactions as well as dictate how the Polly instance should handle them. Lets take a look at how we can modify our previous test case to test against a post that does not exist. ```js describe('Simple Client-Side Server Example', function () { it('fetches an unknown post', async function () { /* Create a new polly instance. Connect Polly to fetch. By default, it will record any requests that it hasn't yet seen while replaying ones it has already recorded. */ const polly = new Polly('Simple Client-Side Server Example', { adapters: ['fetch'], // Hook into `fetch` persister: 'local-storage', // Read/write to/from local-storage logLevel: 'info' // Log requests to console }); const { server } = polly; /* Add a rule via the client-side server to intercept the `https://jsonplaceholder.typicode.com/posts/404` request and return an error. */ server .get('https://jsonplaceholder.typicode.com/posts/404') .intercept((req, res) => { res.status(404).json({ error: 'Post not found.' }); }); const response = await fetch( 'https://jsonplaceholder.typicode.com/posts/404' ); const post = await response.json(); expect(response.status).to.equal(404); expect(post.error).to.equal('Post not found.'); /* Calling `stop` will persist requests as well as disconnect from any connected adapters. */ await polly.stop(); }); }); ```

See the Pen Polly.JS Simple Client-Side Server Example on CodePen.

When the test executes, Polly will detect that we've set a custom intercept rule for `https://jsonplaceholder.typicode.com/posts/404` and will deffer to the intercept handler to handle the response for that request. You will see the following in the console: ```text Intercepted ➞ GET https://jsonplaceholder.typicode.com/posts/404 404 • 1ms ``` ## Test Helpers Using Mocha or QUnit? We've got you covered! Checkout the [Mocha](test-frameworks/mocha) or [QUnit](test-frameworks/qunit) documentation pages for detailed instructions on how to use the provided test helpers. ================================================ FILE: docs/server/api.md ================================================ # API ## HTTP Methods The `GET`, `PUT`, `POST`, `PATCH`, `DELETE`, `MERGE`, `HEAD`, and `OPTIONS` HTTP methods have a corresponding method on the server instance. ```js server.get('/ping'); server.put('/ping'); server.post('/ping'); server.patch('/ping'); server.delete('/ping'); server.merge('/ping'); server.head('/ping'); server.options('/ping'); ``` Each of these methods returns a [Route Handler](server/route-handler.md) which you can use to pass-through, intercept, and attach events to. ```js server.get('/ping').passthrough(); server.put('/ping').intercept((req, res) => res.sendStatus(200)); server.post('/ping').on('request', req => { /* Do Something */ }); server.patch('/ping').off('request'); ``` ## any Declare [Events & Middleware](server/events-and-middleware#middleware) globally or for a specific route. **Example** ```js server.any('/session/:id').on('request', (req, res) => { req.query.email = 'test@netflix.com'; }); ``` ## host Define a block where all methods will inherit the provided host. **Example** ```js server.host('http://netflix.com', () => { // Middleware will be attached to the host server.any().on('request', req => {}); server.get('/session').intercept(() => {}); // → http://netflix.com/session }); ``` ## namespace Define a block where all methods will inherit the provided namespace. **Example** ```js server.namespace('/api', () => { // Middleware will be attached to the namespace server.any().on('request', req => {}); server.get('/session').intercept(() => {}); // → /api/session server.namespace('/v2', () => { server.get('/session').intercept(() => {}); // → /api/v2/session }); }); ``` ## timeout Returns a promise that will resolve after the given number of milliseconds. **Example** ```js server.get('/ping').intercept(async (req, res) => { await server.timeout(500); res.sendStatus(200); }); ``` ================================================ FILE: docs/server/event.md ================================================ # Event ## Properties ### type _Type_: `String` The event type. (e.g. `request`, `response`, `beforePersist`) ## Methods ### stopPropagation 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. **Example** ```js server.get('/session/:id').on('beforeResponse', (req, res, event) => { event.stopPropagation(); res.setHeader('X-SESSION-ID', 'ABC123'); }); server.get('/session/:id').on('beforeResponse', (req, res, event) => { // This will never be reached res.setHeader('X-SESSION-ID', 'XYZ456'); }); ``` ================================================ FILE: docs/server/events-and-middleware.md ================================================ # Events & Middleware ## Events Events can be attached to a server route using `.on()` and detached via the `.off()` methods. ?> **NOTE:** Event handlers can be asynchronous. An `async` function can be used or a `Promise` can be returned. ```js // Events server .get('/') .on('request', req => {}) .off('request'); // Passthrough w/ Events server .get('/') .passthrough() .on('beforeResponse', (req, res) => {}) .off('beforeResponse'); // Intercept w/ Events server .get('/', (req, res) => {}) .on('request', req => {}) .on('beforeResponse', (req, res) => {}); // Middleware w/ Events server .any('/') .on('request', req => {}) .on('beforeResponse', (req, res) => {}); ``` ### request Fires right before the request goes out. | Param | Type | Description | | ----- | ------------------------- | -------------------- | | req | [Request](server/request) | The request instance | | event | [Event](server/event) | The event instance | **Example** ```js server.get('/session').on('request', req => { req.headers['X-AUTH'] = ''; req.query.email = 'test@app.com'; }); ``` ### beforeResponse Fires right before the response materializes and the promise resolves. | Param | Type | Description | | ----- | --------------------------- | --------------------- | | req | [Request](server/request) | The request instance | | res | [Response](server/response) | The response instance | | event | [Event](server/event) | The event instance | **Example** ```js server.get('/session').on('beforeResponse', (req, res) => { res.setHeader('X-AUTH', ''); }); ``` ### response Fires right after the response has been finalized for the request but before the response materializes and the promise resolves. | Param | Type | Description | | ----- | --------------------------- | --------------------- | | req | [Request](server/request) | The request instance | | res | [Response](server/response) | The response instance | | event | [Event](server/event) | The event instance | **Example** ```js server.get('/session').on('response', (req, res) => { console.log( `${req.url} took ${req.responseTime}ms with a status of ${res.statusCode}.` ); }); ``` ### beforePersist Fires before the request/response gets persisted. | Param | Type | Description | | --------- | ------------------------- | ------------------------------------ | | req | [Request](server/request) | The request instance | | recording | `Object` | The recording that will be persisted | | event | [Event](server/event) | The event instance | **Example** ```js server.any().on('beforePersist', (req, recording) => { recording.request = encrypt(recording.request); recording.response = encrypt(recording.response); }); ``` ### beforeReplay Fires after retrieving the recorded request/response from the persister and before the recording materializes into a response. | Param | Type | Description | | --------- | ------------------------- | ----------------------- | | req | [Request](server/request) | The request instance | | recording | `Object` | The retrieved recording | | event | [Event](server/event) | The event instance | **Example** ```js server.any().on('beforeReplay', (req, recording) => { recording.request = decrypt(recording.request); recording.response = decrypt(recording.response); }); ``` ### error Fires when any error gets emitted during the request life-cycle. | Param | Type | Description | | ----- | ------------------------- | -------------------- | | req | [Request](server/request) | The request instance | | error | Error | The error | | event | [Event](server/event) | The event instance | **Example** ```js server.any().on('error', (req, error) => { console.error(error); process.exit(1); }); ``` ### abort Fires when a request is aborted. | Param | Type | Description | | ----- | ------------------------- | -------------------- | | req | [Request](server/request) | The request instance | | event | [Event](server/event) | The event instance | **Example** ```js server.any().on('abort', req => { console.error('Request aborted.'); process.exit(1); }); ``` ## Middleware Middleware can be added via the `.any()` method. ?> **NOTE:** Middleware events will be executed by the order in which they were declared. ### Global Middleware The following is an example of a global middleware that will be attached to all routes. This middleware in specific overrides the `X-Auth-Token` with a test token. ```js server.any().on('request', (req, res) => { req.headers['X-Auth-Token'] = 'abc123'; }); ``` ### Route Level Middleware The following is an example of a route level middleware that will be attached to any route that matches `/session/:id`. This middleware in specific overrides the email query param with that of a test email. ```js server.any('/session/:id').on('request', (req, res) => { req.query.email = 'test@netflix.com'; }); ``` ================================================ FILE: docs/server/overview.md ================================================ # Overview Every polly instance has a reference to a client-side server which you can leverage to gain full control of all HTTP interactions as well as dictate how the Polly instance should handle them. ## Usage ```js const polly = new Polly(''); const { server } = polly; // Events & Middleware server.any().on('request', (req, res) => { req.headers['X-Auth-Token'] = 'abc123'; }); // Intercept requests server.get('/session').intercept((req, res) => { res.status(200).json({ user: { email: 'test@netflix.com' } }); }); // Passthrough requests server.get('/coverage').passthrough(); ``` ## Defining Routes The server uses [Route Recognizer](https://github.com/tildeio/route-recognizer) under the hood. This allows you to define static routes, as well as dynamic, and starred segments. **Example** ```js // Static Routes server.get('/api/v2/users').intercept((req, res) => { res.sendStatus(200); }); // Dynamic Segments server.get('http://netflix.com/movies/:id').intercept((req, res) => { console.log(req.params.id); // http://netflix.com/movies/1 → '1' res.sendStatus(200); }); // Starred Segments server.get('/secrets/*path').intercept((req, res) => { console.log(req.params.path); // /secrets/foo/bar → 'foo/bar' res.status(401).send('Shhh!'); }); ``` ### Multi Route Handlers HTTPS methods as well as `.any()` accept a single string as well as an array of strings. **Example** ```js // Match against '/api/v2/users' as well as any child route server.get(['/api/v2/users', '/api/v2/users/*path']).passthrough(); // Register the same event handler on both '/session' and '/users/session' server.any(['/session', '/users/session']).on('request', () => {}); ``` ================================================ FILE: docs/server/request.md ================================================ # Request ## Properties ### method _Type_: `String` The request method. (e.g. `GET`, `POST`, `DELETE`) ### url _Type_: `String` The request URL. ### protocol _Type_: `String` The request url protocol. (e.g. `http://`, `https:`) ### hostname _Type_: `String` The request url host name. (e.g. `localhost`, `netflix.com`) ### port _Type_: `String` The request url port. (e.g. `3000`) ### pathname _Type_: `String` The request url path name. (e.g. `/session`, `/movies/1`) ### hash _Type_: `String` The request url hash. ### headers _Type_: `Object` _Default_: `{}` The request headers. ### body _Type_: `any` The request body. ### query _Type_: `Object` _Default_: `{}` The request url query parameters. ### params _Type_: `Object` _Default_: `{}` The matching route's path params. **Example** ```js server.get('/movies/:id').intercept((req, res) => { console.log(req.params.id); }); ``` ### recordingName _Type_: `String` The recording the request should be recorded under. ## Methods ### getHeader Get a header with a given name. | Param | Type | Description | | ----------- | ------------------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | `String` \| `Array` | The header value | **Example** ```js req.getHeader('Content-Type'); // → application/json ``` ### setHeader Set a header with a given name. If the value is `null` or `undefined`, the header will be removed. | Param | Type | Description | | ----------- | ------------------------- | ------------------------ | | name | `String` | The name of the header | | value | `String` \| `Array` | The value for the header | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.setHeader('Content-Length', 42); ``` ### setHeaders Add multiple headers at once. If a value is `null` or `undefined`, the header will be removed. | Param | Type | Description | | ----------- | ------------------------- | --------------------------------- | | headers | `Object` | The headers to add to the request | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.setHeaders({ Accept: ['text/html', 'image/*'], 'Content-Type': 'application/json', 'Content-Length': 42 }); ``` ### removeHeader Remove a header with the given name. | Param | Type | Description | | ----------- | ------------------------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.removeHeader('Content-Length'); ``` ### removeHeaders Remove multiple headers at once. | Param | Type | Description | | ----------- | ------------------------- | -------------------------------------- | | headers | `Array` | The headers to remove from the request | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.removeHeaders(['Content-Type' 'Content-Length']); ``` ### hasHeader Returns 'true' or 'false' depending on if the request has the given header. | Param | Type | Description | | ----------- | --------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | `Boolean` |   | **Example** ```js req.hasHeader('X-AUTH'); // → false ``` ### type Sets the request's Content Type. | Param | Type | Description | | ----------- | ------------------------- | ------------------- | | value | `String` |   | | **Returns** | [Request](server/request) | The current request | ### send Sets the request's body. - If the body is a `String`, it defaults the content type to `text/html` if does not exist. - If the body is a `String` and no charset is found, a `utf-8` charset is appended to the content type. - Body that is a `Boolean`, `Number`, or `Object` gets passed to the [json](#json) method. | Param | Type | Description | | ----------- | ------------------------- | ------------------- | | body | `any` |   | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.send('Hello World'); req.send(200); req.send(true); req.send(); ``` ### json A shortcut method to set the content type to `application/json` if it hasn't been set already, and call [send](#send) with the stringified object. | Param | Type | Description | | ----------- | ------------------------- | ------------------- | | obj | `Object` | Object to send | | **Returns** | [Request](server/request) | The current request | **Example** ```js req.json({ Hello: 'World' }); ``` ### jsonBody A shortcut method that calls JSON.parse on the request's body. !> This method will throw if the body is an invalid JSON string. | Param | Type | Description | | ----------- | -------- | -------------------- | | **Returns** | `Object` | The JSON parsed body | **Example** ```js req.jsonBody(); ``` ### overrideRecordingName Override the recording name for the request. | Param | Type | Description | | ------------- | -------- | ---------------------- | | recordingName | `String` | The new recording name | **Example** ```js req.overrideRecordingName(req.hostname); ``` ### configure Override configuration options for the request. | Param | Type | Description | | ------ | -------- | ------------------------------------- | | config | `Object` | [Configuration](configuration) object | **Example** ```js req.configure({ recordFailedRequests: true }); req.configure({ timing: Timing.relative(3.0) }); req.configure({ logLevel: 'info' }); ``` ================================================ FILE: docs/server/response.md ================================================ # Response ## Properties ### statusCode _Type_: `Number` _Default_: `undefined` The response's status code. ### headers _Type_: `Object` _Default_: `{}` The response's headers. ### body _Type_: `String` _Default_: `undefined` The response's body. ## Methods ### status Set the response's status code. | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | status | `Number` | Status code | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.status(200); ``` ### getHeader Get a header with a given name. | Param | Type | Description | | ----------- | ------------------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | `String` \| `Array` | The header value | **Example** ```js res.getHeader('Content-Type'); // → application/json ``` ### setHeader Set a header with a given name. If the value is `null` or `undefined`, the header will be removed. | Param | Type | Description | | ----------- | --------------------------- | ------------------------ | | name | `String` | The name of the header | | value | `String` \| `Array` | The value for the header | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.setHeader('Content-Length', 42); ``` ### setHeaders Add multiple headers at once. If a value is `null` or `undefined`, the header will be removed. | Param | Type | Description | | ----------- | --------------------------- | ---------------------------------- | | headers | `Object` | The headers to add to the response | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.setHeaders({ Accept: ['text/html', 'image/*'], 'Content-Type': 'application/json', 'Content-Length': 42 }); ``` ### removeHeader Remove a header with the given name. | Param | Type | Description | | ----------- | --------------------------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.removeHeader('Content-Length'); ``` ### removeHeaders Remove multiple headers at once. | Param | Type | Description | | ----------- | --------------------------- | --------------------------------------- | | headers | `Array` | The headers to remove from the response | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.removeHeaders(['Content-Type' 'Content-Length']); ``` ### hasHeader Returns 'true' or 'false' depending on if the response has the given header. | Param | Type | Description | | ----------- | --------- | ---------------------- | | name | `String` | The name of the header | | **Returns** | `Boolean` |   | **Example** ```js res.hasHeader('X-AUTH'); // → false ``` ### type Sets the response's Content Type. | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | value | `String` |   | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.type('application/json'); ``` ### send Sets the response's body. - If the body is a `String`, it defaults the content type to `text/html` if does not exist. - If the body is a `String` and no charset is found, a `utf-8` charset is appended to the content type. - Body that is a `Boolean`, `Number`, or `Object` gets passed to the [json](#json) method. | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | body | `any` |   | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.send('Hello World'); res.send(200); res.send(true); res.send(); ``` ### sendStatus A shortcut method to set the status to the given status code, set the content type to `text/plain`, and call [send](#send). | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | status | `Number` | Status code | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.sendStatus(200); ``` ### json A shortcut method to set the content type to `application/json` if it hasn't been set already, and call [send](#send) with the stringified object. | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | obj | `Object` | Object to send | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.json({ Hello: 'World' }); ``` ### jsonBody A shortcut method that calls JSON.parse on the response's body. !> This method will throw if the body is an invalid JSON string. | Param | Type | Description | | ----------- | -------- | -------------------- | | **Returns** | `Object` | The JSON parsed body | **Example** ```js res.jsonBody(); ``` ### end Freeze the response and headers so they can no longer be modified. | Param | Type | Description | | ----------- | --------------------------- | -------------------- | | **Returns** | [Response](server/response) | The current response | **Example** ```js res.end(); ``` ================================================ FILE: docs/server/route-handler.md ================================================ # Route Handler An object that is returned when calling any of the server's HTTP methods as well as `server.any()`. ## Methods ?> **NOTE:** Event & Intercept handlers can be asynchronous. An `async` function can be used or a `Promise` can be returned. ### on Register an [event](server/events-and-middleware) handler. ?> **Tip:** You can attach multiple handlers to a single event. Handlers will be called in the order they were declared. | Param | Type | Description | | ------------- | ---------- | ---------------------------------------------------------------- | | eventName | `String` | The event name | | handler | `Function` | The event handler | | options | `Object` | The event handler options | | options.times | `number` | Remove listener after being called the specified amount of times | **Example** ```js server .get('/session') .on('request', (req) => { req.headers['X-AUTH'] = ''; req.query.email = 'test@app.com'; }) .on('request', () => { /* Do something else */ }) .on( 'request', () => { /* Do something else twice */ }, { times: 2 } ); ``` ### once Register a one-time [event](server/events-and-middleware) handler. ?> **Tip:** You can attach multiple handlers to a single event. Handlers will be called in the order they were declared. | Param | Type | Description | | --------- | ---------- | ----------------- | | eventName | `String` | The event name | | handler | `Function` | The event handler | **Example** ```js server .get('/session') .once('request', (req) => { req.headers['X-AUTH'] = ''; req.query.email = 'test@app.com'; }) .once('request', () => { /* Do something else */ }); ``` ### off Un-register an [event](server/events-and-middleware) handler. If no handler is specified, all event handlers are un-registered for the given event name. | Param | Type | Description | | --------- | ---------- | ----------------- | | eventName | `String` | The event name | | handler | `Function` | The event handler | **Example** ```js const handler = () => {}; server .get('/session') .on('request', , handler) .on('request', () => {}) .off('request', handler) /* Un-register the specified event/handler pair */ .off('request'); /* Un-register all handlers */ ``` ### intercept Register an intercept handler. Once set, the [request](server/request) will never go to server but instead defer to the provided handler to handle the [response](server/response). If multiple intercept handlers have been registered, each handler will be called in the order in which it was registered. | Param | Type | Description | | ------------- | ---------- | --------------------------------------------------------------- | | handler | `Function` | The intercept handler | | options | `Object` | The event handler options | | options.times | `number` | Remove handler after being called the specified amount of times | **Example** ```js server.any('/session').intercept((req, res) => res.sendStatus(200)); server.any('/twice').intercept((req, res) => res.sendStatus(200), { times: 2 }); server.get('/session/:id').intercept((req, res, interceptor) => { if (req.params.id === '1') { res.status(200).json({ token: 'ABC123XYZ' }); } else if (req.params.id === '2') { res.status(404).json({ error: 'Unknown Session' }); } else { interceptor.abort(); } }); ``` #### Interceptor _Extends [Event](server/event)_ The `intercept` handler receives a third `interceptor` argument that provides some utilities. ##### abort Calling the `abort` method on the interceptor tells the Polly instance to continue handling the request as if it hasn't been intercepted. This allows you to only intercept specific types of requests while opting out of others. **Example** ```js server.get('/session/:id').intercept((req, res, interceptor) => { if (req.params.id === '1') { res.status(200).json({ token: 'ABC123XYZ' }); } else { interceptor.abort(); } }); ``` ##### passthrough Calling the `passthrough` method on the interceptor tells the Polly instance to continue handling the request as if it has been declared as a passthrough. This allows you to only intercept specific types of requests while passing others through. **Example** ```js server.get('/session/:id').intercept((req, res, interceptor) => { if (req.params.id === '1') { res.status(200).json({ token: 'ABC123XYZ' }); } else { interceptor.passthrough(); } }); ``` ##### stopPropagation If several intercept handlers are attached to the same route, they are called in the order in which they were added. If `stopPropagation` is invoked during one such call, no remaining handlers will be called. **Example** ```js // First call should return the user and not enter the 2nd handler server .get('/session/:id') .times(1) // Remove this interceptor after it gets called once .intercept((req, res, interceptor) => { // Do not continue to the next intercept handler which handles the 404 case interceptor.stopPropagation(); res.sendStatus(200); }); server.delete('/session/:id').intercept((req, res) => res.sendStatus(204)); // Second call should 404 since the user no longer exists server.get('/session/:id').intercept((req, res) => res.sendStatus(404)); await fetch('/session/1'); // --> 200 await fetch('/session/1', { method: 'DELETE' }); // --> 204 await fetch('/session/1'); // --> 404 ``` ### passthrough Declare a route as a passthrough meaning any request that matches that route will directly use the native implementation. Passthrough requests will not be recorded. | Param | Type | Description | | ----------- | --------- | ----------------------------------------------------- | | passthrough | `boolean` | Enable or disable the passthrough. Defaults to `true` | **Example** ```js server.any('/session').passthrough(); server.get('/session/1').passthrough(false); ``` ### filter Filter requests matched by the route handler with a predicate callback function. This can be useful when trying to match a request by a part of the url, a header, and/or parts of the request body. The callback will receive the [Request](server/request) as an argument. Return `true` to match the request, `false` otherwise. ?> Multiple filters can be chained together. They must all return `true` for the route handler to match the given request. | Param | Type | Description | | -------- | ---------- | ----------------------------- | | callback | `Function` | The filter predicate function | **Example** ```js server .any() .filter(req => req.hasHeader('Authentication')); .on('request', req => { res.setHeader('Authentication', 'test123') }); server .get('/users/:id') .filter(req => req.params.id === '1'); .intercept((req, res) => { res.status(200).json({ email: 'user1@test.com' }); }); ``` ### times Proceeding intercept and event handlers defined will be removed after being called the specified amount of times. The number specified is used as a default value and can be overridden by passing a custom `times` option to the handler. | Param | Type | Description | | ----- | -------- | -------------------------------------------------------------------------------------------------- | | times | `number` | Default times value for proceeding handlers. If no value is provided, the default value is removed | **Example** ```js server .any() .times(2); .on('request', req => {}); .intercept((req, res) => {}); .times() .on('response', (req, res) => {}); // Is the same as: server .any() .on('request', req => {}, { times: 2 }); .intercept((req, res) => {}, { times: 2 }); .on('response', (req, res) => {}); ``` ### configure Override configuration options for the given route. All matching middleware and route level configs are merged together and the overrides are applied to the current Polly instance's config. !> The following options are not supported to be overridden via the server API: `mode`, `adapters`, `adapterOptions`, `persister`, `persisterOptions` | Param | Type | Description | | ------ | -------- | ------------------------------------- | | config | `Object` | [Configuration](configuration) object | **Example** ```js server.any('/session').configure({ recordFailedRequests: true }); server.get('/users/:id').configure({ timing: Timing.relative(3.0) }); server.get('/users/1').configure({ logLevel: 'info' }); ``` ### recordingName Override the recording name for the given route. This allows for grouping common requests to share a single recording which can drastically help de-clutter test recordings. For example, if your tests always make a `/users` or `/session` call, instead of having each of those requests be recorded for every single test, you can use this to create a common recording file for them. | Param | Type | Description | | ------------- | -------- | ------------------------------------------------------------------------- | | recordingName | `String` | Name of the [recording](api#recordingName) to store the recordings under. | **Example** ```js server.any('/session').recordingName('User Session'); server.get('/users/:id').recordingName('User Data'); server .get('/users/1') .recordingName(); /* Fallback to the polly instance's recording name */ ``` ================================================ FILE: docs/test-frameworks/jest-jasmine.md ================================================ # Jest & Jasmine Due to the nature of the Jest & Jasmine APIs and their restrictions on accessing the current running test and its parent modules, we've decided to keep this test helper as a 3rd party library provided by [@gribnoysup](https://github.com/gribnoysup). 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. The Polly instance's recording name is derived from the current test name as well as its parent module(s). [README.md](https://raw.githubusercontent.com/gribnoysup/setup-polly-jest/master/README.md ':include :type=markdown') ================================================ FILE: docs/test-frameworks/mocha.md ================================================ # Mocha The `@pollyjs/core` package provides a `setupMocha` utility which will setup a new polly instance for each test as well as stop it once the test has ended. The Polly instance's recording name is derived from the current test name as well as its parent module(s). | Param | Type | Description | | ------ | -------- | ------------------------------------- | | config | `Object` | [Configuration](configuration) object | ## Usage ### Simple Example {docsify-ignore} ```js import { setupMocha as setupPolly } from '@pollyjs/core'; describe('Netflix Homepage', function() { setupPolly({ /* default configuration options */ }); it('should be able to sign in', async function() { /* The setupPolly test helper creates a new polly instance which you can access via `this.polly`. The recording name is generated based on the module and test names. */ this.polly.configure({ recordIfMissing: false }); /* start: pseudo test code */ await visit('/login'); await fillIn('email', 'polly@netflix.com'); await fillIn('password', '@pollyjs'); await submit(); /* end: pseudo test code */ expect(location.pathname).to.equal('/browse'); /* The setupPolly test helper will call `this.polly.stop()` when your test has finished. */ }); }); ``` ### Intercept Example {docsify-ignore} ```js import { setupMocha as setupPolly } from '@pollyjs/core'; describe('module', function() { setupPolly(); it('does a thing', function() { const { server } = this.polly; server .get('/ping') .intercept((req, res) => res.sendStatus(200)); expect((await fetch('/ping').status).to.equal(200); }); }); ``` ## Test Hook Ordering Accessing `this.polly` during a test run after the polly instance has been stopped and destroyed produces the following error: !> _You are trying to access an instance of Polly that is no longer available._ If you need to do some work before the polly instance gets destroyed or just need more control on when each of the test hooks are called, `setupMocha` can be invoked as a function or accessed as an object with two methods: `setupMocha.beforeEach` and `setupMocha.afterEach`. Instead of calling `setupMocha()`, register these two hooks separately in the order that fits within your test. ```js import { setupMocha as setupPolly } from '@pollyjs/core'; describe('Netflix Homepage', function() { setupPolly.beforeEach({ /* default configuration options */ }); afterEach(function() { /* do something before the polly instance is destroyed... */ }); setupPolly.afterEach(); it('should be able to sign in', async function() { /* ... */ }); }); ``` ## Configuring ember-mocha If you're using [`ember-mocha`](https://github.com/emberjs/ember-mocha) be sure to use their built-in [hooks API](https://github.com/emberjs/ember-mocha#setup-tests). Otherwise, Polly's mocha test helper will be unable to teardown Polly between tests. An example of how to correctly setup Polly with `ember-mocha`: ```js import { expect } from 'chai'; import { describe, it } from 'mocha'; import { setupApplicationTest } from 'ember-mocha'; import { visit, currentURL } from '@ember/test-helpers'; import { setupMocha as setupPolly } from '@pollyjs/core'; describe('Acceptance | Home', function() { const hooks = setupApplicationTest(); setupPolly( { /* optional config */ }, hooks ); it('can visit /', async function() { await visit('/'); expect(currentURL()).to.equal('/'); }); }); ``` ================================================ FILE: docs/test-frameworks/qunit.md ================================================ # QUnit The `@pollyjs/core` package provides a `setupQunit` utility which will setup a new polly instance for each test as well as stop it once the test has ended. The Polly instance's recording name is derived from the current test name as well as its parent module(s). | Param | Type | Description | | ------ | -------- | ------------------------------------- | | hooks | `Object` | QUnit hooks object | | config | `Object` | [Configuration](configuration) object | ## Usage ### Simple Example {docsify-ignore} ```js import { setupQunit as setupPolly } from '@pollyjs/core'; module('Netflix Homepage', function(hooks) { setupPolly(hooks, { /* default configuration options */ }); test('should be able to sign in', async function(assert) { /* The setupPolly test helper creates a new polly instance which you can access via `this.polly`. The recording name is generated based on the module and test names. */ this.polly.configure({ recordIfMissing: false }); /* start: pseudo test code */ await visit('/login'); await fillIn('email', 'polly@netflix.com'); await fillIn('password', '@pollyjs'); await submit(); /* end: pseudo test code */ assert.equal(location.pathname, '/browse'); /* The setupPolly test helper will call `this.polly.stop()` when your test has finished. */ }); }); ``` ### Intercept Example {docsify-ignore} ```js import { setupQunit as setupPolly } from '@pollyjs/core'; module('module', function(hooks) { setupPolly(hooks); test('does a thing', function(assert) { const { server } = this.polly; server .get('/ping') .intercept((req, res) => res.sendStatus(200)); assert.equal((await fetch('/ping').status, 200); }); }); ``` ## Test Hook Ordering Accessing `this.polly` during a test run after the polly instance has been stopped and destroyed produces the following error: !> _You are trying to access an instance of Polly that is no longer available._ If you need to do some work before the polly instance gets destroyed or just need more control on when each of the test hooks are called, `setupQunit` can be invoked as a function or accessed as an object with two methods: `setupQunit.beforeEach` and `setupQunit.afterEach`. Instead of calling `setupQunit()`, register these two hooks separately in the order that fits within your test. ```js import { setupQunit as setupPolly } from '@pollyjs/core'; module('Netflix Homepage', function(hooks) { setupPolly.beforeEach(hooks, { /* default configuration options */ }); hooks.afterEach(function() { /* do something before the polly instance is destroyed... */ }); setupPolly.afterEach(hooks); test('should be able to sign in', async function() { /* ... */ }); }); ``` ================================================ FILE: examples/.eslintrc.js ================================================ module.exports = { env: { node: true, browser: true } }; ================================================ FILE: examples/client-server/index.html ================================================ Client Server Tests
================================================ FILE: examples/client-server/package.json ================================================ { "name": "@pollyjs/client-server-example", "version": "0.1.0", "private": true, "license": "Apache-2.0", "scripts": { "test": "http-server -p 3000 -o -c-1 -s" }, "devDependencies": { "@pollyjs/adapter-fetch": "*", "@pollyjs/core": "*", "@pollyjs/persister-local-storage": "*", "chai": "*", "http-server": "*", "mocha": "*" } } ================================================ FILE: examples/client-server/tests/events.test.js ================================================ /* global setupPolly */ describe('Events', function () { setupPolly({ adapters: ['fetch'], persister: 'local-storage' }); it('can help test dynamic data', async function () { const { server } = this.polly; let numPosts = 0; server .get('https://jsonplaceholder.typicode.com/posts') .on('response', (_, res) => { numPosts = res.jsonBody().length; }); const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const posts = await res.json(); expect(res.status).to.equal(200); expect(posts.length).to.equal(numPosts); }); }); ================================================ FILE: examples/client-server/tests/intercept.test.js ================================================ /* global setupPolly */ describe('Intercept', function () { setupPolly({ adapters: ['fetch'], persister: 'local-storage' }); it('can mock valid responses', async function () { const { server } = this.polly; server .get('https://jsonplaceholder.typicode.com/posts/:id') .intercept((req, res) => { res.status(200).json({ id: Number(req.params.id), title: `Post ${req.params.id}` }); }); const res = await fetch('https://jsonplaceholder.typicode.com/posts/42'); const post = await res.json(); expect(res.status).to.equal(200); expect(post.id).to.equal(42); expect(post.title).to.equal('Post 42'); }); it('can mock invalid responses', async function () { const { server } = this.polly; server .get('https://jsonplaceholder.typicode.com/posts/404') .intercept((_, res) => { res.status(404).send('Post not found.'); }); const res = await fetch('https://jsonplaceholder.typicode.com/posts/404'); const text = await res.text(); expect(res.status).to.equal(404); expect(text).to.equal('Post not found.'); }); it('can conditionally intercept requests', async function () { const { server } = this.polly; server .get('https://jsonplaceholder.typicode.com/posts/:id') .intercept((req, res, interceptor) => { if (req.params.id === '42') { res.status(200).send('Life'); } else { // Abort out of the intercept handler and continue with the request interceptor.abort(); } }); let res = await fetch('https://jsonplaceholder.typicode.com/posts/42'); expect(res.status).to.equal(200); expect(await res.text()).to.equal('Life'); res = await fetch('https://jsonplaceholder.typicode.com/posts/1'); expect(res.status).to.equal(200); expect((await res.json()).id).to.equal(1); }); }); ================================================ FILE: examples/client-server/tests/setup.js ================================================ // Expose common globals window.PollyJS = window['@pollyjs/core']; window.setupPolly = window.PollyJS.setupMocha; window.expect = window.chai.expect; // Register the fetch adapter and local-storage persister window.PollyJS.Polly.register(window['@pollyjs/adapter-fetch']); window.PollyJS.Polly.register(window['@pollyjs/persister-local-storage']); // Setup Mocha mocha.setup({ ui: 'bdd', noHighlighting: true }); ================================================ FILE: examples/dummy-app/.eslintrc.js ================================================ module.exports = { extends: ['plugin:react/recommended'], settings: { react: { version: '16.5.1' } } }; ================================================ FILE: examples/dummy-app/.gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies /node_modules # testing /coverage # production /build # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: examples/dummy-app/README.md ================================================ This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). Below you will find some information on how to perform common tasks.
You can find the most recent version of this guide [here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md). ## Table of Contents - [Updating to New Releases](#updating-to-new-releases) - [Sending Feedback](#sending-feedback) - [Folder Structure](#folder-structure) - [Available Scripts](#available-scripts) - [npm start](#npm-start) - [npm test](#npm-test) - [npm run build](#npm-run-build) - [npm run eject](#npm-run-eject) - [Supported Browsers](#supported-browsers) - [Supported Language Features and Polyfills](#supported-language-features-and-polyfills) - [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) - [Debugging in the Editor](#debugging-in-the-editor) - [Formatting Code Automatically](#formatting-code-automatically) - [Changing the Page ``](#changing-the-page-title) - [Installing a Dependency](#installing-a-dependency) - [Importing a Component](#importing-a-component) - [Code Splitting](#code-splitting) - [Adding a Stylesheet](#adding-a-stylesheet) - [Post-Processing CSS](#post-processing-css) - [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc) - [Adding Images, Fonts, and Files](#adding-images-fonts-and-files) - [Using the `public` Folder](#using-the-public-folder) - [Changing the HTML](#changing-the-html) - [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system) - [When to Use the `public` Folder](#when-to-use-the-public-folder) - [Using Global Variables](#using-global-variables) - [Adding Bootstrap](#adding-bootstrap) - [Using a Custom Theme](#using-a-custom-theme) - [Adding Flow](#adding-flow) - [Adding a Router](#adding-a-router) - [Adding Custom Environment Variables](#adding-custom-environment-variables) - [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html) - [Adding Temporary Environment Variables In Your Shell](#adding-temporary-environment-variables-in-your-shell) - [Adding Development Environment Variables In `.env`](#adding-development-environment-variables-in-env) - [Can I Use Decorators?](#can-i-use-decorators) - [Fetching Data with AJAX Requests](#fetching-data-with-ajax-requests) - [Integrating with an API Backend](#integrating-with-an-api-backend) - [Node](#node) - [Ruby on Rails](#ruby-on-rails) - [Proxying API Requests in Development](#proxying-api-requests-in-development) - ["Invalid Host Header" Errors After Configuring Proxy](#invalid-host-header-errors-after-configuring-proxy) - [Configuring the Proxy Manually](#configuring-the-proxy-manually) - [Configuring a WebSocket Proxy](#configuring-a-websocket-proxy) - [Using HTTPS in Development](#using-https-in-development) - [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) - [Pre-Rendering into Static HTML Files](#pre-rendering-into-static-html-files) - [Injecting Data from the Server into the Page](#injecting-data-from-the-server-into-the-page) - [Running Tests](#running-tests) - [Filename Conventions](#filename-conventions) - [Command Line Interface](#command-line-interface) - [Version Control Integration](#version-control-integration) - [Writing Tests](#writing-tests) - [Testing Components](#testing-components) - [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries) - [Initializing Test Environment](#initializing-test-environment) - [Focusing and Excluding Tests](#focusing-and-excluding-tests) - [Coverage Reporting](#coverage-reporting) - [Continuous Integration](#continuous-integration) - [Disabling jsdom](#disabling-jsdom) - [Snapshot Testing](#snapshot-testing) - [Editor Integration](#editor-integration) - [Debugging Tests](#debugging-tests) - [Debugging Tests in Chrome](#debugging-tests-in-chrome) - [Debugging Tests in Visual Studio Code](#debugging-tests-in-visual-studio-code) - [Developing Components in Isolation](#developing-components-in-isolation) - [Getting Started with Storybook](#getting-started-with-storybook) - [Getting Started with Styleguidist](#getting-started-with-styleguidist) - [Publishing Components to npm](#publishing-components-to-npm) - [Making a Progressive Web App](#making-a-progressive-web-app) - [Opting Out of Caching](#opting-out-of-caching) - [Offline-First Considerations](#offline-first-considerations) - [Progressive Web App Metadata](#progressive-web-app-metadata) - [Analyzing the Bundle Size](#analyzing-the-bundle-size) - [Deployment](#deployment) - [Static Server](#static-server) - [Other Solutions](#other-solutions) - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) - [Building for Relative Paths](#building-for-relative-paths) - [Azure](#azure) - [Firebase](#firebase) - [GitHub Pages](#github-pages) - [Heroku](#heroku) - [Netlify](#netlify) - [Now](#now) - [S3 and CloudFront](#s3-and-cloudfront) - [Surge](#surge) - [Advanced Configuration](#advanced-configuration) - [Troubleshooting](#troubleshooting) - [`npm start` doesn’t detect changes](#npm-start-doesnt-detect-changes) - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) - [`npm run build` exits too early](#npm-run-build-exits-too-early) - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) - [`npm run build` fails to minify](#npm-run-build-fails-to-minify) - [Moment.js locales are missing](#momentjs-locales-are-missing) - [Alternatives to Ejecting](#alternatives-to-ejecting) - [Something Missing?](#something-missing) ## Updating to New Releases Create React App is divided into two packages: - `create-react-app` is a global command-line utility that you use to create new projects. - `react-scripts` is a development dependency in the generated projects (including this one). You almost never need to update `create-react-app` itself: it delegates all the setup to `react-scripts`. When you run `create-react-app`, it always creates the project with the latest version of `react-scripts` so you’ll get all the new features and improvements in newly created apps automatically. To update an existing project to a new version of `react-scripts`, [open the changelog](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md), find the version you’re currently on (check `package.json` in this folder if you’re not sure), and apply the migration instructions for the newer versions. In most cases bumping the `react-scripts` version in `package.json` and running `npm install` in this folder should be enough, but it’s good to consult the [changelog](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md) for potential breaking changes. We commit to keeping the breaking changes minimal so you can upgrade `react-scripts` painlessly. ## Sending Feedback We are always open to [your feedback](https://github.com/facebookincubator/create-react-app/issues). ## Folder Structure After creation, your project should look like this: ``` my-app/ README.md node_modules/ package.json public/ index.html favicon.ico src/ App.css App.js App.test.js index.css index.js logo.svg ``` For the project to build, **these files must exist with exact filenames**: - `public/index.html` is the page template; - `src/index.js` is the JavaScript entry point. You can delete or rename the other files. You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.<br> You need to **put any JS and CSS files inside `src`**, otherwise Webpack won’t see them. Only files inside `public` can be used from `public/index.html`.<br> Read instructions below for using assets from JavaScript and HTML. You can, however, create more top-level directories.<br> They will not be included in the production build so you can use them for things like documentation. ## Available Scripts In the project directory, you can run: ### `npm start` Runs the app in the development mode.<br> Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.<br> You will also see any lint errors in the console. ### `npm test` Launches the test runner in the interactive watch mode.<br> See the section about [running tests](#running-tests) for more information. ### `npm run build` Builds the app for production to the `build` folder.<br> It correctly bundles React in production mode and optimizes the build for the best performance. The build is minified and the filenames include the hashes.<br> Your app is ready to be deployed! See the section about [deployment](#deployment) for more information. ### `npm run eject` **Note: this is a one-way operation. Once you `eject`, you can’t go back!** If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. ## Supported Browsers By default, the generated project uses the latest version of React. You can refer [to the React documentation](https://reactjs.org/docs/react-dom.html#browser-support) for more information about supported browsers. ## Supported Language Features and Polyfills This project supports a superset of the latest JavaScript standard.<br> In addition to [ES6](https://github.com/lukehoban/es6features) syntax features, it also supports: - [Exponentiation Operator](https://github.com/rwaldron/exponentiation-operator) (ES2016). - [Async/await](https://github.com/tc39/ecmascript-asyncawait) (ES2017). - [Object Rest/Spread Properties](https://github.com/sebmarkbage/ecmascript-rest-spread) (stage 3 proposal). - [Dynamic import()](https://github.com/tc39/proposal-dynamic-import) (stage 3 proposal) - [Class Fields and Static Properties](https://github.com/tc39/proposal-class-public-fields) (part of stage 3 proposal). - [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) and [Flow](https://flowtype.org/) syntax. Learn more about [different proposal stages](https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-). While we recommend using experimental proposals with some caution, Facebook heavily uses these features in the product code, so we intend to provide [codemods](https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb) if any of these proposals change in the future. Note that **the project only includes a few ES6 [polyfills](https://en.wikipedia.org/wiki/Polyfill)**: - [`Object.assign()`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) via [`object-assign`](https://github.com/sindresorhus/object-assign). - [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) via [`promise`](https://github.com/then/promise). - [`fetch()`](https://developer.mozilla.org/en/docs/Web/API/Fetch_API) via [`whatwg-fetch`](https://github.com/github/fetch). If you use any other ES6+ features that need **runtime support** (such as `Array.from()` or `Symbol`), make sure you are including the appropriate polyfills manually, or that the browsers you are targeting already support them. Also note that using some newer syntax features like `for...of` or `[...nonArrayValue]` causes Babel to emit code that depends on ES6 runtime features and might not work without a polyfill. When in doubt, use [Babel REPL](https://babeljs.io/repl/) to see what any specific syntax compiles down to. ## Syntax Highlighting in the Editor To configure the syntax highlighting in your favorite text editor, head to the [relevant Babel documentation page](https://babeljs.io/docs/editors) and follow the instructions. Some of the most popular editors are covered. ## Displaying Lint Output in the Editor > Note: this feature is available with `react-scripts@0.2.0` and higher.<br> > It also only works with npm 3 or higher. Some editors, including Sublime Text, Atom, and Visual Studio Code, provide plugins for ESLint. They are not required for linting. You should see the linter output right in your terminal as well as the browser console. However, if you prefer the lint results to appear right in your editor, there are some extra steps you can do. You would need to install an ESLint plugin for your editor first. Then, add a file called `.eslintrc` to the project root: ```js { "extends": "react-app" } ``` Now your editor should report the linting warnings. Note that even if you edit your `.eslintrc` file further, these changes will **only affect the editor integration**. They won’t affect the terminal and in-browser lint output. This is because Create React App intentionally provides a minimal set of rules that find common mistakes. If you want to enforce a coding style for your project, consider using [Prettier](https://github.com/jlongster/prettier) instead of ESLint style rules. ## Debugging in the Editor **This feature is currently only supported by [Visual Studio Code](https://code.visualstudio.com) and [WebStorm](https://www.jetbrains.com/webstorm/).** Visual Studio Code and WebStorm support debugging out of the box with Create React App. This enables you as a developer to write and debug your React code without leaving the editor, and most importantly it enables you to have a continuous development workflow, where context switching is minimal, as you don’t have to switch between tools. ### Visual Studio Code You would need to have the latest version of [VS Code](https://code.visualstudio.com) and VS Code [Chrome Debugger Extension](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) installed. Then add the block below to your `launch.json` file and put it inside the `.vscode` folder in your app’s root directory. ```json { "version": "0.2.0", "configurations": [ { "name": "Chrome", "type": "chrome", "request": "launch", "url": "http://localhost:3000", "webRoot": "${workspaceRoot}/src", "sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*" } } ] } ``` > Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration). Start your app by running `npm start`, and start debugging in VS Code by pressing `F5` or by clicking the green debug icon. You can now write code, set breakpoints, make changes to the code, and debug your newly modified code—all from your editor. Having problems with VS Code Debugging? Please see their [troubleshooting guide](https://github.com/Microsoft/vscode-chrome-debug/blob/master/README.md#troubleshooting). ### WebStorm You would need to have [WebStorm](https://www.jetbrains.com/webstorm/) and [JetBrains IDE Support](https://chrome.google.com/webstore/detail/jetbrains-ide-support/hmhgeddbohgjknpmjagkdomcpobmllji) Chrome extension installed. In the WebStorm menu `Run` select `Edit Configurations...`. Then click `+` and select `JavaScript Debug`. Paste `http://localhost:3000` into the URL field and save the configuration. > Note: the URL may be different if you've made adjustments via the [HOST or PORT environment variables](#advanced-configuration). Start your app by running `npm start`, then press `^D` on macOS or `F9` on Windows and Linux or click the green debug icon to start debugging in WebStorm. The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine. ## Formatting Code Automatically Prettier is an opinionated code formatter with support for JavaScript, CSS and JSON. With Prettier you can format the code you write automatically to ensure a code style within your project. See the [Prettier's GitHub page](https://github.com/prettier/prettier) for more information, and look at this [page to see it in action](https://prettier.github.io/prettier/). To format our code whenever we make a commit in git, we need to install the following dependencies: ```sh npm install --save husky lint-staged prettier ``` Alternatively you may use `yarn`: ```sh yarn add husky lint-staged prettier ``` - `husky` makes it easy to use githooks as if they are npm scripts. - `lint-staged` allows us to run scripts on staged files in git. See this [blog post about lint-staged to learn more about it](https://medium.com/@okonetchnikov/make-linting-great-again-f3890e1ad6b8). - `prettier` is the JavaScript formatter we will run before commits. Now we can make sure every file is formatted correctly by adding a few lines to the `package.json` in the project root. Add the following line to `scripts` section: ```diff "scripts": { + "precommit": "lint-staged", "start": "react-scripts start", "build": "react-scripts build", ``` Next we add a 'lint-staged' field to the `package.json`, for example: ```diff "dependencies": { // ... }, + "lint-staged": { + "src/**/*.{js,jsx,json,css}": [ + "prettier --single-quote --write", + "git add" + ] + }, "scripts": { ``` Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,json,css}"` to format your entire project for the first time. Next you might want to integrate Prettier in your favorite editor. Read the section on [Editor Integration](https://prettier.io/docs/en/editors.html) on the Prettier GitHub page. ## Changing the Page `<title>` You can find the source HTML file in the `public` folder of the generated project. You may edit the `<title>` tag in it to change the title from “React App” to anything else. Note that normally you wouldn’t edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML. If you need to dynamically update the page title based on the content, you can use the browser [`document.title`](https://developer.mozilla.org/en-US/docs/Web/API/Document/title) API. For more complex scenarios when you want to change the title from React components, you can use [React Helmet](https://github.com/nfl/react-helmet), a third party library. If you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server). Alternatively, you can pre-build each page as a static HTML file which then loads the JavaScript bundle, which is covered [here](#pre-rendering-into-static-html-files). ## Installing a Dependency The generated project includes React and ReactDOM as dependencies. It also includes a set of scripts used by Create React App as a development dependency. You may install other dependencies (for example, React Router) with `npm`: ```sh npm install --save react-router ``` Alternatively you may use `yarn`: ```sh yarn add react-router ``` This works for any library, not just `react-router`. ## Importing a Component This project setup supports ES6 modules thanks to Babel.<br> While you can still use `require()` and `module.exports`, we encourage you to use [`import` and `export`](http://exploringjs.com/es6/ch_modules.html) instead. For example: ### `Button.js` ```js import React, { Component } from 'react'; class Button extends Component { render() { // ... } } export default Button; // Don’t forget to use export default! ``` ### `DangerButton.js` ```js import React, { Component } from 'react'; import Button from './Button'; // Import a component from another file class DangerButton extends Component { render() { return <Button color="red" />; } } export default DangerButton; ``` Be aware of the [difference between default and named exports](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281). It is a common source of mistakes. We suggest that you stick to using default imports and exports when a module only exports a single thing (for example, a component). That’s what you get when you use `export default Button` and `import Button from './Button'`. Named exports are useful for utility modules that export several functions. A module may have at most one default export and as many named exports as you like. Learn more about ES6 modules: - [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281) - [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html) - [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules) ## Code Splitting Instead of downloading the entire app before users can use it, code splitting allows you to split your code into small chunks which you can then load on demand. This project setup supports code splitting via [dynamic `import()`](http://2ality.com/2017/01/import-operator.html#loading-code-on-demand). Its [proposal](https://github.com/tc39/proposal-dynamic-import) is in stage 3. The `import()` function-like form takes the module name as an argument and returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which always resolves to the namespace object of the module. Here is an example: ### `moduleA.js` ```js const moduleA = 'Hello'; export { moduleA }; ``` ### `App.js` ```js import React, { Component } from 'react'; class App extends Component { handleClick = () => { import('./moduleA') .then(({ moduleA }) => { // Use moduleA }) .catch(err => { // Handle failure }); }; render() { return ( <div> <button onClick={this.handleClick}>Load</button> </div> ); } } export default App; ``` This will make `moduleA.js` and all its unique dependencies as a separate chunk that only loads after the user clicks the 'Load' button. You can also use it with `async` / `await` syntax if you prefer it. ### With React Router If you are using React Router check out [this tutorial](http://serverless-stack.com/chapters/code-splitting-in-create-react-app.html) on how to use code splitting with it. You can find the companion GitHub repository [here](https://github.com/AnomalyInnovations/serverless-stack-demo-client/tree/code-splitting-in-create-react-app). Also check out the [Code Splitting](https://reactjs.org/docs/code-splitting.html) section in React documentation. ## Adding a Stylesheet This project setup uses [Webpack](https://webpack.js.org/) for handling all assets. Webpack offers a custom way of “extending” the concept of `import` beyond JavaScript. To express that a JavaScript file depends on a CSS file, you need to **import the CSS from the JavaScript file**: ### `Button.css` ```css .Button { padding: 20px; } ``` ### `Button.js` ```js import React, { Component } from 'react'; import './Button.css'; // Tell Webpack that Button.js uses these styles class Button extends Component { render() { // You can use them as regular CSS styles return <div className="Button" />; } } ``` **This is not required for React** but many people find this feature convenient. You can read about the benefits of this approach [here](https://medium.com/seek-blog/block-element-modifying-your-javascript-components-d7f99fcab52b). However you should be aware that this makes your code less portable to other build tools and environments than Webpack. In development, expressing dependencies this way allows your styles to be reloaded on the fly as you edit them. In production, all CSS files will be concatenated into a single minified `.css` file in the build output. If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool. ## Post-Processing CSS This project setup minifies your CSS and adds vendor prefixes to it automatically through [Autoprefixer](https://github.com/postcss/autoprefixer) so you don’t need to worry about it. For example, this: ```css .App { display: flex; flex-direction: row; align-items: center; } ``` becomes this: ```css .App { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; -webkit-box-align: center; -ms-flex-align: center; align-items: center; } ``` If you need to disable autoprefixing for some reason, [follow this section](https://github.com/postcss/autoprefixer#disabling). ## Adding a CSS Preprocessor (Sass, Less etc.) Generally, we recommend that you don’t reuse the same CSS classes across different components. For example, instead of using a `.Button` CSS class in `<AcceptButton>` and `<RejectButton>` components, we recommend creating a `<Button>` component with its own `.Button` styles, that both `<AcceptButton>` and `<RejectButton>` can render (but [not inherit](https://facebook.github.io/react/docs/composition-vs-inheritance.html)). Following this rule often makes CSS preprocessors less useful, as features like mixins and nesting are replaced by component composition. You can, however, integrate a CSS preprocessor if you find it valuable. In this walkthrough, we will be using Sass, but you can also use Less, or another alternative. First, let’s install the command-line interface for Sass: ```sh npm install --save node-sass-chokidar ``` Alternatively you may use `yarn`: ```sh yarn add node-sass-chokidar ``` Then in `package.json`, add the following lines to `scripts`: ```diff "scripts": { + "build-css": "node-sass-chokidar src/ -o src/", + "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive", "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", ``` > Note: To use a different preprocessor, replace `build-css` and `watch-css` commands according to your preprocessor’s documentation. Now you can rename `src/App.css` to `src/App.scss` and run `npm run watch-css`. The watcher will find every Sass file in `src` subdirectories, and create a corresponding CSS file next to it, in our case overwriting `src/App.css`. Since `src/App.js` still imports `src/App.css`, the styles become a part of your application. You can now edit `src/App.scss`, and `src/App.css` will be regenerated. To share variables between Sass files, you can use Sass imports. For example, `src/App.scss` and other component style files could include `@import "./shared.scss";` with variable definitions. To enable importing files without using relative paths, you can add the `--include-path` option to the command in `package.json`. ``` "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/", "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive", ``` This will allow you to do imports like ```scss @import 'styles/_colors.scss'; // assuming a styles directory under src/ @import 'nprogress/nprogress'; // importing a css file from the nprogress node module ``` At this point you might want to remove all CSS files from the source control, and add `src/**/*.css` to your `.gitignore` file. It is generally a good practice to keep the build products outside of the source control. As a final step, you may find it convenient to run `watch-css` automatically with `npm start`, and run `build-css` as a part of `npm run build`. You can use the `&&` operator to execute two scripts sequentially. However, there is no cross-platform way to run two scripts in parallel, so we will install a package for this: ```sh npm install --save npm-run-all ``` Alternatively you may use `yarn`: ```sh yarn add npm-run-all ``` Then we can change `start` and `build` scripts to include the CSS preprocessor commands: ```diff "scripts": { "build-css": "node-sass-chokidar src/ -o src/", "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive", - "start": "react-scripts start", - "build": "react-scripts build", + "start-js": "react-scripts start", + "start": "npm-run-all -p watch-css start-js", + "build-js": "react-scripts build", + "build": "npm-run-all build-css build-js", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } ``` Now running `npm start` and `npm run build` also builds Sass files. **Why `node-sass-chokidar`?** `node-sass` has been reported as having the following issues: - `node-sass --watch` has been reported to have _performance issues_ in certain conditions when used in a virtual machine or with docker. - Infinite styles compiling [#1939](https://github.com/facebookincubator/create-react-app/issues/1939) - `node-sass` has been reported as having issues with detecting new files in a directory [#1891](https://github.com/sass/node-sass/issues/1891) `node-sass-chokidar` is used here as it addresses these issues. ## Adding Images, Fonts, and Files With Webpack, using static assets like images and fonts works similarly to CSS. You can **`import` a file right in a JavaScript module**. This tells Webpack to include that file in the bundle. Unlike CSS imports, importing a file gives you a string value. This value is the final path you can reference in your code, e.g. as the `src` attribute of an image or the `href` of a link to a PDF. To reduce the number of requests to the server, importing images that are less than 10,000 bytes returns a [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) instead of a path. This applies to the following file extensions: bmp, gif, jpg, jpeg, and png. SVG files are excluded due to [#1153](https://github.com/facebookincubator/create-react-app/issues/1153). Here is an example: ```js import React from 'react'; import logo from './logo.png'; // Tell Webpack this JS file uses this image console.log(logo); // /logo.84287d09.png function Header() { // Import result is the URL of your image return <img src={logo} alt="Logo" />; } export default Header; ``` This ensures that when the project is built, Webpack will correctly move the images into the build folder, and provide us with correct paths. This works in CSS too: ```css .Logo { background-image: url(./logo.png); } ``` Webpack finds all relative module references in CSS (they start with `./`) and replaces them with the final paths from the compiled bundle. If you make a typo or accidentally delete an important file, you will see a compilation error, just like when you import a non-existent JavaScript module. The final filenames in the compiled bundle are generated by Webpack from content hashes. If the file content changes in the future, Webpack will give it a different name in production so you don’t need to worry about long-term caching of assets. Please be advised that this is also a custom feature of Webpack. **It is not required for React** but many people enjoy it (and React Native uses a similar mechanism for images).<br> An alternative way of handling static assets is described in the next section. ## Using the `public` Folder > Note: this feature is available with `react-scripts@0.5.0` and higher. ### Changing the HTML The `public` folder contains the HTML file so you can tweak it, for example, to [set the page title](#changing-the-page-title). The `<script>` tag with the compiled code will be added to it automatically during the build process. ### Adding Assets Outside of the Module System You can also add other assets to the `public` folder. Note that we normally encourage you to `import` assets in JavaScript files instead. For example, see the sections on [adding a stylesheet](#adding-a-stylesheet) and [adding images and fonts](#adding-images-fonts-and-files). This mechanism provides a number of benefits: - Scripts and stylesheets get minified and bundled together to avoid extra network requests. - Missing files cause compilation errors instead of 404 errors for your users. - Result filenames include content hashes so you don’t need to worry about browsers caching their old versions. However there is an **escape hatch** that you can use to add an asset outside of the module system. If you put a file into the `public` folder, it will **not** be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the `public` folder, you need to use a special variable called `PUBLIC_URL`. Inside `index.html`, you can use it like this: ```html <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> ``` Only files inside the `public` folder will be accessible by `%PUBLIC_URL%` prefix. If you need to use a file from `src` or `node_modules`, you’ll have to copy it there to explicitly specify your intention to make this file a part of the build. When you run `npm run build`, Create React App will substitute `%PUBLIC_URL%` with a correct absolute path so your project works even if you use client-side routing or host it at a non-root URL. In JavaScript code, you can use `process.env.PUBLIC_URL` for similar purposes: ```js render() { // Note: this is an escape hatch and should be used sparingly! // Normally we recommend using `import` for getting asset URLs // as described in “Adding Images and Fonts” above this section. return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />; } ``` Keep in mind the downsides of this approach: - None of the files in `public` folder get post-processed or minified. - Missing files will not be called at compilation time, and will cause 404 errors for your users. - Result filenames won’t include content hashes so you’ll need to add query arguments or rename them every time they change. ### When to Use the `public` Folder Normally we recommend importing [stylesheets](#adding-a-stylesheet), [images, and fonts](#adding-images-fonts-and-files) from JavaScript. The `public` folder is useful as a workaround for a number of less common cases: - You need a file with a specific name in the build output, such as [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest). - You have thousands of images and need to dynamically reference their paths. - You want to include a small script like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code. - Some library may be incompatible with Webpack and you have no other option but to include it as a `<script>` tag. Note that if you add a `<script>` that declares global variables, you also need to read the next section on using them. ## Using Global Variables When you include a script in the HTML file that defines global variables and try to use one of these variables in the code, the linter will complain because it cannot see the definition of the variable. You can avoid this by reading the global variable explicitly from the `window` object, for example: ```js const $ = window.$; ``` This makes it obvious you are using a global variable intentionally rather than because of a typo. Alternatively, you can force the linter to ignore any line by adding `// eslint-disable-line` after it. ## Adding Bootstrap You don’t have to use [React Bootstrap](https://react-bootstrap.github.io) together with React but it is a popular library for integrating Bootstrap with React apps. If you need it, you can integrate it with Create React App by following these steps: Install React Bootstrap and Bootstrap from npm. React Bootstrap does not include Bootstrap CSS so this needs to be installed as well: ```sh npm install --save react-bootstrap bootstrap@3 ``` Alternatively you may use `yarn`: ```sh yarn add react-bootstrap bootstrap@3 ``` Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your `src/index.js` file: ```js import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap-theme.css'; // Put any other imports below so that CSS from your // components takes precedence over default styles. ``` Import required React Bootstrap components within `src/App.js` file or your custom component files: ```js import { Navbar, Jumbotron, Button } from 'react-bootstrap'; ``` Now you are ready to use the imported React Bootstrap components within your component hierarchy defined in the render method. Here is an example [`App.js`](https://gist.githubusercontent.com/gaearon/85d8c067f6af1e56277c82d19fd4da7b/raw/6158dd991b67284e9fc8d70b9d973efe87659d72/App.js) redone using React Bootstrap. ### Using a Custom Theme Sometimes you might need to tweak the visual styles of Bootstrap (or equivalent package).<br> We suggest the following approach: - Create a new package that depends on the package you wish to customize, e.g. Bootstrap. - Add the necessary build steps to tweak the theme, and publish your package on npm. - Install your own theme npm package as a dependency of your app. Here is an example of adding a [customized Bootstrap](https://medium.com/@tacomanator/customizing-create-react-app-aa9ffb88165) that follows these steps. ## Adding Flow Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. To add Flow to a Create React App project, follow these steps: 1. Run `npm install --save flow-bin` (or `yarn add flow-bin`). 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 3. Run `npm run flow init` (or `yarn flow init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory. 4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. In the future we plan to integrate it into Create React App even more closely. To learn more about Flow, check out [its documentation](https://flowtype.org/). ## Adding a Router Create React App doesn't prescribe a specific routing solution, but [React Router](https://reacttraining.com/react-router/) is the most popular one. To add it, run: ```sh npm install --save react-router-dom ``` Alternatively you may use `yarn`: ```sh yarn add react-router-dom ``` To try it, delete all the code in `src/App.js` and replace it with any of the examples on its website. The [Basic Example](https://reacttraining.com/react-router/web/example/basic) is a good place to get started. Note that [you may need to configure your production server to support client-side routing](#serving-apps-with-client-side-routing) before deploying your app. ## Adding Custom Environment Variables > Note: this feature is available with `react-scripts@0.2.3` and higher. Your project can consume variables declared in your environment as if they were declared locally in your JS files. By default you will have `NODE_ENV` defined for you, and any other environment variables starting with `REACT_APP_`. **The environment variables are embedded during the build time**. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime. To read them at runtime, you would need to load HTML into memory on the server and replace placeholders in runtime, just like [described here](#injecting-data-from-the-server-into-the-page). Alternatively you can rebuild the app on the server anytime you change them. > Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid accidentally [exposing a private key on the machine that could have the same name](https://github.com/facebookincubator/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. These environment variables will be defined for you on `process.env`. For example, having an environment variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`. There is also a special built-in environment variable called `NODE_ENV`. You can read it from `process.env.NODE_ENV`. When you run `npm start`, it is always equal to `'development'`, when you run `npm test` it is always equal to `'test'`, and when you run `npm run build` to make a production bundle, it is always equal to `'production'`. **You cannot override `NODE_ENV` manually.** This prevents developers from accidentally deploying a slow development build to production. These environment variables can be useful for displaying information conditionally based on where the project is deployed or consuming sensitive data that lives outside of version control. First, you need to have environment variables defined. For example, let’s say you wanted to consume a secret defined in the environment inside a `<form>`: ```jsx render() { return ( <div> <small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small> <form> <input type="hidden" defaultValue={process.env.REACT_APP_SECRET_CODE} /> </form> </div> ); } ``` During the build, `process.env.REACT_APP_SECRET_CODE` will be replaced with the current value of the `REACT_APP_SECRET_CODE` environment variable. Remember that the `NODE_ENV` variable will be set for you automatically. When you load the app in the browser and inspect the `<input>`, you will see its value set to `abcdef`, and the bold text will show the environment provided when using `npm start`: ```html <div> <small>You are running this application in <b>development</b> mode.</small> <form><input type="hidden" value="abcdef" /></form> </div> ``` The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this value, we need to have it defined in the environment. This can be done using two ways: either in your shell or in a `.env` file. Both of these ways are described in the next few sections. Having access to the `NODE_ENV` is also useful for performing actions conditionally: ```js if (process.env.NODE_ENV !== 'production') { analytics.disable(); } ``` When you compile the app with `npm run build`, the minification step will strip out this condition, and the resulting bundle will be smaller. ### Referencing Environment Variables in the HTML > Note: this feature is available with `react-scripts@0.9.0` and higher. You can also access the environment variables starting with `REACT_APP_` in the `public/index.html`. For example: ```html <title>%REACT_APP_WEBSITE_NAME% ``` Note that the caveats from the above section apply: - Apart from a few built-in variables (`NODE_ENV` and `PUBLIC_URL`), variable names must start with `REACT_APP_` to work. - The environment variables are injected at build time. If you need to inject them at runtime, [follow this approach instead](#generating-dynamic-meta-tags-on-the-server). ### Adding Temporary Environment Variables In Your Shell Defining environment variables can vary between OSes. It’s also important to know that this manner is temporary for the life of the shell session. #### Windows (cmd.exe) ```cmd set "REACT_APP_SECRET_CODE=abcdef" && npm start ``` (Note: Quotes around the variable assignment are required to avoid a trailing whitespace.) #### Windows (Powershell) ```Powershell ($env:REACT_APP_SECRET_CODE = "abcdef") -and (npm start) ``` #### Linux, macOS (Bash) ```bash REACT_APP_SECRET_CODE=abcdef npm start ``` ### Adding Development Environment Variables In `.env` > Note: this feature is available with `react-scripts@0.5.0` and higher. To define permanent environment variables, create a file called `.env` in the root of your project: ``` REACT_APP_SECRET_CODE=abcdef ``` > Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid [accidentally exposing a private key on the machine that could have the same name](https://github.com/facebookincubator/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. `.env` files **should be** checked into source control (with the exclusion of `.env*.local`). #### What other `.env` files can be used? > Note: this feature is **available with `react-scripts@1.0.0` and higher**. - `.env`: Default. - `.env.local`: Local overrides. **This file is loaded for all environments except test.** - `.env.development`, `.env.test`, `.env.production`: Environment-specific settings. - `.env.development.local`, `.env.test.local`, `.env.production.local`: Local overrides of environment-specific settings. Files on the left have more priority than files on the right: - `npm start`: `.env.development.local`, `.env.development`, `.env.local`, `.env` - `npm run build`: `.env.production.local`, `.env.production`, `.env.local`, `.env` - `npm test`: `.env.test.local`, `.env.test`, `.env` (note `.env.local` is missing) These variables will act as the defaults if the machine does not explicitly set them.
Please refer to the [dotenv documentation](https://github.com/motdotla/dotenv) for more details. > Note: If you are defining environment variables for development, your CI and/or hosting platform will most likely need > these defined as well. Consult their documentation how to do this. For example, see the documentation for [Travis CI](https://docs.travis-ci.com/user/environment-variables/) or [Heroku](https://devcenter.heroku.com/articles/config-vars). #### Expanding Environment Variables In `.env` > Note: this feature is available with `react-scripts@1.1.0` and higher. Expand variables already on your machine for use in your `.env` file (using [dotenv-expand](https://github.com/motdotla/dotenv-expand)). For example, to get the environment variable `npm_package_version`: ``` REACT_APP_VERSION=$npm_package_version # also works: # REACT_APP_VERSION=${npm_package_version} ``` Or expand variables local to the current `.env` file: ``` DOMAIN=www.example.com REACT_APP_FOO=$DOMAIN/foo REACT_APP_BAR=$DOMAIN/bar ``` ## Can I Use Decorators? Many popular libraries use [decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841) in their documentation.
Create React App doesn’t support decorator syntax at the moment because: - It is an experimental proposal and is subject to change. - The current specification version is not officially supported by Babel. - If the specification changes, we won’t be able to write a codemod because we don’t use them internally at Facebook. However in many cases you can rewrite decorator-based code without decorators just as fine.
Please refer to these two threads for reference: - [#214](https://github.com/facebookincubator/create-react-app/issues/214) - [#411](https://github.com/facebookincubator/create-react-app/issues/411) Create React App will add decorator support when the specification advances to a stable stage. ## Fetching Data with AJAX Requests React doesn't prescribe a specific approach to data fetching, but people commonly use either a library like [axios](https://github.com/axios/axios) or the [`fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) provided by the browser. Conveniently, Create React App includes a polyfill for `fetch()` so you can use it without worrying about the browser support. The global `fetch` function allows to easily makes AJAX requests. It takes in a URL as an input and returns a `Promise` that resolves to a `Response` object. You can find more information about `fetch` [here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). This project also includes a [Promise polyfill](https://github.com/then/promise) which provides a full implementation of Promises/A+. A Promise represents the eventual result of an asynchronous operation, you can find more information about Promises [here](https://www.promisejs.org/) and [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). Both axios and `fetch()` use Promises under the hood. You can also use the [`async / await`](https://davidwalsh.name/async-await) syntax to reduce the callback nesting. You can learn more about making AJAX requests from React components in [the FAQ entry on the React website](https://reactjs.org/docs/faq-ajax.html). ## Integrating with an API Backend These tutorials will help you to integrate your app with an API backend running on another port, using `fetch()` to access it. ### Node Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). ### Ruby on Rails Check out [this tutorial](https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/). You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo-rails). ## Proxying API Requests in Development > Note: this feature is available with `react-scripts@0.2.3` and higher. People often serve the front-end React app from the same host and port as their backend implementation.
For example, a production setup might look like this after the app is deployed: ``` / - static server returns index.html with React app /todos - static server returns index.html with React app /api/todos - server handles any /api/* requests using the backend implementation ``` Such setup is **not** required. However, if you **do** have a setup like this, it is convenient to write requests like `fetch('/api/todos')` without worrying about redirecting them to another host or port during development. To tell the development server to proxy any unknown requests to your API server in development, add a `proxy` field to your `package.json`, for example: ```js "proxy": "http://localhost:4000", ``` This way, when you `fetch('/api/todos')` in development, the development server will recognize that it’s not a static asset, and will proxy your request to `http://localhost:4000/api/todos` as a fallback. The development server will **only** attempt to send requests without `text/html` in its `Accept` header to the proxy. Conveniently, this avoids [CORS issues](http://stackoverflow.com/questions/21854516/understanding-ajax-cors-and-security-considerations) and error messages like this in development: ``` Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. ``` Keep in mind that `proxy` only has effect in development (with `npm start`), and it is up to you to ensure that URLs like `/api/todos` point to the right thing in production. You don’t have to use the `/api` prefix. Any unrecognized request without a `text/html` accept header will be redirected to the specified `proxy`. The `proxy` option supports HTTP, HTTPS and WebSocket connections.
If the `proxy` option is **not** flexible enough for you, alternatively you can: - [Configure the proxy yourself](#configuring-the-proxy-manually) - Enable CORS on your server ([here’s how to do it for Express](http://enable-cors.org/server_expressjs.html)). - Use [environment variables](#adding-custom-environment-variables) to inject the right server host and port into your app. ### "Invalid Host Header" Errors After Configuring Proxy When you enable the `proxy` option, you opt into a more strict set of host checks. This is necessary because leaving the backend open to remote hosts makes your computer vulnerable to DNS rebinding attacks. The issue is explained in [this article](https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a) and [this issue](https://github.com/webpack/webpack-dev-server/issues/887). This shouldn’t affect you when developing on `localhost`, but if you develop remotely like [described here](https://github.com/facebookincubator/create-react-app/issues/2271), you will see this error in the browser after enabling the `proxy` option: > Invalid Host header To work around it, you can specify your public development host in a file called `.env.development` in the root of your project: ``` HOST=mypublicdevhost.com ``` If you restart the development server now and load the app from the specified host, it should work. If you are still having issues or if you’re using a more exotic environment like a cloud editor, you can bypass the host check completely by adding a line to `.env.development.local`. **Note that this is dangerous and exposes your machine to remote code execution from malicious websites:** ``` # NOTE: THIS IS DANGEROUS! # It exposes your machine to attacks from the websites you visit. DANGEROUSLY_DISABLE_HOST_CHECK=true ``` We don’t recommend this approach. ### Configuring the Proxy Manually > Note: this feature is available with `react-scripts@1.0.0` and higher. If the `proxy` option is **not** flexible enough for you, you can specify an object in the following form (in `package.json`).
You may also specify any configuration value [`http-proxy-middleware`](https://github.com/chimurai/http-proxy-middleware#options) or [`http-proxy`](https://github.com/nodejitsu/node-http-proxy#options) supports. ```js { // ... "proxy": { "/api": { "target": "", "ws": true // ... } } // ... } ``` All requests matching this path will be proxies, no exceptions. This includes requests for `text/html`, which the standard `proxy` option does not proxy. If you need to specify multiple proxies, you may do so by specifying additional entries. Matches are regular expressions, so that you can use a regexp to match multiple paths. ```js { // ... "proxy": { // Matches any request starting with /api "/api": { "target": "", "ws": true // ... }, // Matches any request starting with /foo "/foo": { "target": "", "ssl": true, "pathRewrite": { "^/foo": "/foo/beta" } // ... }, // Matches /bar/abc.html but not /bar/sub/def.html "/bar/[^/]*[.]html": { "target": "", // ... }, // Matches /baz/abc.html and /baz/sub/def.html "/baz/.*/.*[.]html": { "target": "" // ... } } // ... } ``` ### Configuring a WebSocket Proxy When setting up a WebSocket proxy, there are a some extra considerations to be aware of. If you’re using a WebSocket engine like [Socket.io](https://socket.io/), you must have a Socket.io server running that you can use as the proxy target. Socket.io will not work with a standard WebSocket server. Specifically, don't expect Socket.io to work with [the websocket.org echo test](http://websocket.org/echo.html). There’s some good documentation available for [setting up a Socket.io server](https://socket.io/docs/). Standard WebSockets **will** work with a standard WebSocket server as well as the websocket.org echo test. You can use libraries like [ws](https://github.com/websockets/ws) for the server, with [native WebSockets in the browser](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). Either way, you can proxy WebSocket requests manually in `package.json`: ```js { // ... "proxy": { "/socket": { // Your compatible WebSocket server "target": "ws://", // Tell http-proxy-middleware that this is a WebSocket proxy. // Also allows you to proxy WebSocket requests without an additional HTTP request // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade "ws": true // ... } } // ... } ``` ## Using HTTPS in Development > Note: this feature is available with `react-scripts@0.4.0` and higher. You may require the dev server to serve pages over HTTPS. One particular case where this could be useful is when using [the "proxy" feature](#proxying-api-requests-in-development) to proxy requests to an API server when that API server is itself serving HTTPS. To do this, set the `HTTPS` environment variable to `true`, then start the dev server as usual with `npm start`: #### Windows (cmd.exe) ```cmd set HTTPS=true&&npm start ``` #### Windows (Powershell) ```Powershell ($env:HTTPS = $true) -and (npm start) ``` (Note: the lack of whitespace is intentional.) #### Linux, macOS (Bash) ```bash HTTPS=true npm start ``` Note that the server will use a self-signed certificate, so your web browser will almost definitely display a warning upon accessing the page. ## Generating Dynamic `` Tags on the Server Since Create React App doesn’t support server rendering, you might be wondering how to make `` tags dynamic and reflect the current URL. To solve this, we recommend to add placeholders into the HTML, like this: ```html ``` Then, on the server, regardless of the backend you use, you can read `index.html` into memory and replace `__OG_TITLE__`, `__OG_DESCRIPTION__`, and any other placeholders with values depending on the current URL. Just make sure to sanitize and escape the interpolated values so that they are safe to embed into HTML! If you use a Node server, you can even share the route matching logic between the client and the server. However duplicating it also works fine in simple cases. ## Pre-Rendering into Static HTML Files If you’re hosting your `build` with a static hosting provider you can use [react-snapshot](https://www.npmjs.com/package/react-snapshot) or [react-snap](https://github.com/stereobooster/react-snap) to generate HTML pages for each route, or relative link, in your application. These pages will then seamlessly become active, or “hydrated”, when the JavaScript bundle has loaded. There are also opportunities to use this outside of static hosting, to take the pressure off the server when generating and caching routes. The primary benefit of pre-rendering is that you get the core content of each page _with_ the HTML payload—regardless of whether or not your JavaScript bundle successfully downloads. It also increases the likelihood that each route of your application will be picked up by search engines. You can read more about [zero-configuration pre-rendering (also called snapshotting) here](https://medium.com/superhighfives/an-almost-static-stack-6df0a2791319). ## Injecting Data from the Server into the Page Similarly to the previous section, you can leave some placeholders in the HTML that inject global variables, for example: ```js ``` Then, on the server, you can replace `__SERVER_DATA__` with a JSON of real data right before sending the response. The client code can then read `window.SERVER_DATA` to use it. **Make sure to [sanitize the JSON before sending it to the client](https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0) as it makes your app vulnerable to XSS attacks.** ## Running Tests > Note: this feature is available with `react-scripts@0.3.0` and higher.
>[Read the migration guide to learn how to enable it in older projects!](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030) Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it years ago, give it another try. Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness. While Jest provides browser globals such as `window` thanks to [jsdom](https://github.com/tmpvar/jsdom), they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks. We recommend that you use a separate tool for browser end-to-end tests if you need them. They are beyond the scope of Create React App. ### Filename Conventions Jest will look for test files with any of the following popular naming conventions: - Files with `.js` suffix in `__tests__` folders. - Files with `.test.js` suffix. - Files with `.spec.js` suffix. The `.test.js` / `.spec.js` files (or the `__tests__` folders) can be located at any depth under the `src` top level folder. We recommend to put the test files (or `__tests__` folders) next to the code they are testing so that relative imports appear shorter. For example, if `App.test.js` and `App.js` are in the same folder, the test just needs to `import App from './App'` instead of a long relative path. Colocation also helps find tests more quickly in larger projects. ### Command Line Interface When you run `npm test`, Jest will launch in the watch mode. Every time you save a file, it will re-run the tests, just like `npm start` recompiles the code. The watcher includes an interactive command-line interface with the ability to run all tests, or focus on a search pattern. It is designed this way so that you can keep it open and enjoy fast re-runs. You can learn the commands from the “Watch Usage” note that the watcher prints after every run: ![Jest watch mode](http://facebook.github.io/jest/img/blog/15-watch.gif) ### Version Control Integration By default, when you run `npm test`, Jest will only run the tests related to files changed since the last commit. This is an optimization designed to make your tests run fast regardless of how many tests you have. However it assumes that you don’t often commit the code that doesn’t pass the tests. Jest will always explicitly mention that it only ran tests related to the files changed since the last commit. You can also press `a` in the watch mode to force Jest to run all tests. Jest will always run all tests on a [continuous integration](#continuous-integration) server or if the project is not inside a Git or Mercurial repository. ### Writing Tests To create tests, add `it()` (or `test()`) blocks with the name of the test and its code. You may optionally wrap them in `describe()` blocks for logical grouping but this is neither required nor recommended. Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this: ```js import sum from './sum'; it('sums numbers', () => { expect(sum(1, 2)).toEqual(3); expect(sum(2, 2)).toEqual(4); }); ``` All `expect()` matchers supported by Jest are [extensively documented here](https://facebook.github.io/jest/docs/en/expect.html#content).
You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](https://facebook.github.io/jest/docs/en/expect.html#tohavebeencalled) to create “spies” or mock functions. ### Testing Components There is a broad spectrum of component testing techniques. They range from a “smoke test” verifying that a component renders without throwing, to shallow rendering and testing some of the output, to full rendering and testing component lifecycle and state changes. Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components: ```js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(, div); }); ``` This test mounts a component and makes sure that it didn’t throw during rendering. Tests like this provide a lot of value with very little effort so they are great as a starting point, and this is the test you will find in `src/App.test.js`. When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior. If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). To install it, run: ```sh npm install --save enzyme enzyme-adapter-react-16 react-test-renderer ``` Alternatively you may use `yarn`: ```sh yarn add enzyme enzyme-adapter-react-16 react-test-renderer ``` As of Enzyme 3, you will need to install Enzyme along with an Adapter corresponding to the version of React you are using. (The examples above use the adapter for React 16.) The adapter will also need to be configured in your [global setup file](#initializing-test-environment): #### `src/setupTests.js` ```js import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() }); ``` > Note: Keep in mind that if you decide to "eject" before creating `src/setupTests.js`, the resulting `package.json` file won't contain any reference to it. [Read here](#initializing-test-environment) to learn how to add this after ejecting. Now you can write a smoke test with it: ```js import React from 'react'; import { shallow } from 'enzyme'; import App from './App'; it('renders without crashing', () => { shallow(); }); ``` Unlike the previous smoke test using `ReactDOM.render()`, this test only renders `` and doesn’t go deeper. For example, even if `` itself renders a `