Skip to content

Commit

Permalink
[Fix] makeOptions: ensure that config-level attachTo/hydrateIn
Browse files Browse the repository at this point in the history
…are inherited into wrapper options.

Fixes #1836.
  • Loading branch information
ljharb committed Oct 4, 2018
1 parent d91d95b commit e29321c
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 6 deletions.
103 changes: 103 additions & 0 deletions packages/enzyme-test-suite/test/Utils-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
spyMethod,
nodeHasType,
isCustomComponentElement,
makeOptions,
} from 'enzyme/build/Utils';
import getAdapter from 'enzyme/build/getAdapter';
import {
flatten,
mapNativeEventNames,
propFromEvent,
} from 'enzyme-adapter-utils';
import { get, reset, merge as configure } from 'enzyme/build/configuration';

import './_helpers/setupAdapters';

Expand Down Expand Up @@ -590,6 +592,107 @@ describe('Utils', () => {
});
});

describe('makeOptions', () => {
let originalConfig;
const adapter = getAdapter();
const initialConfig = {
adapter,
foo: 'bar',
};
const node = {};
const otherNode = {};

beforeEach(() => {
originalConfig = get();
reset(initialConfig);
});

afterEach(() => {
reset(originalConfig);
});

it('throws when passed attachTo and hydrateIn do not agree', () => {
expect(() => makeOptions({ attachTo: node, hydrateIn: otherNode })).to.throw(
TypeError,
'If both the `attachTo` and `hydrateIn` options are provided, they must be === (for backwards compatibility)',
);
});

it('throws when config attachTo and hydrateIn do not agree', () => {
configure({ attachTo: node, hydrateIn: otherNode });
expect(() => makeOptions({})).to.throw(
TypeError,
'If both the `attachTo` and `hydrateIn` options are provided, they must be === (for backwards compatibility)',
);
});

it('returns an object that includes the config', () => {
expect(makeOptions({ bar: 'baz' })).to.eql({
...initialConfig,
bar: 'baz',
});
});

it('sets attachTo and hydrateIn to hydrateIn, when attachTo is missing', () => {
expect(makeOptions({ hydrateIn: node })).to.eql({
...initialConfig,
hydrateIn: node,
attachTo: node,
});
});

it('sets attachTo and hydrateIn to hydrateIn, when attachTo === hydrateIn', () => {
expect(makeOptions({ hydrateIn: node, attachTo: node })).to.eql({
...initialConfig,
hydrateIn: node,
attachTo: node,
});
});

it('only sets attachTo, when hydrateIn is missing', () => {
expect(makeOptions({ attachTo: node })).to.eql({
...initialConfig,
attachTo: node,
});
});

describe('when DOM node options are set in the config', () => {
it('inherits attachTo', () => {
reset({ ...initialConfig, attachTo: node });
expect(makeOptions({})).to.eql({
...initialConfig,
attachTo: node,
});
});

it('inherits hydrateIn', () => {
reset({ ...initialConfig, hydrateIn: node });
expect(makeOptions({})).to.eql({
...initialConfig,
attachTo: node,
hydrateIn: node,
});
});

it('allows overriding of attachTo', () => {
reset({ ...initialConfig, attachTo: node });
expect(makeOptions({ attachTo: otherNode })).to.eql({
...initialConfig,
attachTo: otherNode,
});
});

it('allows overriding of hydrateIn', () => {
reset({ ...initialConfig, hydrateIn: node });
expect(makeOptions({ hydrateIn: otherNode })).to.eql({
...initialConfig,
attachTo: otherNode,
hydrateIn: otherNode,
});
});
});
});

describe('spyMethod', () => {
it('can spy last return value and restore it', () => {
class Counter {
Expand Down
20 changes: 14 additions & 6 deletions packages/enzyme/src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,32 @@ export function getAdapter(options = {}) {
return realGetAdapter(options);
}

export function makeOptions(options) {
const { attachTo, hydrateIn } = options;

function validateMountOptions(attachTo, hydrateIn) {
if (attachTo && hydrateIn && attachTo !== hydrateIn) {
throw new TypeError('If both the `attachTo` and `hydrateIn` options are provided, they must be === (for backwards compatibility)');
}
}

export function makeOptions(options) {
const { attachTo: configAttachTo, hydrateIn: configHydrateIn, ...config } = get();
validateMountOptions(configAttachTo, configHydrateIn);

const { attachTo, hydrateIn } = options;
validateMountOptions(attachTo, hydrateIn);

// neither present: both undefined
// only attachTo present: attachTo set, hydrateIn undefined
// only hydrateIn present: both set to hydrateIn
// both present (and ===, per above): both set to hydrateIn
const finalAttachTo = hydrateIn || configHydrateIn || configAttachTo || attachTo || undefined;
const finalHydrateIn = hydrateIn || configHydrateIn || undefined;
const mountTargets = {
attachTo: hydrateIn || attachTo || undefined,
hydrateIn: hydrateIn || undefined,
...(finalAttachTo && { attachTo: finalAttachTo }),
...(finalHydrateIn && { hydrateIn: finalHydrateIn }),
};

return {
...get(),
...config,
...options,
...mountTargets,
};
Expand Down

0 comments on commit e29321c

Please sign in to comment.