<|start_filename|>tests/feature/content.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin, utils } from '../../src/index';
import App from '../../examples/content/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Content Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree, clock;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
    clock = new sinon.useFakeTimers();
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
    clock.restore();
  });

  describe('default content', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target default' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === "This is a default content.\nIt's a second line.", 'should be default content');
    });
  });

  describe('custom content', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target custom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a custom content.\n', 'should be custom content');
    });

    it('should be resized', () => {
      // Mouseover on custom
      const custom = firstComponent(tree, Origin.WrappedComponent, { className: 'target custom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(custom);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const customPos = position(tooltip);
      TestUtils.Simulate.mouseLeave(custom);

      // Mouseover on default
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target default' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const defaultPos = position(tooltip);
      TestUtils.Simulate.mouseLeave(origin);
 
      assert(customPos.height < defaultPos.height, "default's height is higher than custom's height");
    });
  });

  describe('HTML or DOM content', () => {
    it('should be worked if string HTML is passed', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target html' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a html content.\nSanitized by DOMPurify.\n');
      assert(tooltip.innerHTML.indexOf('</script>') === -1);
      assert(tooltip.innerHTML.indexOf('</a>') !== -1);
      assert(tooltip.innerHTML.indexOf('</b>') !== -1);
    });

    it('should be worked if DOM element is passed', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target dom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'RedGreenBlue\n');
      assert(tooltip.innerHTML.indexOf('</span>') !== -1);
    });
  });

  describe('continuous updating content', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target time' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      let first = tooltip.innerText;

      clock.tick(1100);

      let second = tooltip.innerText;
      assert(first !== second, 'should be updated');

      clock.tick(1100);

      first = second;
      second = tooltip.innerText;
      assert(first !== second, 'should be updated');
    });

    it('should re-calculate a position of the tooltip', () => {
      // Mouseover to 'time' origin
      const time = firstComponent(tree, Origin.WrappedComponent, { className: 'target time' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(time);

      clock.tick(500);

      // Mouseover to 'custom' origin
      const custom = firstComponent(tree, Origin.WrappedComponent, { className: 'target custom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(custom);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const oriPos = position(custom);
      const tipPos = position(tooltip);
      const center = tipPos.left + tipPos.width / 2;
      assert(oriPos.left < center && center < oriPos.right, 'tooltip should be located a center of the origin');
    });
  });

  describe('continuous updating children', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target count' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'count' }).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Count down from 10\n');

      clock.tick(1100);
      assert(tooltip.innerText === 'Count down from 10, 9\n');

      clock.tick(5000);
      assert(tooltip.innerText === 'Count down from 10, 9, 8, 7, 6, 5, 4\n');

      clock.tick(4000);
      assert(tooltip.innerText === 'Count down from 10\n');

      clock.tick(2000);
      assert(tooltip.innerText === 'Count down from 10, 9, 8\n');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');
    });

    it('should re-calculate a position of the tooltip', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target count' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'count' }).refs.tooltip;
      const tipPos1 = position(tooltip);
      const oriPos = position(origin);

      assert(tipPos1.bottom < oriPos.top, 'tooltip should be located upon the origin');

      clock.tick(7000);
      const tipPos2 = position(tooltip);

      assert(oriPos.right < tipPos2.left, 'tooltip should be moved a right of the origin');
    });
  });
});


<|start_filename|>src/utils.js<|end_filename|>
import isDOM from 'is-dom';
import * as actions from './actions';

function dimension(el) {
  const rect = el.getBoundingClientRect();
  return { width: rect.width, height: rect.height };
}

/**
 * Returns a position of given DOM element.
 *
 * @param {Object} el - DOM element.
 * @return {Object}
 */
export function position(el) {
  const pos = el.getBoundingClientRect();
  const { pageYOffset, pageXOffset } = window;
  const { scrollTop, clientTop, scrollLeft, clientLeft } = document.documentElement;
  const winTop = (pageYOffset || scrollTop) - clientTop;
  const winLeft = (pageXOffset || scrollLeft) - clientLeft;

  return {
    top: pos.top + winTop,
    left: pos.left + winLeft,
    right: pos.right + winLeft,
    bottom: pos.bottom + winTop,
    width: pos.width,
    height: pos.height,
  };
}

/**
 * Calculates a position of the tooltip.
 *
 * @param {string} place - 'top', 'right', 'bottom', or 'left'.
 * @param {Object} content - DOM element that contains a content.
 * @param {Object} origin - DOM element, element id or position object.
 * @return {Object} contains 'top', 'left', and extra keys.
 */
export function placement(place, content, origin) {
  const gap = 12;
  const dim = dimension(content);
  let pos = { top: origin.y, right: origin.x, bottom: origin.y, left: origin.x, width: 0, height: 0 }

  if (isDOM(origin)) {
    pos = position(origin)
  } else if (typeof origin === 'string') {
    pos = position(document.getElementById(origin))
  }

  let offset = { width: dim.width, height: dim.height };
  switch(place) {
  case 'top': case 'bottom':
    offset.left = `${pos.left + (pos.width * 0.5) - (dim.width * 0.5)}px`;
    break;
  case 'left': case 'right':
    offset.top = `${pos.top + (pos.height * 0.5) - (dim.height * 0.5)}px`;
    break;
  }

  switch(place) {
  case 'top':
    offset.top = `${pos.top - dim.height - gap}px`;
    break;
  case 'right':
    offset.left = `${pos.right + gap}px`;
    break;
  case 'bottom':
    offset.top = `${pos.top + pos.height + gap}px`;
    break;
  case 'left':
    offset.left = `${pos.left - dim.width - gap}px`;
    break;
  }

  return offset;
}

/**
 * Completes missing directions in given direction(s).
 *
 * @param {string|Array} dir - a direction or a list of directions.
 * @return {Array}
 */
const DIRS = ['top', 'right', 'bottom', 'left'];
export function complete(dir) {
  if (typeof dir === 'string') {
    dir = [dir];
  } else {
    dir = [...dir];
  }

  const missings = DIRS.filter(d => dir.indexOf(d) === -1);
  return dir.concat(missings);
}

/**
 * Calculates an intersection of two areas.
 *
 * @param {Object} area1
 * @param {Object} area2
 * @return {Object} an intersection.
 */
export function intersection(area1, area2) {
  let area = {};
  area.top = Math.max(area1.top, area2.top);
  area.right = Math.min(area1.left + area1.width, area2.left + area2.width);
  area.bottom = Math.min(area1.top + area1.height, area2.top + area2.height);
  area.left = Math.max(area1.left, area2.left);
  area.height = area.bottom - area.top;
  area.width = area.right - area.left;
  return area;
}

function scrollOffset() {
  const list = [document.documentElement, document.body.parentNode, document.body];
  return {
    top: list.map(el => el.scrollTop).reduce((p, v) => p || v),
    left: list.map(el => el.scrollLeft).reduce((p, v) => p || v),
  };
}

