123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- const { CompositeDisposable } = require('atom');
- const TooltipManager = require('../src/tooltip-manager');
- const Tooltip = require('../src/tooltip');
- const _ = require('underscore-plus');
- describe('TooltipManager', () => {
- let manager, element;
- const ctrlX = _.humanizeKeystroke('ctrl-x');
- const ctrlY = _.humanizeKeystroke('ctrl-y');
- const hover = function(element, fn) {
- mouseEnter(element);
- advanceClock(manager.hoverDefaults.delay.show);
- fn();
- mouseLeave(element);
- advanceClock(manager.hoverDefaults.delay.hide);
- };
- beforeEach(function() {
- manager = new TooltipManager({
- keymapManager: atom.keymaps,
- viewRegistry: atom.views
- });
- element = createElement('foo');
- });
- describe('::add(target, options)', () => {
- describe("when the trigger is 'hover' (the default)", () => {
- it('creates a tooltip when hovering over the target element', () => {
- manager.add(element, { title: 'Title' });
- hover(element, () =>
- expect(document.body.querySelector('.tooltip')).toHaveText('Title')
- );
- });
- it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', () => {
- const disposables = new CompositeDisposable();
- const element1 = createElement('foo');
- disposables.add(manager.add(element1, { title: 'Title' }));
- const element2 = createElement('bar');
- disposables.add(manager.add(element2, { title: 'Title' }));
- const element3 = createElement('baz');
- disposables.add(manager.add(element3, { title: 'Title' }));
- hover(element1, () => {});
- expect(document.body.querySelector('.tooltip')).toBeNull();
- mouseEnter(element2);
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- mouseLeave(element2);
- advanceClock(manager.hoverDefaults.delay.hide);
- expect(document.body.querySelector('.tooltip')).toBeNull();
- advanceClock(Tooltip.FOLLOW_THROUGH_DURATION);
- mouseEnter(element3);
- expect(document.body.querySelector('.tooltip')).toBeNull();
- advanceClock(manager.hoverDefaults.delay.show);
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- disposables.dispose();
- });
- it('hides the tooltip on keydown events', () => {
- const disposable = manager.add(element, {
- title: 'Title',
- trigger: 'hover'
- });
- hover(element, function() {
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- window.dispatchEvent(
- new CustomEvent('keydown', {
- bubbles: true
- })
- );
- expect(document.body.querySelector('.tooltip')).toBeNull();
- disposable.dispose();
- });
- });
- });
- describe("when the trigger is 'manual'", () =>
- it('creates a tooltip immediately and only hides it on dispose', () => {
- const disposable = manager.add(element, {
- title: 'Title',
- trigger: 'manual'
- });
- expect(document.body.querySelector('.tooltip')).toHaveText('Title');
- disposable.dispose();
- expect(document.body.querySelector('.tooltip')).toBeNull();
- }));
- describe("when the trigger is 'click'", () =>
- it('shows and hides the tooltip when the target element is clicked', () => {
- manager.add(element, { title: 'Title', trigger: 'click' });
- expect(document.body.querySelector('.tooltip')).toBeNull();
- element.click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- element.click();
- expect(document.body.querySelector('.tooltip')).toBeNull();
- // Hide the tooltip when clicking anywhere but inside the tooltip element
- element.click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- document.body.querySelector('.tooltip').click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- document.body.querySelector('.tooltip').firstChild.click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- document.body.click();
- expect(document.body.querySelector('.tooltip')).toBeNull();
- // Tooltip can show again after hiding due to clicking outside of the tooltip
- element.click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- element.click();
- expect(document.body.querySelector('.tooltip')).toBeNull();
- }));
- it('does not hide the tooltip on keyboard input', () => {
- manager.add(element, { title: 'Title', trigger: 'click' });
- element.click();
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- window.dispatchEvent(
- new CustomEvent('keydown', {
- bubbles: true
- })
- );
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- // click again to hide the tooltip because otherwise state leaks
- // into other tests.
- element.click();
- });
- it('allows a custom item to be specified for the content of the tooltip', () => {
- const tooltipElement = document.createElement('div');
- manager.add(element, { item: { element: tooltipElement } });
- hover(element, () =>
- expect(tooltipElement.closest('.tooltip')).not.toBeNull()
- );
- });
- it('allows a custom class to be specified for the tooltip', () => {
- manager.add(element, { title: 'Title', class: 'custom-tooltip-class' });
- hover(element, () =>
- expect(
- document.body
- .querySelector('.tooltip')
- .classList.contains('custom-tooltip-class')
- ).toBe(true)
- );
- });
- it('allows jQuery elements to be passed as the target', () => {
- const element2 = document.createElement('div');
- jasmine.attachToDOM(element2);
- const fakeJqueryWrapper = {
- 0: element,
- 1: element2,
- length: 2,
- jquery: 'any-version'
- };
- const disposable = manager.add(fakeJqueryWrapper, { title: 'Title' });
- hover(element, () =>
- expect(document.body.querySelector('.tooltip')).toHaveText('Title')
- );
- expect(document.body.querySelector('.tooltip')).toBeNull();
- hover(element2, () =>
- expect(document.body.querySelector('.tooltip')).toHaveText('Title')
- );
- expect(document.body.querySelector('.tooltip')).toBeNull();
- disposable.dispose();
- hover(element, () =>
- expect(document.body.querySelector('.tooltip')).toBeNull()
- );
- hover(element2, () =>
- expect(document.body.querySelector('.tooltip')).toBeNull()
- );
- });
- describe('when a keyBindingCommand is specified', () => {
- describe('when a title is specified', () =>
- it('appends the key binding corresponding to the command to the title', () => {
- atom.keymaps.add('test', {
- '.foo': { 'ctrl-x ctrl-y': 'test-command' },
- '.bar': { 'ctrl-x ctrl-z': 'test-command' }
- });
- manager.add(element, {
- title: 'Title',
- keyBindingCommand: 'test-command'
- });
- hover(element, function() {
- const tooltipElement = document.body.querySelector('.tooltip');
- expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`);
- });
- }));
- describe('when no title is specified', () =>
- it('shows the key binding corresponding to the command alone', () => {
- atom.keymaps.add('test', {
- '.foo': { 'ctrl-x ctrl-y': 'test-command' }
- });
- manager.add(element, { keyBindingCommand: 'test-command' });
- hover(element, function() {
- const tooltipElement = document.body.querySelector('.tooltip');
- expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`);
- });
- }));
- describe('when a keyBindingTarget is specified', () => {
- it('looks up the key binding relative to the target', () => {
- atom.keymaps.add('test', {
- '.bar': { 'ctrl-x ctrl-z': 'test-command' },
- '.foo': { 'ctrl-x ctrl-y': 'test-command' }
- });
- manager.add(element, {
- keyBindingCommand: 'test-command',
- keyBindingTarget: element
- });
- hover(element, function() {
- const tooltipElement = document.body.querySelector('.tooltip');
- expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`);
- });
- });
- it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', () => {
- manager.add(element, {
- title: 'A Title',
- keyBindingCommand: 'test-command',
- keyBindingTarget: element
- });
- hover(element, function() {
- const tooltipElement = document.body.querySelector('.tooltip');
- expect(tooltipElement.textContent).toBe('A Title');
- });
- });
- });
- });
- describe('when .dispose() is called on the returned disposable', () =>
- it('no longer displays the tooltip on hover', () => {
- const disposable = manager.add(element, { title: 'Title' });
- hover(element, () =>
- expect(document.body.querySelector('.tooltip')).toHaveText('Title')
- );
- disposable.dispose();
- hover(element, () =>
- expect(document.body.querySelector('.tooltip')).toBeNull()
- );
- }));
- describe('when the window is resized', () =>
- it('hides the tooltips', () => {
- const disposable = manager.add(element, { title: 'Title' });
- hover(element, function() {
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- window.dispatchEvent(new CustomEvent('resize'));
- expect(document.body.querySelector('.tooltip')).toBeNull();
- disposable.dispose();
- });
- }));
- describe('findTooltips', () => {
- it('adds and remove tooltips correctly', () => {
- expect(manager.findTooltips(element).length).toBe(0);
- const disposable1 = manager.add(element, { title: 'elem1' });
- expect(manager.findTooltips(element).length).toBe(1);
- const disposable2 = manager.add(element, { title: 'elem2' });
- expect(manager.findTooltips(element).length).toBe(2);
- disposable1.dispose();
- expect(manager.findTooltips(element).length).toBe(1);
- disposable2.dispose();
- expect(manager.findTooltips(element).length).toBe(0);
- });
- it('lets us hide tooltips programmatically', () => {
- const disposable = manager.add(element, { title: 'Title' });
- hover(element, function() {
- expect(document.body.querySelector('.tooltip')).not.toBeNull();
- manager.findTooltips(element)[0].hide();
- expect(document.body.querySelector('.tooltip')).toBeNull();
- disposable.dispose();
- });
- });
- });
- });
- });
- function createElement(className) {
- const el = document.createElement('div');
- el.classList.add(className);
- jasmine.attachToDOM(el);
- return el;
- }
- function mouseEnter(element) {
- element.dispatchEvent(new CustomEvent('mouseenter', { bubbles: false }));
- element.dispatchEvent(new CustomEvent('mouseover', { bubbles: true }));
- }
- function mouseLeave(element) {
- element.dispatchEvent(new CustomEvent('mouseleave', { bubbles: false }));
- element.dispatchEvent(new CustomEvent('mouseout', { bubbles: true }));
- }
|