Skip to content

Commit

Permalink
feat: Fetch adapter support for context provided via adapterOptions (
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonmit authored Jul 17, 2018
1 parent edc45d8 commit 82ebd09
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 12 deletions.
22 changes: 22 additions & 0 deletions docs/adapters/fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,25 @@ 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
}
}
});
```
24 changes: 18 additions & 6 deletions packages/@pollyjs/adapter-fetch/src/index.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
import Adapter from '@pollyjs/adapter';
import { Fetch as FetchUtils } from '@pollyjs/utils';

const nativeFetch = global.fetch;
const { defineProperty } = Object;
const STUB_META = Symbol();

export default class FetchAdapter extends Adapter {
static get name() {
return 'fetch';
}

get defaultOptions() {
return {
context: global
};
}

onConnect() {
this.assert('Fetch global not found.', nativeFetch);
const { context } = this.options;

this.assert('Fetch global not found.', !!(context && context.fetch));
this.assert(
'Running concurrent fetch adapters is unsupported, stop any running Polly instances.',
global.fetch === nativeFetch
!(STUB_META in context.fetch)
);

this.native = nativeFetch;
global.fetch = (url, options = {}) =>
this.native = context.fetch;

context.fetch = (url, options = {}) =>
this.handleRequest({
url,
method: options.method || 'GET',
headers: FetchUtils.serializeHeaders(options.headers),
body: options.body,
requestArguments: [url, options]
});

defineProperty(context.fetch, STUB_META, { value: true });
}

onDisconnect() {
global.fetch = nativeFetch;
this.options.context.fetch = this.native;
this.native = null;
}

async onRecord(pollyRequest) {
Expand Down
101 changes: 100 additions & 1 deletion packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { setupMocha as setupPolly } from '@pollyjs/core';
import { setupMocha as setupPolly, Polly } from '@pollyjs/core';
import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record';
import adapterTests from '@pollyjs-tests/integration/adapter-tests';
import adapterBrowserTests from '@pollyjs-tests/integration/adapter-browser-tests';
Expand All @@ -21,3 +21,102 @@ describe('Integration | Fetch Adapter', function() {
adapterTests();
adapterBrowserTests();
});

describe('Integration | Fetch Adapter | Context', function() {
it(`should assign context's fetch as the native fetch`, async function() {
const polly = new Polly('context', { adapters: [] });
const adapterOptions = {
fetch: {
context: {
fetch() {}
}
}
};

polly.configure({
adapters: [FetchAdapter],
adapterOptions
});

expect(polly.adapters.get('fetch').native).to.equal(
adapterOptions.fetch.context.fetch
);

expect(function() {
polly.configure({
adapterOptions: {
fetch: {
context: undefined
}
}
});
}).to.throw(`[Polly] [adapter:fetch] Fetch global not found.`);

await polly.stop();
});

it('should throw when context and fetch are undefined', async function() {
const polly = new Polly('context', { adapters: [] });

polly.configure({
adapters: [FetchAdapter]
});

expect(function() {
polly.configure({
adapterOptions: {
fetch: {
context: undefined
}
}
});
}).to.throw(`[Polly] [adapter:fetch] Fetch global not found.`);

expect(function() {
polly.configure({
adapterOptions: {
fetch: {
context: {
fetch: undefined
}
}
}
});
}).to.throw(`[Polly] [adapter:fetch] Fetch global not found.`);

await polly.stop();
});
});

describe('Integration | Fetch Adapter | Concurrency', function() {
it('should prevent concurrent fetch adapter instances', async function() {
const one = new Polly('one');
const two = new Polly('two');

one.connectTo(FetchAdapter);

expect(function() {
two.connectTo(FetchAdapter);
}).to.throw(
`[Polly] [adapter:fetch] Running concurrent fetch adapters is unsupported, stop any running Polly instances.`
);

await one.stop();
await two.stop();
});

it('should allow you to register new instances once stopped', async function() {
const one = new Polly('one');
const two = new Polly('two');

one.connectTo(FetchAdapter);
one.stop();

expect(function() {
two.connectTo(FetchAdapter);
}).to.not.throw();

await one.stop();
await two.stop();
});
});
8 changes: 3 additions & 5 deletions packages/@pollyjs/core/src/polly.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,11 @@ export default class Polly {
this.mode !== MODES.STOPPED
);

this.config = mergeOptions(DefaultConfig, this.config, config);

/* Handle Adapters */

// Disconnect from all current adapters
// Disconnect from all current adapters before updating the config
this.disconnect();

this.config = mergeOptions(DefaultConfig, this.config, config);

// Register and connect to all specified adapters
this.config.adapters.forEach(adapter => this.connectTo(adapter));

Expand Down

0 comments on commit 82ebd09

Please sign in to comment.