// Returns current content area
function contentArea() {
  return {
    ...scrollOffset(),
    height: window.innerHeight,
    width: window.innerWidth,
  };
}

// Strip unit string from property values and convert to float
const STRIP_FOR = ['top', 'left', 'right', 'bottom', 'width', 'height'];
export function strip(obj) {
  const data = { ...obj };
  STRIP_FOR.forEach(prop => {
    if (typeof data[prop] === 'string') {
      data[prop] = parseFloat(data[prop].replace('px', ''));
    }
  });
  return data;
}

// Make full area data from minimum data
export function amend(area) {
  const data = strip(area);
  if (typeof data.top !== 'number') {
    data.top = 0;
  }
  if (typeof data.left !== 'number') {
    data.left = 0;
  }
  if (typeof data.right !== 'number' && typeof data.width === 'number') {
    data.right = data.left + data.width;
  }
  if (typeof data.bottom !== 'number' && typeof data.height === 'number') {
    data.bottom = data.top + data.height;
  }
  return data;
}

// Returns directions which are not in target rectangle
export function overDirs(tip, el) {
  tip = amend(tip);
  let area = amend(contentArea());
  if (el && typeof el === 'object') {
    area = intersection(area, position(el));
  }

  const dirs = [];
  if (tip.top < area.top) {
    dirs.push('top');
  }
  if (area.right < tip.right) {
    dirs.push('right');
  }
  if (area.bottom < tip.bottom) {
    dirs.push('bottom');
  }
  if (tip.left < area.left) {
    dirs.push('left');
  }

  return dirs;
}

/**
 * Places and adjusts a tooltip.
 *
 * @param {Object} content - DOM element which contans a content.
 * @param {Object} props - props set from Tooltip component.
 * @return {Object} 'offset': style data to locate, 'place': final direction of the tooltip
 */
export function adjust(content, props) {
  const { auto, within } = props;
  const origin = originOrEl(props);
  let { place } = props;
  if (typeof place === 'string') {
    place = place.split(',').map(p => p.trim());
  }
  if (auto) {
    place = complete(place);
  }

  let pos, dirs, current, first;
  const tries = [ ...place ];
  while (0 < tries.length) {
    current = tries.shift();
    pos = placement(current, content, origin);
    if (typeof first === 'undefined') {
      first = { offset: pos, place: current };
    }
    dirs = overDirs(pos, within && within());
    if (dirs.length === 0) {
      return { offset: pos, place: current };
    }
  }
  return first;
}

/**
 * Resolves names of target tooltip from action or props.
 *
 * @param {Object} action or props.
 * @return {Array} a list of tooltip names.
 */
export function resolve(obj) {
  let names;
  if (obj && obj.payload && obj.payload.name) {
    names = obj.payload.name;
  } else if (obj && obj.name) {
    names = obj.name;
  } else {
    names = ['default'];
  }

  if (typeof names === 'string') {
    names = [names];
  }

  return names;
}

const actionTypes = Object.keys(actions).map(k => actions[k]).filter(e => typeof e === 'string');
export function deprecatedWarning(action) {
  const { type, payload } = action;
  if (actionTypes.indexOf(action.type) === -1) {
    return; // Ignore non-related actions
  }
  if (payload && payload.el) {
    console.warn(`DEPRECATED: Use 'origin' instead of 'el' in props for Tooltip component or 'show' action.`);
  }
  if (payload && payload.el && payload.origin) {
    console.warn(`Do not pass both 'origin' and 'el' props at the same time.`);
  }
}

export function originOrEl(props) {
  return props.origin || props.el;
}


<|start_filename|>tests/feature/place.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin, utils } from '../../src/index';
import App from '../../examples/place/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Place Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe("using origin's prop", () => {
    it('should be worked', () => {
      // Mouseover to right
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const right = firstComponent(tree, Origin.WrappedComponent, { place: 'right' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(right);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const rightOri = position(right);
      const rightTip = position(tooltip);
      assert(tooltip.innerText === 'This is a shared tooltip.');
      assert(rightOri.left < rightTip.left);

      // Mouseover to bottom
      const bottom = firstComponent(tree, Origin.WrappedComponent, { place: 'bottom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(bottom);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const bottomOri = position(bottom);
      const bottomTip = position(tooltip);
      assert(tooltip.innerText === 'This is a shared tooltip.');
      assert(bottomOri.top < bottomTip.top);

      // Mouseover to left
      const left = firstComponent(tree, Origin.WrappedComponent, { place: 'left' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(left);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const leftOri = position(left);
      const leftTip = position(tooltip);
      assert(tooltip.innerText === 'This is a shared tooltip.');
      assert(leftTip.left < leftOri.left);

      // Mouseover to top
      const top = firstComponent(tree, Origin.WrappedComponent, { place: 'top' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(top);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const topOri = position(top);
      const topTip = position(tooltip);
      assert(tooltip.innerText === 'This is a shared tooltip.');
      assert(topTip.top < topOri.top);
    });
  });

  describe("using tooltip's prop", () => {
    it('should be worked', () => {
      // Mouseover to right
      const rightTooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'right' }).refs.tooltip;
      const right = firstComponent(tree, Origin.WrappedComponent, { name: 'right' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(right);
      assert(getStyleValue(rightTooltip, 'visibility') === 'visible');

      const rightOri = position(right);
      const rightTip = position(rightTooltip);
      assert(rightTooltip.innerText === 'This is a right tooltip.');
      assert(rightOri.left < rightTip.left);

      // Mouseover to bottom
      const bottomTooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'bottom' }).refs.tooltip;
      const bottom = firstComponent(tree, Origin.WrappedComponent, { name: 'bottom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(bottom);
      assert(getStyleValue(bottomTooltip, 'visibility') === 'visible');

      const bottomOri = position(bottom);
      const bottomTip = position(bottomTooltip);
      assert(bottomTooltip.innerText === 'This is a bottom tooltip.');
      assert(bottomOri.top < bottomTip.top);

      // Mouseover to left
      const leftTooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'left' }).refs.tooltip;
      const left = firstComponent(tree, Origin.WrappedComponent, { name: 'left' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(left);
      assert(getStyleValue(leftTooltip, 'visibility') === 'visible');

      const leftOri = position(left);
      const leftTip = position(leftTooltip);
      assert(leftTooltip.innerText === 'This is a left tooltip.');
      assert(leftTip.left < leftOri.left);

      // Mouseover to top
      const topTooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'top' }).refs.tooltip;
      const top = firstComponent(tree, Origin.WrappedComponent, { name: 'top' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(top);
      assert(getStyleValue(topTooltip, 'visibility') === 'visible');

      const topOri = position(top);
      const topTip = position(topTooltip);
      assert(topTooltip.innerText === 'This is a top tooltip.');
      assert(topTip.top < topOri.top);
    });
  });

  describe('auto placement', () => {
    it('should be worked for basic usage', () => {
      // Mouseover
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-basic' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a shared tooltip.');

      const tipPos = position(tooltip);
      const oriPos = position(origin);
      assert(oriPos.right < tipPos.left, 'tooltip should be located right of the origin');
    });

    it('should be worked for custom fallback', () => {
      // Mouseover to array
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const array = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-array' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(array);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a shared tooltip.');

      const arrayTipPos = position(tooltip);
      const arrayOriPos = position(array);
      assert(arrayTipPos.bottom < arrayOriPos.top, 'tooltip should be located on top of the origin');

      // Mouseover to string
      const string = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-string' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(string);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const stringTipPos = position(tooltip);
      const stringOriPos = position(string);
      assert(stringOriPos.bottom < stringTipPos.top, 'tooltip should be located on bottom of the origin');
    });

    it('should be worked for more fallbacks', () => {
      // Mouseover to more
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'more' }).refs.tooltip;
      const more = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-more' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(more);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a more tooltip.');

      const tipPos = position(tooltip);
      const oriPos = position(more);
      assert(oriPos.right < tipPos.left, 'tooltip should be located right of the origin');
    });

    it('should be worked within specified element', () => {
      // Mouseover to top
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'restricted' }).refs.tooltip;
      const top = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-top' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(top);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a restricted tooltip.');

      const topTipPos = position(tooltip);
      const topOriPos = position(top);
      assert(topOriPos.right < topTipPos.left, 'tooltip should be located on right of the origin');

      // Mouseover to right
      const right = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-right' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(right);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const rightTipPos = position(tooltip);
      const rightOriPos = position(right);
      assert(rightTipPos.right < rightOriPos.left, 'tooltip should be located left of the origin');

      // Mouseover to bottom
      const bottom = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-bottom' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(bottom);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const bottomTipPos = position(tooltip);
      const bottomOriPos = position(bottom);
      assert(bottomOriPos.right < bottomTipPos.left, 'tooltip should be located on right of the origin');
    });

    it("should be disabled by auto={false}", () => {
      // Mouseover to disabled
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'disabled' }).refs.tooltip;
      const disabled = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-disabled' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(disabled);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'This is a disabled tooltip.');

      const tipPos = position(tooltip);
      const oriPos = position(disabled);
      assert(tipPos.right < oriPos.left, 'tooltip should be located on left of the origin');
    });
  });
});


