From 5c655bfcbae7ef4419afca12ed7618b277cad819 Mon Sep 17 00:00:00 2001 From: Offir Golan Date: Sat, 11 Jan 2020 22:46:41 -0800 Subject: [PATCH] fix(core): Disconnect from all adapters when `pause` is called (#291) BREAKING CHANGE: 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. --- docs/api.md | 41 ++++++++++++---- .../tests/integration/adapter-test.js | 2 + .../tests/integration/adapter-test.js | 10 +++- packages/@pollyjs/core/src/polly.js | 20 +++++--- .../@pollyjs/core/tests/unit/polly-test.js | 49 ++++++++++++++++--- tests/integration/adapter-polly-tests.js | 22 +++++++++ 6 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 tests/integration/adapter-polly-tests.js diff --git a/docs/api.md b/docs/api.md index dcae96e8..c367ba24 100644 --- a/docs/api.md +++ b/docs/api.md @@ -179,32 +179,55 @@ played back from a saved recording. 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 -Puts polly in a paused mode. All requests going forward will pass through -and will not be recorded or replayed. The previous mode will be saved and can -be restored by calling [play](api#play) +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 -// polly.mode === 'replay' +await fetch('/api/not-a-secret'); polly.pause(); -// polly.mode === 'passthrough' +// This and all subsequent requests will no longer be handled by polly +await fetch('/api/secret'); ``` ### play -Restores the mode to the one before [pause](api#pause) was called. +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 -// polly.mode === 'replay' +await fetch('/api/not-a-secret'); polly.pause(); -// polly.mode === 'passthrough' +// This and all subsequent requests will no longer be handled by polly +await fetch('/api/secret'); polly.play(); -// polly.mode === 'replay' +// This and all subsequent requests will again be handled by polly +await fetch('/api/not-a-secret'); ``` ### stop diff --git a/packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js b/packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js index 68bccfec..e7ad9887 100644 --- a/packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js +++ b/packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js @@ -2,6 +2,7 @@ import { Polly, setupMocha as setupPolly } from '@pollyjs/core'; import { URL } from '@pollyjs/utils'; import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record'; import adapterTests from '@pollyjs-tests/integration/adapter-tests'; +import adapterPollyTests from '@pollyjs-tests/integration/adapter-polly-tests'; import adapterBrowserTests from '@pollyjs-tests/integration/adapter-browser-tests'; import adapterIdentifierTests from '@pollyjs-tests/integration/adapter-identifier-tests'; @@ -19,6 +20,7 @@ describe('Integration | Fetch Adapter', function() { setupPolly.afterEach(); adapterTests(); + adapterPollyTests(); adapterBrowserTests(); adapterIdentifierTests(); diff --git a/packages/@pollyjs/adapter-puppeteer/tests/integration/adapter-test.js b/packages/@pollyjs/adapter-puppeteer/tests/integration/adapter-test.js index 0094baf4..20a109e7 100644 --- a/packages/@pollyjs/adapter-puppeteer/tests/integration/adapter-test.js +++ b/packages/@pollyjs/adapter-puppeteer/tests/integration/adapter-test.js @@ -34,7 +34,7 @@ describe('Integration | Puppeteer Adapter', function() { } }); - setupFetchRecord({ host: HOST }); + setupFetchRecord.beforeEach({ host: HOST }); beforeEach(function() { // Override this.fetch here since it needs access to the current context @@ -55,6 +55,14 @@ describe('Integration | Puppeteer Adapter', function() { }); }); + afterEach(async function() { + // Turn off request interception before setupFetchRecord's afterEach so it + // can correctly do it's thing + await this.page.setRequestInterception(false); + }); + + setupFetchRecord.afterEach(); + afterEach(async function() { await this.page.close(); }); diff --git a/packages/@pollyjs/core/src/polly.js b/packages/@pollyjs/core/src/polly.js index 788f4a77..b71d1ded 100644 --- a/packages/@pollyjs/core/src/polly.js +++ b/packages/@pollyjs/core/src/polly.js @@ -14,7 +14,7 @@ import { validateRecordingName } from './utils/validators'; const RECORDING_NAME = Symbol(); const RECORDING_ID = Symbol(); -const PAUSED_MODE = Symbol(); +const PAUSED_ADAPTERS = Symbol(); const FACTORY_REGISTRATION = new WeakMap(); const EVENT_EMITTER = new EventEmitter({ @@ -202,19 +202,27 @@ export default class Polly { * @public * @memberof Polly */ - pause() { - this[PAUSED_MODE] = this.mode; + passthrough() { this.mode = MODES.PASSTHROUGH; } + /** + * @public + * @memberof Polly + */ + pause() { + this[PAUSED_ADAPTERS] = [...this.adapters.keys()]; + this.disconnect(); + } + /** * @public * @memberof Polly */ play() { - if (this[PAUSED_MODE]) { - this.mode = this[PAUSED_MODE]; - delete this[PAUSED_MODE]; + if (this[PAUSED_ADAPTERS]) { + this[PAUSED_ADAPTERS].forEach(adapterName => this.connectTo(adapterName)); + delete this[PAUSED_ADAPTERS]; } } diff --git a/packages/@pollyjs/core/tests/unit/polly-test.js b/packages/@pollyjs/core/tests/unit/polly-test.js index 98a41187..aa0c84d2 100644 --- a/packages/@pollyjs/core/tests/unit/polly-test.js +++ b/packages/@pollyjs/core/tests/unit/polly-test.js @@ -239,6 +239,21 @@ describe('Unit | Polly', function() { describe('API', function() { setupPolly(); + class MockAdapterA extends Adapter { + static get name() { + return 'adapter-a'; + } + + onConnect() {} + onDisconnect() {} + } + + class MockAdapterB extends MockAdapterA { + static get name() { + return 'adapter-b'; + } + } + it('.record()', async function() { this.polly.mode = MODES.REPLAY; @@ -255,24 +270,44 @@ describe('Unit | Polly', function() { expect(this.polly.mode).to.equal(MODES.REPLAY); }); - it('.pause()', async function() { + it('.passthrough()', async function() { this.polly.mode = MODES.RECORD; expect(this.polly.mode).to.equal(MODES.RECORD); - this.polly.pause(); + this.polly.passthrough(); expect(this.polly.mode).to.equal(MODES.PASSTHROUGH); }); + it('.pause()', async function() { + this.polly.configure({ adapters: [MockAdapterA, MockAdapterB] }); + + expect([...this.polly.adapters.keys()]).to.deep.equal([ + 'adapter-a', + 'adapter-b' + ]); + this.polly.pause(); + expect([...this.polly.adapters.keys()]).to.deep.equal([]); + }); + it('.play()', async function() { - this.polly.mode = MODES.RECORD; + this.polly.configure({ adapters: [MockAdapterA, MockAdapterB] }); - expect(this.polly.mode).to.equal(MODES.RECORD); + expect([...this.polly.adapters.keys()]).to.deep.equal([ + 'adapter-a', + 'adapter-b' + ]); this.polly.play(); - expect(this.polly.mode).to.equal(MODES.RECORD); + expect([...this.polly.adapters.keys()]).to.deep.equal([ + 'adapter-a', + 'adapter-b' + ]); this.polly.pause(); - expect(this.polly.mode).to.equal(MODES.PASSTHROUGH); + expect([...this.polly.adapters.keys()]).to.deep.equal([]); this.polly.play(); - expect(this.polly.mode).to.equal(MODES.RECORD); + expect([...this.polly.adapters.keys()]).to.deep.equal([ + 'adapter-a', + 'adapter-b' + ]); }); it('.stop()', async function() { diff --git a/tests/integration/adapter-polly-tests.js b/tests/integration/adapter-polly-tests.js new file mode 100644 index 00000000..7804ff44 --- /dev/null +++ b/tests/integration/adapter-polly-tests.js @@ -0,0 +1,22 @@ +export default function pollyTests() { + it('should not handle any requests when paused', async function() { + const { server } = this.polly; + const requests = []; + + server.any().on('request', req => requests.push(req)); + + await this.fetchRecord(); + await this.fetchRecord(); + + this.polly.pause(); + await this.fetchRecord(); + await this.fetchRecord(); + + this.polly.play(); + await this.fetchRecord(); + + expect(requests.length).to.equal(3); + expect(this.polly._requests.length).to.equal(3); + expect(requests.map(r => r.order)).to.deep.equal([0, 1, 2]); + }); +}