angular-material-npfixed
Version:
The Angular Material project is an implementation of Material Design in Angular.js. This project provides a set of reusable, well-tested, and accessible Material Design UI components. Angular Material is supported internally at Google by the Angular.js, M
523 lines (421 loc) • 14.8 kB
JavaScript
describe('MdTooltip Component', function() {
var $compile, $rootScope, $material, $timeout, $mdPanel, $$mdTooltipRegistry;
var element;
var injectLocals = function($injector) {
$compile = $injector.get('$compile');
$rootScope = $injector.get('$rootScope');
$material = $injector.get('$material');
$timeout = $injector.get('$timeout');
$mdPanel = $injector.get('$mdPanel');
$$mdTooltipRegistry = $injector.get('$$mdTooltipRegistry');
};
beforeEach(function() {
module(
'material.components.tooltip',
'material.components.button'
);
inject(injectLocals);
});
afterEach(function() {
// Make sure to remove/cleanup after each test.
element.remove();
var scope = element && element.scope();
scope && scope.$destroy;
element = undefined;
});
it('should support dynamic directions', function() {
expect(function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-direction="{{direction}}">Tooltip</md-tooltip>' +
'</md-button>'
);
}).not.toThrow();
});
it('should set the position to "bottom" if it is undefined', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="true">Tooltip</md-tooltip>' +
'</md-button>'
);
expect(findTooltip()).toHaveClass('md-origin-bottom');
});
it('should not re-templatize tooltip content', function() {
$rootScope.name = '{{2 + 2}}';
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="true">{{name}}</md-tooltip>' +
'</md-button>'
);
expect(findTooltip().text()).toBe('{{2 + 2}}');
});
it('should preserve parent text', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">Tooltip</md-tooltip>' +
'</md-button>'
);
expect(element.text()).toBe('Hello');
});
it('should label parent', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
expect(element.attr('aria-label')).toEqual('Tooltip');
});
it('should interpolate the aria-label', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip>{{ "hello" | uppercase }}</md-tooltip>' +
'</md-button>'
);
expect(element.attr('aria-label')).toBe('HELLO');
});
it('should update the aria-label when the interpolated value changes',
function() {
buildTooltip(
'<md-button>' +
'<md-tooltip>{{ testModel.ariaText }}</md-tooltip>' +
'</md-button>'
);
$rootScope.$apply(function() {
$rootScope.testModel.ariaText = 'test 1';
});
expect(element.attr('aria-label')).toBe('test 1');
$rootScope.$apply(function() {
$rootScope.testModel.ariaText = 'test 2';
});
expect(element.attr('aria-label')).toBe('test 2');
});
it('should not interpolate interpolated values', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip>{{ testModel.ariaTest }}</md-tooltip>' +
'</md-button>'
);
$rootScope.$apply(function() {
$rootScope.testModel.ariaTest = 'test {{1+1}}';
});
expect(element.attr('aria-label')).toBe('test {{1+1}}');
$rootScope.$apply(function() {
$rootScope.testModel.ariaTest = 'test {{1+1336}}';
});
expect(element.attr('aria-label')).toBe('test {{1+1336}}');
});
it('should not set parent to items with no pointer events',
inject(function($window) {
spyOn($window, 'getComputedStyle').and.callFake(function(el) {
return { 'pointer-events': el ? 'none' : '' };
});
buildTooltip(
'<outer>' +
'<inner>' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Hello world' +
'</md-tooltip>' +
'</inner>' +
'</outer>'
);
triggerEvent('mouseenter', true);
expect($rootScope.testModel.isVisible).toBeUndefined();
}));
it('should show after tooltipDelay ms', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible" md-delay="99">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
triggerEvent('focus', true);
expect($rootScope.testModel.isVisible).toBeFalsy();
// Wait 1 below delay, nothing should happen
$timeout.flush(98);
expect($rootScope.testModel.isVisible).toBeFalsy();
// Total 300 == tooltipDelay
$timeout.flush(1);
expect($rootScope.testModel.isVisible).toBe(true);
});
it('should register itself with the $$mdTooltipRegistry', function() {
spyOn($$mdTooltipRegistry, 'register');
buildTooltip(
'<md-button>' +
'<md-tooltip>Tooltip</md-tooltip>' +
'</md-button>'
);
expect($$mdTooltipRegistry.register).toHaveBeenCalled();
});
describe('show and hide', function() {
it('should show and hide when visible is set', function() {
expect(findTooltip().length).toBe(0);
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
showTooltip(true);
expect(findTooltip().length).toBe(1);
expect(findTooltip().hasClass('md-show')).toBe(true);
showTooltip(false);
expect(findTooltip().length).toBe(1);
expect(findTooltip().hasClass('md-hide')).toBe(true);
});
it('should set visible on mouseenter and mouseleave', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
triggerEvent('mouseenter');
expect($rootScope.testModel.isVisible).toBe(true);
triggerEvent('mouseleave');
expect($rootScope.testModel.isVisible).toBe(false);
});
it('should toggle visibility on the next touch',
inject(function($document) {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
triggerEvent('touchstart');
expect($rootScope.testModel.isVisible).toBe(true);
triggerEvent('touchend');
$document.triggerHandler('touchend');
$timeout.flush();
expect($rootScope.testModel.isVisible).toBe(false);
}));
it('should cancel when mouseleave was before the delay', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip ' +
'md-delay="99" ' +
'md-autohide ' +
'md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
triggerEvent('mouseenter', true);
expect($rootScope.testModel.isVisible).toBeFalsy();
triggerEvent('mouseleave', true);
expect($rootScope.testModel.isVisible).toBeFalsy();
// Total 99 == tooltipDelay
$timeout.flush(99);
expect($rootScope.testModel.isVisible).toBe(false);
});
it('should throw when the tooltip text is empty', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'{{ textContent }}' +
'</md-tooltip>' +
'</md-button>'
);
expect(function() {
showTooltip(true);
}).toThrow();
});
it('should set visible on focus and blur', function() {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
triggerEvent('focus');
expect($rootScope.testModel.isVisible).toBe(true);
triggerEvent('blur');
expect($rootScope.testModel.isVisible).toBe(false);
});
it('should not be visible on mousedown and then mouseleave',
inject(function($document) {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
// Append element to DOM so it can be set as activeElement.
$document[0].body.appendChild(element[0]);
element[0].focus();
triggerEvent('focus,mousedown');
expect($document[0].activeElement).toBe(element[0]);
expect($rootScope.testModel.isVisible).toBe(true);
triggerEvent('mouseleave');
expect($rootScope.testModel.isVisible).toBe(false);
// Clean up document.body.
// element.remove();
}));
it('should not be visible when the window is refocused',
inject(function($window, $document) {
buildTooltip(
'<md-button>' +
'Hello' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
// Append element to DOM so it can be set as activeElement.
$document[0].body.appendChild(element[0]);
element[0].focus();
triggerEvent('focus,mousedown');
expect(document.activeElement).toBe(element[0]);
triggerEvent('mouseleave');
// Simulate tabbing away.
angular.element($window).triggerHandler('blur');
// Simulate focus event that occurs when tabbing back to the window.
triggerEvent('focus');
expect($rootScope.testModel.isVisible).toBe(false);
// Clean up document.body.
$document[0].body.removeChild(element[0]);
}));
});
describe('cleanup', function() {
it('should clean up if the parent scope was destroyed', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="true">Tooltip</md-tooltip>' +
'</md-button>'
);
var tooltip = findTooltip();
expect(tooltip.length).toBe(1);
expect(tooltip.scope()).toBeTruthy();
element.scope().$destroy();
expect(tooltip.scope()).toBeUndefined();
expect(findTooltip().length).toBe(0);
});
it('should remove the tooltip when its own scope is destroyed', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="true">Tooltip</md-tooltip>' +
'</md-button>'
);
var tooltip = findTooltip();
expect(tooltip.length).toBe(1);
tooltip.scope().$destroy();
expect(findTooltip().length).toBe(0);
});
it('should remove itself from the $$mdTooltipRegistry when the parent ' +
'scope is destroyed', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="true">Tooltip</md-tooltip>' +
'</md-button>'
);
spyOn($$mdTooltipRegistry, 'deregister');
element.scope().$destroy();
expect($$mdTooltipRegistry.deregister).toHaveBeenCalled();
});
it('should not re-appear if it was outside the DOM when the parent was ' +
'removed', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="testModel.isVisible">' +
'Tooltip' +
'</md-tooltip>' +
'</md-button>'
);
showTooltip(false);
expect(findTooltip().length).toBe(0);
element.remove();
showTooltip(true);
expect(findTooltip().length).toBe(0);
});
it('should unbind the parent listeners when it gets destroyed', function() {
buildTooltip(
'<md-button>' +
'<md-tooltip md-visible="testModel.isVisible">Tooltip</md-tooltip>' +
'</md-button>'
);
triggerEvent('focus');
expect($rootScope.testModel.isVisible).toBe(true);
element.remove();
triggerEvent('blur mouseleave touchend touchcancel');
expect($rootScope.testModel.isVisible).toBe(true);
});
});
// ******************************************************
// Internal Utility methods
// ******************************************************
function buildTooltip(markup) {
element = $compile(markup)($rootScope);
$rootScope.testModel = {};
$rootScope.$apply();
$material.flushOutstandingAnimations();
return element;
}
function showTooltip(isVisible) {
if (angular.isUndefined(isVisible)) {
isVisible = true;
}
$rootScope.testModel.isVisible = !!isVisible;
$rootScope.$apply();
$material.flushOutstandingAnimations();
}
function findTooltip() {
return angular.element(document.querySelector('.md-tooltip'));
}
function triggerEvent(eventType, skipFlush) {
angular.forEach(eventType.split(','), function(name) {
element.triggerHandler(name);
});
!skipFlush && $timeout.flush();
}
});
// ******************************************************
// mdTooltipRegistry Testing
// ******************************************************
describe('$$mdTooltipRegistry service', function() {
var tooltipRegistry, ngWindow;
beforeEach(function() {
module('material.components.tooltip');
inject(function($$mdTooltipRegistry, $window) {
tooltipRegistry = $$mdTooltipRegistry;
ngWindow = angular.element($window);
});
});
it('should allow for registering event handlers on the window', function() {
var obj = { callback: function() {} };
spyOn(obj, 'callback');
tooltipRegistry.register('resize', obj.callback);
ngWindow.triggerHandler('resize');
// check that the callback was triggered
expect(obj.callback).toHaveBeenCalled();
// check that the event object was passed
expect(obj.callback.calls.mostRecent().args[0]).toBeTruthy();
});
it('should allow deregistering of the callbacks', function() {
var obj = { callback: function() {} };
spyOn(obj, 'callback');
tooltipRegistry.register('resize', obj.callback);
ngWindow.triggerHandler('resize');
expect(obj.callback).toHaveBeenCalledTimes(1);
tooltipRegistry.deregister('resize', obj.callback);
ngWindow.triggerHandler('resize');
expect(obj.callback).toHaveBeenCalledTimes(1);
});
});