<|start_filename|>tests/unit/test_reducer.js<|end_filename|>
import assert from 'assert';
import sinon from 'sinon';
import reducer from '../../src/reducer';
import * as actions from '../../src/actions';

describe('reducer', () => {
  let warn;
  beforeEach(() => {
    warn = sinon.spy(console, 'warn');
  });

  afterEach(() => {
    console.warn.restore();
  });

  it("warns with 'el' prop", () => {
    reducer(undefined, actions.show({ el: 'abc' }));
    assert(warn.calledOnce);
    assert(warn.firstCall.args[0] === "DEPRECATED: Use 'origin' instead of 'el' in props for Tooltip component or 'show' action.");
  });

  it("doesn't warn with 'origin' prop", () => {
    reducer(undefined, actions.show({ origin: 'abc' }));
    assert(warn.callCount === 0);
  });

  it("ignores non-related actions which contains 'el' prop in payload", () => {
    reducer(undefined, { type: 'NON_RELATED_ACTION', payload: { el: 'abc' } });
    assert(warn.callCount === 0);
  });
});


<|start_filename|>tests/helpers.js<|end_filename|>
import TestUtils from 'react-addons-test-utils';
import { CSSStyleDeclaration } from 'cssstyle';
import equal from 'deep-equal';

export function scryComponents(tree, cls, props = {}) {
  const components = TestUtils.scryRenderedComponentsWithType(tree, cls);
  return components.filter(comp => {
    return Object.keys(props).map(key => {
      if (typeof props[key] === 'undefined') {
        return !!comp.props[key];
      } else {
        return equal(comp.props[key], props[key]);
      }
    }).reduce((prev, val) => prev && val, true);
  });
}

export function firstComponent(tree, cls, props = {}) {
  const all = scryComponents(tree, cls, props);
  if (0 < all.length) {
    return all[0];
  }
  throw new Error(`No matched components: ${cls.displayName} with ${JSON.stringify(props)}`);
}

const parser = new CSSStyleDeclaration();
export function getStyleValue(element, propName) {
  parser.cssText = element.getAttribute('style');
  return parser.getPropertyValue(propName);
}

export function getComputedStyleValue(element, propName) {
  const style = getComputedStyle(element);
  return style.getPropertyValue(propName);
}


<|start_filename|>src/reducer.js<|end_filename|>
import { resolve, deprecatedWarning } from './utils';

import {
  SHOW, HIDE, TOGGLE, KEEP,
  CONTENT, PLACE,
  START_TIMEOUT, END_TIMEOUT
} from './actions';

const initial = {
  show: false,
  place: 'top',
  origin: null,
  el: null,
  auto: true,
  content: null,
  timeout: null,
};

const SHOW_PROPS = ['origin', 'el', 'place', 'content'];

const handlers = {
  [SHOW]: function (state, action) {
    const props = {};
    SHOW_PROPS.forEach(name => {
      if (action.payload[name]) {
        props[name] = action.payload[name];
      } else {
        props[name] = initial[name];
      }
    });
    return { ...state, show: true, ...props };
  },
  [HIDE]: function (state) {
    return { ...state, show: false };
  },
  [TOGGLE]: function (state) {
    return { ...state, show: !state.show };
  },
  [KEEP]: function (state) {
    return { ...state, timeout: null };
  },
  [CONTENT]: function (state, action) {
    return { ...state, content: action.payload };
  },
  [PLACE]: function (state, action) {
    return { ...state, place: action.payload };
  },
  [START_TIMEOUT]: function (state, action) {
    return { ...state, timeout: action.payload.token };
  },
  [END_TIMEOUT]: function (state) {
    return { ...state, timeout: null };
  },
};

function tooltip(state = initial, action) {
  // Check usage of deprecated props
  deprecatedWarning(action);

  const handler = handlers[action.type];
  return handler ? handler(state, action) : state;
}

export default function reducer(state = {}, action) {
  let newState = { ...state };
  const names = resolve(action);
  names.forEach(name => {
    newState = { ...newState, [name]: tooltip(newState[name], action) };
  });
  return newState;
}


