mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 19:17:09 +03:00
318 lines
11 KiB
JavaScript
318 lines
11 KiB
JavaScript
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 }));
|
|
}
|