<|start_filename|>tests/feature/origin.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, utils } from '../../src/index';
import App from '../../examples/origin/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Origin Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe('custom origin', () => {
    it('should be worked', () => {
      // Mouseover on red
      const red = TestUtils.findRenderedDOMComponentWithClass(tree, 'red');
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      TestUtils.Simulate.mouseEnter(red);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Red\n');
      const redPos = position(tooltip);
      TestUtils.Simulate.mouseLeave(red);

      // Mouseover on green
      const green = TestUtils.findRenderedDOMComponentWithClass(tree, 'green');
      TestUtils.Simulate.mouseEnter(green);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Green\n');
      const greenPos = position(tooltip);
      TestUtils.Simulate.mouseLeave(green);

      assert(redPos.left < greenPos.left);
      assert(redPos.top === greenPos.top);

      // Mouseover on blue
      const blue = TestUtils.findRenderedDOMComponentWithClass(tree, 'blue');
      TestUtils.Simulate.mouseEnter(blue);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Blue\n');
      const bluePos = position(tooltip);
      TestUtils.Simulate.mouseLeave(blue);

      assert(greenPos.left < bluePos.left);
      assert(redPos.top === bluePos.top);

      // Mouseover on inner
      const inner = TestUtils.findRenderedDOMComponentWithClass(tree, 'inner');
      TestUtils.Simulate.mouseEnter(inner);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Inner\n');
      const innerPos = position(tooltip);
      TestUtils.Simulate.mouseLeave(inner);

      assert(bluePos.top < innerPos.top);
      assert(Math.round(bluePos.left + bluePos.width * 0.5) === Math.round(innerPos.left + innerPos.width * 0.5));
    });
  });

  describe('moving tooltip', () => {
    it('should be worked', () => {
      // Mousemove
      const moving = document.querySelector('div.moving');
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      const boxPos = position(moving);
      const cursor = { x: (boxPos.left + boxPos.width * 0.5), y: (boxPos.top + boxPos.height * 0.5) };
      TestUtils.Simulate.mouseMove(moving, { clientX: cursor.x, clientY: cursor.y });
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === 'Moving Tooltip!\n');

      const tipPos1 = position(tooltip);
      assert(boxPos.left < tipPos1.left);
      assert(boxPos.top < tipPos1.top);
      assert(tipPos1.width < boxPos.width);
      assert(tipPos1.height < boxPos.height);

      assert(tipPos1.top < cursor.y);
      assert(tipPos1.left < cursor.x);

      // Move more
      TestUtils.Simulate.mouseMove(moving, { clientX: cursor.x - 10, clientY: cursor.y - 10 });
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const tipPos2 = position(tooltip);
      assert(tipPos1.left - tipPos2.left === 10);
      assert(tipPos1.top - tipPos2.top === 10);
    });
  });
});


<|start_filename|>karma.conf.js<|end_filename|>
module.exports = function(config) {
  var configuration = {
    basePath: '',
    frameworks: ['mocha', 'sinon'],
    files: [
      'tests/feature/*.js',
      'examples/*.css'
    ],
    exclude: [],
    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-mocha-reporter',
      'karma-sinon',
      'karma-webpack',
      'karma-html2js-preprocessor'
    ],
    preprocessors: {
      'tests/feature/*.js': ['webpack'],
      'examples/*.css': ['html2js']
    },
    webpack: {
      devtool: 'inline-source-map',
      module: {
        loaders: [
          {
            test: /\.js$/,
            loader: 'babel',
            exclude: /node_modules/,
            query: {
              presets: ['react', 'es2015', 'stage-2', 'power-assert']
            }
          },
          {
            test: /\.json$/,
            loader: 'json'
          }
        ]
      },
      node: {
        fs: 'empty'
      }
    },
    reporters: ['mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    phantomjsLauncher: {
      options: {
        viewportSize: {
          width: 800,
          height: 600
        }
      }
    },
    singleRun: false,
    concurrency: Infinity,
  };

  config.set(configuration);
}


<|start_filename|>tests/unit/test_actions.js<|end_filename|>
import assert from 'assert';
import * as actions from '../../src/actions';

describe('actions', () => {
  describe('.delay', () => {
    context('with action contains no meta data', () => {
      const action = {
        type: 'WOO',
        payload: {
          value: 123
        },
      };

      it('appends meta data for delay feature', () => {
        assert.deepStrictEqual(actions.delay(action), {
          type: 'WOO',
          payload: {
            value: 123
          },
          meta: {
            'redux-tooltip/DELAY': {
              duration: 1500,
              callback: undefined,
            },
          }
        });
      });

      it('overwrites default duration', () => {
        assert.deepStrictEqual(actions.delay(action, { duration: 500 }), {
          type: 'WOO',
          payload: {
            value: 123
          },
          meta: {
            'redux-tooltip/DELAY': {
              duration: 500,
              callback: undefined,
            },
          }
        });
      });

      it('overwrites default duration', () => {
        const fn = () => {};
        assert.deepStrictEqual(actions.delay(action, { callback: fn }), {
          type: 'WOO',
          payload: {
            value: 123
          },
          meta: {
            'redux-tooltip/DELAY': {
              duration: 1500,
              callback: fn,
            },
          }
        });
      });
    });

    context('with action contains other meta data', () => {
      const action = {
        type: 'RUBY',
        payload: {
          on: 'rails',
        },
        meta: {
          gem: 'bundler',
        }
      };

      it('does not modify other meta data', () => {
        assert.deepStrictEqual(actions.delay(action), {
          type: 'WOO',
          payload: {
            value: 123
          },
          type: 'RUBY',
          payload: {
            on: 'rails',
          },
          meta: {
            gem: 'bundler',
            'redux-tooltip/DELAY': {
              duration: 1500,
              callback: undefined,
            },
          }
        });
      });
    });
  });

  describe('.prefix', () => {
    it('returns a prefixed action type', () => {
      const type = actions.prefix('HOGE');
      assert(type === 'redux-tooltip/HOGE');
    });
  });

  describe('.createAction', () => {
    it('creates an action creator', () => {
      const creator = actions.createAction('HOGE');
      assert(typeof creator === 'function');
      assert.deepStrictEqual(creator(123), { type: 'HOGE', payload: 123 });
      assert.deepStrictEqual(creator({ nyan: 'cat' }), { type: 'HOGE', payload: { nyan: 'cat' } });
    });
  });
});


<|start_filename|>examples/delay/app.js<|end_filename|>
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { Tooltip, Origin, actions } from '../../src/index';

const { show, hide, delay } = actions;

class App extends Component {
  handleTimeout(type, duration) {
    this.props.dispatch(show({ origin: this.refs.bold }));
  }

  render() {
    return (
      <div>
        <h1>Delay Example</h1>

        <h2>Basic usage</h2>
        <p>
          After leaving from <Origin delay className="target">an origin element</Origin>, a tooltip will stay a while.<br />
          The default duration is 1.5 seconds.
        </p>

        <h2>Advanced usage</h2>
        <p>
          You can specify a duration to delay hiding of a tooltip.<br />
          For example: <Origin delay={500} className="target">0.5 second</Origin>, <Origin delay="1000" className="target">1 second</Origin>, <Origin delay={2000.0} className="target">2 seconds</Origin>, <Origin delay="3000.0" className="target">3 seconds</Origin>
        </p>

        <h2>Delay on show</h2>
        <p>
          Hovering on an <Origin delay delayOn="show" className="target">origin</Origin>, but it is'n shown immediately.<br />
          You need to stay a while on it. Delay on <Origin delay delayOn="both" className="target">both</Origin>.
        </p>

        <h2>Timeout callback</h2>
        <p>
          The callback function is <b ref="bold">called</b> when <Origin delay className="target" onTimeout={this.handleTimeout.bind(this)}>timeout</Origin>.
        </p>

        <Tooltip>
          This is a <b>delay</b> tooltip.
        </Tooltip>
      </div>
    );
  }
}

function select(state) {
  const { app } = state;
  return { app };
}

export default connect(select)(App);


<|start_filename|>examples/style.css<|end_filename|>
body {
  color: #333;
  font-family: "Helvetica Neue", Arial, sans-serif;
  font-weight: 200;
}

.target {
  text-decoration: underline;
  cursor: pointer;
}

#my-id {
  background: #FFCDD2 !important;
}

#my-id-arrow > span {
  border-color: #FFCDD2 transparent transparent transparent !important;
}

.my-class {
  background: #BBDEFB !important;
}

.my-class .my-class-arrow > span {
  border-color: #BBDEFB transparent transparent transparent !important;
}


<|start_filename|>tests/feature/delay.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin, utils } from '../../src/index';
import App from '../../examples/delay/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Delay Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree, clock;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));

    // Setup fake timer
    clock = new sinon.useFakeTimers();
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));

    // Take back real timer
    clock.restore();
  });

  describe('basic usage', () => {
    it('should be worked for default duration', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delay: true }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be kept');

      // A bit later
      clock.tick(500);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Wait more
      clock.tick(1000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');
    });
  });

  describe('advanced usage', () => {
    it('should be worked for 0.5 second as an integer', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delay: 500 }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be kept');

      // 0.5 second later
      clock.tick(500);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden after 0.5 second');
    });

    it('should be worked for 1 second as a string', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delay: '1000' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be kept');

      // 1 second later
      clock.tick(1000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden after 1 second');
    });

    it('should be worked for 2 seconds as a float', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delay: 2000.0 }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be kept');

      // 2 seconds later
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden after 2 seconds');
    });

    it('should be worked for 3 seconds as a string of float', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delay: '3000.0' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be kept');

      // 3 seconds later
      clock.tick(3000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden after 3 seconds');
    });
  });

  describe('delay on show', () => {
    it('should be worked on show', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delayOn: 'show' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');

      // Mouseout 0.5 second later
      clock.tick(500);
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');

      // 2 second later
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip is still hidden');

      // Mouseover again
      TestUtils.Simulate.mouseEnter(origin);

      // 0.5 second later
      clock.tick(500);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip is still hidden');

      // Wait more 1 second 
      clock.tick(1000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden immediately');
    });

    it('should be worked on both', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { delayOn: 'both' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');

      // 0.5 second later
      clock.tick(500);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');

      // Mouseout before showing
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');

      // Wait 2 seconds
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip is still hidden');

      // Mouseover again
      TestUtils.Simulate.mouseEnter(origin);

      // 1.5 seconds later
      clock.tick(1500);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);

      // 0.5 second later
      clock.tick(500);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip is still shown');

      // Mouseover again
      TestUtils.Simulate.mouseEnter(origin);

      // Wait 2 seconds
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip is still shown');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);

      // 1.5 seconds later
      clock.tick(1500);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');
    });
  });

  describe('timeout callback', () => {
    it('should be worked', () => {
      // Mouseover and mouseout
      const origin = firstComponent(tree, Origin.WrappedComponent, { onTimeout: undefined }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);
      TestUtils.Simulate.mouseLeave(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const pos1 = position(tooltip);

      // 2 seconds later
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const pos2 = position(tooltip);
      assert(pos2.left < pos1.left);

      // 10 seconds later
      clock.tick(10000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
    });
  });
});


<|start_filename|>src/actions.js<|end_filename|>
export function prefix(name) {
  return `redux-tooltip/${name}`;
}

// NOTE: No compatibility with redux-actions.
export function createAction(type) {
  return payload => {
    return { type, payload };
  };
}

export const SHOW = prefix('SHOW');
export const HIDE = prefix('HIDE');
export const TOGGLE = prefix('TOGGLE');
export const KEEP = prefix('KEEP');
export const CONTENT = prefix('CONTENT');
export const PLACE = prefix('PLACE');

export const show = createAction(SHOW);
export const hide = createAction(HIDE);
export const toggle = createAction(TOGGLE);
export const keep = createAction(KEEP);
export const content = createAction(CONTENT);
export const place = createAction(PLACE);

export const DELAY = prefix('DELAY');
const DURATION = 1500;
export function delay(action, { duration, callback } = { duration: DURATION }) {
  if (!action.meta) {
    action.meta = {};
  }
  if (typeof duration === 'undefined' || duration === true) {
    duration = DURATION;
  }
  action.meta[DELAY] = { duration: parseInt(duration), callback };
  return action;
}

export const START_TIMEOUT = prefix('START_TIMEOUT');
export const END_TIMEOUT = prefix('END_TIMEOUT');

export const startTimeout = createAction(START_TIMEOUT);
export const endTimeout = createAction(END_TIMEOUT);


<|start_filename|>src/index.js<|end_filename|>
import * as actions from './actions';
import * as utils from './utils';

export { default as Tooltip } from './tooltip';
export { default as Origin } from './origin';
export { default as reducer } from './reducer';
export { default as middleware } from './middleware';
export { actions };
export { utils };


<|start_filename|>src/middleware.js<|end_filename|>
import { SHOW, HIDE, DELAY, startTimeout, endTimeout } from './actions';
import { resolve } from './utils';

function getToken(state, name) {
  const { tooltip: tooltips } = state;
  const tooltip = tooltips[name];
  if (tooltip) {
    return tooltip.timeout;
  }
}

const CANCEL_TYPES = [SHOW, HIDE];

export default function middleware(store) {
  return next => action => {
    const names = resolve(action);
    if (CANCEL_TYPES.indexOf(action.type) !== -1) {
      // Clear timeout
      names.forEach(name => {
        const token = getToken(store.getState(), name);
        token && clearTimeout(token);
      });
    }

    if (!action.meta || !action.meta[DELAY]) {
      return next(action);
    }

    // Setup timeout
    const { duration, callback } = action.meta[DELAY];
    names.forEach(name => {
      const newToken = setTimeout(() => {
        // Ignore if token is cleared
        const token = getToken(store.getState(), name);
        if (token !== null) {
          // Clear timeout token
          next(endTimeout({ name }));

          // Dispatch original action
          delete action.meta[DELAY];
          next(action);

          // Notify via callback
          callback && callback(action.type, duration, action);
        }
      }, duration);

      // Store timeout token
      next(startTimeout({ name, token: newToken }));
    });

    return names;
  };
}


<|start_filename|>tests/feature/multiple.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin } from '../../src/index';
import App from '../../examples/multiple/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

describe('Multiple Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe('basic usage', () => {
    it('should be worked', () => {
      // Mouseover to green origin
      const green = firstComponent(tree, Origin.WrappedComponent, { name: 'green' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(green);

      const greenTip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'green' }).refs.tooltip;
      assert(getStyleValue(greenTip, 'visibility') === 'visible');
      assert(greenTip.innerText === 'This is a Green tooltip.');

      TestUtils.Simulate.mouseLeave(green);
      assert(getStyleValue(greenTip, 'visibility') === 'hidden');

      // Mouseover to red origin
      const red = firstComponent(tree, Origin.WrappedComponent, { name: 'red' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(red);

      const redTip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'red' }).refs.tooltip;
      assert(getStyleValue(redTip, 'visibility') === 'visible');
      assert(redTip.innerText === 'This is a Red tooltip.');

      TestUtils.Simulate.mouseLeave(red);
      assert(getStyleValue(redTip, 'visibility') === 'hidden');

      // Mouseover to both origin
      const both = firstComponent(tree, Origin.WrappedComponent, { name: ['green', 'red'] }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(both);

      assert(getStyleValue(greenTip, 'visibility') === 'visible');
      assert(greenTip.innerText === 'This is a Green tooltip.');
      assert(getStyleValue(redTip, 'visibility') === 'visible');
      assert(redTip.innerText === 'This is a Red tooltip.');

      TestUtils.Simulate.mouseLeave(both);
      assert(getStyleValue(greenTip, 'visibility') === 'hidden');
      assert(getStyleValue(redTip, 'visibility') === 'hidden');
    });
  });
});


<|start_filename|>tests/feature/remote.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, utils } from '../../src/index';
import App from '../../examples/remote/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Remote Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe('basic usage', () => {
    it('should be worked show/hide/toggle', () => {
      // Show button
      const show = document.querySelector('input[type="button"][value="Show"]');
      TestUtils.Simulate.click(show);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === "This is a tooltip.\n", 'should be default content');

      // Hide button
      const hide = document.querySelector('input[type="button"][value="Hide"]');
      TestUtils.Simulate.click(hide);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');

      // Toggle button
      const toggle = document.querySelector('input[type="button"][value="Toggle"]');
      TestUtils.Simulate.click(toggle);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      TestUtils.Simulate.click(toggle);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');
    });

    it('should be located', () => {
      // Top button
      const top = document.querySelector('input[type="button"][value="Top"]');
      TestUtils.Simulate.click(top);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', "place action doesn't show tooltip");

      const show = document.querySelector('input[type="button"][value="Show"]');
      TestUtils.Simulate.click(show);
      TestUtils.Simulate.click(top);

      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === "This is a tooltip.\n", 'should be default content');

      const topPos = position(tooltip);

      // Right button
      const right = document.querySelector('input[type="button"][value="Right"]');
      TestUtils.Simulate.click(right);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const rightPos = position(tooltip);

      // Bottom button
      const bottom = document.querySelector('input[type="button"][value="Bottom"]');
      TestUtils.Simulate.click(bottom);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const bottomPos = position(tooltip);

      // Left button
      const left = document.querySelector('input[type="button"][value="Left"]');
      TestUtils.Simulate.click(left);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const leftPos = position(tooltip);

      assert(topPos.top < rightPos.top);
      assert(topPos.top < bottomPos.top);
      assert(topPos.top < leftPos.top);

      assert(rightPos.right > topPos.right);
      assert(rightPos.right > bottomPos.right);
      assert(rightPos.right > leftPos.right);

      assert(bottomPos.bottom > topPos.bottom);
      assert(bottomPos.bottom > rightPos.bottom);
      assert(bottomPos.bottom > leftPos.bottom);

      assert(leftPos.left < topPos.left);
      assert(leftPos.left < rightPos.left);
      assert(leftPos.left < bottomPos.left);

      assert(topPos.left === bottomPos.left);
      assert(topPos.right === bottomPos.right);

      assert(leftPos.top === rightPos.top);
      assert(leftPos.bottom === rightPos.bottom);
    });
  });

  describe('deprecated usage', () => {
    it('should show warning in console', () => {
      // Setup spy
      const warn = sinon.spy(console, 'warn');

      // Show button
      const show = document.querySelector('input[type="button"][value="Show via \'el\'"]');
      TestUtils.Simulate.click(show);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(tooltip.innerText === "This is a tooltip.\n", 'should be default content');

      assert(warn.calledOnce);
      assert(warn.firstCall.args[0] === "DEPRECATED: Use 'origin' instead of 'el' in props for Tooltip component or 'show' action.");
    });
  });
});


<|start_filename|>examples/common/reducers.js<|end_filename|>
import { combineReducers } from 'redux';
import tooltip from '../../src/reducer';

function app(state = {}, action) {
  switch (action.type) {
  default:
    return state;
  }
}

export default combineReducers(
  { app, tooltip }
);


<|start_filename|>tests/unit/test_utils.js<|end_filename|>
import assert from 'assert';
import * as utils from '../../src/utils';

describe('utils', () => {
  describe('.complete', () => {
    context('with a direction', () => {
      it('returns a completed list', () => {
        assert.deepStrictEqual(utils.complete('top'), ['top', 'right', 'bottom', 'left']);
        assert.deepStrictEqual(utils.complete('right'), ['right', 'top', 'bottom', 'left']);
        assert.deepStrictEqual(utils.complete('bottom'), ['bottom', 'top', 'right', 'left']);
        assert.deepStrictEqual(utils.complete('left'), ['left', 'top', 'right', 'bottom']);

        assert.deepStrictEqual(utils.complete(['top']), ['top', 'right', 'bottom', 'left']);
        assert.deepStrictEqual(utils.complete(['right']), ['right', 'top', 'bottom', 'left']);
        assert.deepStrictEqual(utils.complete(['bottom']), ['bottom', 'top', 'right', 'left']);
        assert.deepStrictEqual(utils.complete(['left']), ['left', 'top', 'right', 'bottom']);
      });
    });

    context('with a list of directions', () => {
      it('returns a completed list', () => {
        assert.deepStrictEqual(utils.complete(['top', 'bottom']), ['top', 'bottom', 'right', 'left']);
        assert.deepStrictEqual(utils.complete(['left', 'right']), ['left', 'right', 'top', 'bottom']);
        assert.deepStrictEqual(utils.complete(['top', 'right', 'bottom', 'left']), ['top', 'right', 'bottom', 'left']);
        assert.deepStrictEqual(utils.complete(['top', 'right', 'bottom', 'left']), ['top', 'right', 'bottom', 'left']);
      });
    });
  });

  describe('.intersection', () => {
    it("doesn't modify input data");

    context('with valid areas', () => {
      it('returns an intersection', () => {
        const a1 = { top: 0, left: 0, height: 100, width: 50 };
        const a2 = { top: 10, left: 20, height: 60, width: 80 };
        assert.deepStrictEqual(utils.intersection(a1, a2), {
          top: 10, left: 20, right: 50, bottom: 70,
          height: 60, width: 30
        });
      });
    });

    context('with same areas', () => {
      it('returns an intersection', () => {
        const area = { top: 0, left: 0, height: 100, width: 50 };
        assert.deepStrictEqual(utils.intersection(area, area), {
          top: 0, left: 0, right: 50, bottom: 100,
          height: 100, width: 50
        });
      });
    });

    context('with an area that contains an other one', () => {
      it('returns an intersection', () => {
        const a1 = { top: 0, left: 0, height: 100, width: 100 };
        const a2 = { top: 25, left: 25, height: 50, width: 50 };
        assert.deepStrictEqual(utils.intersection(a1, a2), {
          top: 25, left: 25, right: 75, bottom: 75,
          height: 50, width: 50
        });

        const a3 = { top: 50, left: 50, height: 50, width: 50 };
        assert.deepStrictEqual(utils.intersection(a1, a3), {
          top: 50, left: 50, right: 100, bottom: 100,
          height: 50, width: 50
        });
      });
    });

    context('with adjacent areas', () => {
      it('returns an intersection', () => {
        const a1 = { top: -20, left: 0, height: 20, width: 40 };
        const a2 = { top: 0, left: 0, height: 20, width: 40 };
        assert.deepStrictEqual(utils.intersection(a1, a2), {
          top: 0, left: 0, right: 40, bottom: 0,
          height: 0, width: 40
        });

        const a3 = { top: -20, left: 40, height: 20, width: 20 };
        assert.deepStrictEqual(utils.intersection(a1, a3), {
          top: -20, left: 40, right: 40, bottom: 0,
          height: 20, width: 0
        });
      });
    });

    context('with areas which are located in negative coordinate', () => {
      it('returns an intersection', () => {
        const a1 = { top: -100, left: -80, height: 50, width: 20 };
        const a2 = { top: -120, left: -100, height: 40, width: 60 };
        assert.deepStrictEqual(utils.intersection(a1, a2), {
          top: -100, left: -80, right: -60, bottom: -80,
          height: 20, width: 20
        });

        const a3 = { top: -80, left: -100, height: 10, width: 60 };
        assert.deepStrictEqual(utils.intersection(a1, a3), {
          top: -80, left: -80, right: -60, bottom: -70,
          height: 10, width: 20
        });
      });
    });
  });

  describe('.strip', () => {
    it("doesn't modify input data");

    context('with an area contains unit string', () => {
      it('returns a stripped area', () => {
        const area = { top: '50px', left: '48.8px' };
        assert.deepStrictEqual(utils.strip(area), { top: 50, left: 48.8 });
      });
    });

    context('with a stripped area', () => {
      it('returns a stripped area', () => {
        const area = { top: '50px', left: '48.8px' };
        const stripped = utils.strip(area);
        assert.deepStrictEqual(utils.strip(stripped), { top: 50, left: 48.8 });
      });
    });
  });

  describe('.amend', () => {
    it("doesn't modify input data");

    context('with a minimum area data', () => {
      it('returns a completed area data', () => {
        const less = { height: '50px', width: '25.5px' };
        assert.deepStrictEqual(utils.amend(less), {
          top: 0, left: 0, bottom: 50, right: 25.5, width: 25.5, height: 50
        });
      });
    });

    context('with a standard area data', () => {
      it('returns a completed area data', () => {
        const less = { top: '-25px', left: 0, height: 50.5, width: '80px' };
        assert.deepStrictEqual(utils.amend(less), {
          top: -25, left: 0, bottom: 25.5, right: 80, width: 80, height: 50.5
        });
      });
    });
  });

  describe('.resolve', () => {
    context('with action contains no names', () => {
      const action = {
        type: 'OOPS',
        payload: {
          value: 99
        },
      };

      it('returns a default name', () => {
        assert.deepStrictEqual(utils.resolve(action), ['default']);
      });
    });

    context('with action contains single name', () => {
      const action = {
        type: 'FOO',
        payload: {
          name: 'bar',
        },
      };

      it('returns a list of names', () => {
        assert.deepStrictEqual(utils.resolve(action), ['bar']);
      });
    });

    context('with action contains multiple names', () => {
      const action = {
        type: 'COLOR',
        payload: {
          name: ['red', 'green', 'blue'],
        },
      };

      it('returns a list of names', () => {
        assert.deepStrictEqual(utils.resolve(action), ['red', 'green', 'blue']);
      });
    });

    context('with props contains no names', () => {
      const props = {
        place: 'top',
      };

      it('returns a default name', () => {
        assert.deepStrictEqual(utils.resolve(props), ['default']);
      });
    });

    context('with props contains single name', () => {
      const props = {
        name: 'bar',
      };

      it('returns a list of names', () => {
        assert.deepStrictEqual(utils.resolve(props), ['bar']);
      });
    });

    context('with props contains multiple names', () => {
      const props = {
        name: ['red', 'green', 'blue'],
      };

      it('returns a list of names', () => {
        assert.deepStrictEqual(utils.resolve(props), ['red', 'green', 'blue']);
      });
    });
  });
});


<|start_filename|>tests/feature/simple.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin, utils } from '../../src/index';
import App from '../../examples/simple/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

const { position } = utils;

describe('Simple Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe('basic tooltip', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      assert(position(tooltip).top < position(origin).top);
      assert(tooltip.innerText === 'This is a tooltip.\n');

      // Mouseout
      TestUtils.Simulate.mouseLeave(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden');
    });
  });

  describe('multiple tooltips', () => {
    it('should be worked', () => {
      // Mouseover to first
      const first = firstComponent(tree, Origin.WrappedComponent, { className: 'target first' }).refs.wrapper;
      const second = firstComponent(tree, Origin.WrappedComponent, { className: 'target second' }).refs.wrapper;
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      TestUtils.Simulate.mouseEnter(first);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      const firstPos = position(first);
      const secondPos = position(second);
      const firstTipPos = position(tooltip);
      assert(firstTipPos.top < firstPos.top, 'tooltip should be located on top of first origin');
      assert(tooltip.innerText === 'This is a tooltip.\n');

      // Mouseover to second
      TestUtils.Simulate.mouseEnter(second);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      const secondTipPos = position(tooltip);
      assert(secondTipPos.top < secondPos.top, 'tooltip should be located on top of second origin');
      assert(firstTipPos.top === secondTipPos.top, 'position of both tooltips is the same');
      assert(firstTipPos.left < secondTipPos.left, 'first tooltip should be located to the left of second tooltip');
    });
  });

  describe('image tooltip', () => {
    it('should be worked', () => {
      // Mouseover
      const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'image' }).refs.wrapper;
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      TestUtils.Simulate.mouseEnter(origin);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      const oriPos = position(origin);
      const tipPos = position(tooltip);
      assert(tipPos.top < oriPos.top);
      assert(oriPos.left < tipPos.left);
      assert(tipPos.right < oriPos.right);
    });
  });

  describe('form tooltip', () => {
    it('should be worked', () => {
      // Mouseover on user
      const user = firstComponent(tree, Origin.WrappedComponent, { className: 'user' }).refs.wrapper;
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
      TestUtils.Simulate.mouseEnter(user);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      TestUtils.Simulate.mouseLeave(user);

      // Mouseover on domain
      const domain = firstComponent(tree, Origin.WrappedComponent, { className: 'domain' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(domain);
      assert(getStyleValue(tooltip, 'visibility') === 'visible');
      TestUtils.Simulate.mouseLeave(domain);
    });
  });
});


<|start_filename|>examples/style/app.js<|end_filename|>
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Tooltip, Origin } from '../../src/index';

class App extends Component {
  render() {
    return (
      <div>
        <h1>Style Example</h1>

        <p>
          If you want to apply a style defined in external CSS file,<br />
          you can give <Origin name="id-tip" className="target origin-id"><code>id</code></Origin> and <Origin name="class-tip" className="target origin-class"><code>class</code></Origin> attributes to the DIV element of a tooltip.
        </p>

        <Tooltip name="id-tip" id="my-id">
          This is a <b>id</b> tooltip.
        </Tooltip>

        <Tooltip name="class-tip" className="my-class">
          This is a <b>class</b> tooltip.
        </Tooltip>
      </div>
    );
  }
}

function select(state) {
  const { app } = state;
  return { app };
}

export default connect(select)(App);


<|start_filename|>tests/feature/keep.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin } from '../../src/index';
import App from '../../examples/keep/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue } from '../helpers';

// NOTE: Required to simulate both mouseover/out and mouseenter/leave events.

describe('Keep Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
  });

  let tree, clock;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));

    // Setup fake timer
    clock = new sinon.useFakeTimers();
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));

    // Take back real timer
    clock.restore();
  });

  describe('Text content', () => {
    it('should be worked', () => {
      // Mouseover to origin
      const origin = firstComponent(tree, Origin.WrappedComponent, { name: 'text' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);
      TestUtils.Simulate.mouseOver(origin);

      // TODO: firstComponent(tree, 'Tooltip[name="text"]')
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'text' }).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout from origin and mouseover to tooltip
      TestUtils.Simulate.mouseOut(origin);
      TestUtils.Simulate.mouseLeave(origin);
      TestUtils.Simulate.mouseEnter(tooltip);
      TestUtils.Simulate.mouseOver(tooltip);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      // A few moments later
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be stil shown');

      // Mouseout from tooltip
      TestUtils.Simulate.mouseOut(tooltip);
      TestUtils.Simulate.mouseLeave(tooltip);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');
    });
  });

  describe('SVG content', () => {
    it('should be worked', () => {
      // Mouseover to origin
      const origin = firstComponent(tree, Origin.WrappedComponent, { name: 'svg' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);
      TestUtils.Simulate.mouseOver(origin);

      // TODO: firstComponent(tree, 'Tooltip[name="svg"]')
      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'svg' }).refs.tooltip;
      assert(getStyleValue(tooltip, 'visibility') === 'visible');

      // Mouseout from origin and mouseover to tooltip
      TestUtils.Simulate.mouseOut(origin);
      TestUtils.Simulate.mouseLeave(origin);
      TestUtils.Simulate.mouseEnter(tooltip);
      TestUtils.Simulate.mouseOver(tooltip);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be shown');

      // Mouseout from tooltip base and mouseover to SVG
      const svg = document.querySelector('.svg-frame');
      TestUtils.Simulate.mouseOut(tooltip);
      TestUtils.Simulate.mouseEnter(svg);
      TestUtils.Simulate.mouseOver(svg);

      // A few moments later
      clock.tick(2000);
      assert(getStyleValue(tooltip, 'visibility') === 'visible', 'tooltip should be stil shown');

      // Mouseout from tooltip
      TestUtils.Simulate.mouseOut(svg);
      TestUtils.Simulate.mouseLeave(svg);
      TestUtils.Simulate.mouseOut(tooltip);
      TestUtils.Simulate.mouseLeave(tooltip);
      assert(getStyleValue(tooltip, 'visibility') === 'hidden', 'tooltip should be hidden');
    });
  });
});


<|start_filename|>tests/feature/style.js<|end_filename|>
import assert from 'assert';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import { Tooltip, Origin, utils } from '../../src/index';
import App from '../../examples/style/app';
import store from '../../examples/common/store';
import { firstComponent, getStyleValue, getComputedStyleValue } from '../helpers';

const { position } = utils;

describe('Style Example', () => {
  before(() => {
    document.body.innerHTML += '<div id="container" style="position:absolute;top:0;left:0;"></div>';
    document.body.innerHTML += `<style type="text/css">${window.__html__['examples/style.css']}</style>`;
  });

  let tree;
  beforeEach(() => {
    tree = ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    document.getElementById('container'));
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('container'));
  });

  describe('id tooltip', () => {
    it('should be colored', () => {
      const origin = firstComponent(tree, Origin.WrappedComponent, { name: 'id-tip' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'id-tip' }).refs.tooltip;
      assert(getComputedStyleValue(tooltip, 'background-color') === 'rgb(255, 205, 210)');
      assert(tooltip.innerText === 'This is a id tooltip.');

      const border = firstComponent(tree, Tooltip.WrappedComponent, { name: 'id-tip' }).refs.border;
      assert(getComputedStyleValue(border, 'border-color') === 'rgb(255, 205, 210) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)');
    });
  });

  describe('class tooltip', () => {
    it('should be colored', () => {
      const origin = firstComponent(tree, Origin.WrappedComponent, { name: 'class-tip' }).refs.wrapper;
      TestUtils.Simulate.mouseEnter(origin);

      const tooltip = firstComponent(tree, Tooltip.WrappedComponent, { name: 'class-tip' }).refs.tooltip;
      assert(getComputedStyleValue(tooltip, 'background-color') === 'rgb(187, 222, 251)');
      assert(tooltip.innerText === 'This is a class tooltip.');

      const border = firstComponent(tree, Tooltip.WrappedComponent, { name: 'class-tip' }).refs.border;
      assert(getComputedStyleValue(border, 'border-color') === 'rgb(187, 222, 251) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)');
    });
  });
});
