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
466 lines (370 loc) • 14.3 kB
JavaScript
describe('material.components.menu', function() {
var attachedElements = [];
var $mdMenu, $timeout, menuActionPerformed, $mdUtil;
beforeEach(module('material.components.menu'));
beforeEach(inject(function(_$mdUtil_, _$mdMenu_, _$timeout_) {
$mdUtil = _$mdUtil_;
$mdMenu = _$mdMenu_;
$timeout = _$timeout_;
}));
afterEach(inject(function($document) {
menuActionPerformed = false;
attachedElements.forEach(function(element) {
element.remove();
});
attachedElements = [];
var abandonedMenus = $document[0].querySelectorAll('.md-open-menu-container');
angular.element(abandonedMenus).remove();
}));
describe('md-menu directive', function() {
it('should have `._md` class indicator', function() {
var element = setup();
expect(element.hasClass('_md')).toBe(true);
});
it('should throw when trigger element is missing', inject(function($compile, $rootScope) {
function createInvalidMenu() {
$compile(
'<md-menu>' +
' <md-menu-content>Menu Content</md-menu-content>' +
'</md-menu>'
)($rootScope);
}
expect(createInvalidMenu).toThrow();
}));
it('should throw when md-menu-content is missing', inject(function($compile, $rootScope) {
function createInvalidMenu() {
$compile(
'<md-menu>' +
' <button ng-click="null">Trigger Element</button>' +
'</md-menu>'
)($rootScope);
}
expect(createInvalidMenu).toThrow();
}));
it('specifies button type', inject(function($compile, $rootScope) {
var menu = setup()[0];
expect(menu.firstElementChild.getAttribute('type')).toBe('button');
}));
it('opens on click', function () {
var menu = setup();
openMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(1);
closeMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('cleans up an open menu when the element leaves the DOM', function() {
var menu = setup();
openMenu(menu);
menu.remove();
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('sets up proper aria-owns and aria-haspopup relations between the button and the container', function() {
var menu = setup();
var button = menu[0].querySelector('button');
expect(button.hasAttribute('aria-haspopup')).toBe(true);
expect(button.hasAttribute('aria-owns')).toBe(true);
var ownsId = button.getAttribute('aria-owns');
openMenu(menu);
expect(getOpenMenuContainer(menu).attr('id')).toBe(ownsId);
});
it('opens on click without $event', function() {
var menu = setup('ng-click="$mdMenu.open()"');
openMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(1);
closeMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('opens on mouseEnter', function() {
var menu = setup('ng-mouseenter="$mdMenu.open($event)"');
openMenu(menu, 'mouseenter');
expect(getOpenMenuContainer(menu).length).toBe(1);
closeMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('opens on mouseEnter without $event', function() {
var menu = setup('ng-mouseenter="$mdMenu.open()"');
openMenu(menu, 'mouseenter');
expect(getOpenMenuContainer(menu).length).toBe(1);
closeMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('should not propagate the click event', function() {
var clickDetected = false, menu = setup();
menu.on('click', function() {
clickDetected = true;
});
openMenu(menu);
expect(clickDetected).toBe(false);
closeMenu(menu);
expect(clickDetected).toBe(false);
});
it('should remove the backdrop if container got destroyed', inject(function($document) {
var menu = setup();
openMenu(menu);
expect($document.find('md-backdrop').length).not.toBe(0);
menu.remove();
expect($document.find('md-backdrop').length).toBe(0);
}));
it('should remove the backdrop if the container scope got destroyed', inject(function($document, $rootScope) {
var scope = $rootScope.$new();
var menu = setup(null, scope);
openMenu(menu);
expect($document.find('md-backdrop').length).not.toBe(0);
scope.$destroy();
expect($document.find('md-backdrop').length).toBe(0);
}));
it('closes on backdrop click', inject(function($document) {
var menu = setup();
openMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(1);
$document.find('md-backdrop').triggerHandler('click');
waitForMenuClose();
expect(getOpenMenuContainer(menu).length).toBe(0);
}));
it('closes on escape', inject(function($document, $mdConstant) {
var menu = setup();
openMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(1);
var openMenuEl = $document[0].querySelector('md-menu-content');
pressKey(openMenuEl, $mdConstant.KEY_CODE.ESCAPE);
waitForMenuClose();
expect(getOpenMenuContainer(menu).length).toBe(0);
}));
describe('default focus', function() {
it('should focus on first item automatically', inject(function($compile, $rootScope, $document) {
var menu = $compile(
'<md-menu>' +
'<button ng-click="$mdOpenMenu($event)">Hello World</button>' +
'<md-menu-content>' +
'<md-menu-item>' +
'<button id="menuItem0" ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'<md-menu-item>' +
'<button ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'</md-menu-content>' +
'</md-menu>'
)($rootScope);
openMenu(menu);
var menuTarget = $document[0].querySelector('#menuItem0');
expect(document.activeElement).toBe(menuTarget);
}));
it('should focus on first non-disabled item', inject(function($compile, $rootScope, $document) {
var menu = $compile(
'<md-menu>' +
'<button ng-click="$mdOpenMenu($event)">Hello World</button>' +
'<md-menu-content>' +
'<md-menu-item>' +
'<button disabled ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'<md-menu-item>' +
'<button id="menuItem1" ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'</md-menu-content>' +
'</md-menu>'
)($rootScope);
openMenu(menu);
var menuTarget = $document[0].querySelector('#menuItem1');
expect(document.activeElement).toBe(menuTarget);
}));
});
describe('autofocus', function() {
it('should focus a button with md-menu-focus-target', inject(function($compile, $rootScope, $document) {
var menu = $compile(
'<md-menu>' +
'<button ng-click="$mdMenu.open($event)">Hello World</button>' +
'<md-menu-content>' +
'<md-menu-item>' +
'<button ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'<md-menu-item>' +
'<button id="menuFocus" md-menu-focus-target ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'</md-menu-content>' +
'</md-menu>'
)($rootScope);
openMenu(menu);
var menuTarget = $document[0].querySelector('#menuFocus');
expect(document.activeElement).toBe(menuTarget);
}));
it('should focus a button with md-autofocus', inject(function($compile, $rootScope, $document) {
var menu = $compile(
'<md-menu>' +
'<button ng-click="$mdMenu.open($event)">Hello World</button>' +
'<md-menu-content>' +
'<md-menu-item>' +
'<button ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'<md-menu-item>' +
'<button id="menuFocus" md-autofocus ng-click="doSomething($event)"></button>' +
'</md-menu-item>' +
'</md-menu-content>' +
'</md-menu>'
)($rootScope);
openMenu(menu);
var menuTarget = $document[0].querySelector('#menuFocus');
expect(document.activeElement).toBe(menuTarget);
}));
});
describe('closes with -', function() {
it('closes on normal option click', function() {
var menu = setup();
expect(getOpenMenuContainer(menu).length).toBe(0);
openMenu(menu);
expect(menuActionPerformed).toBeFalsy();
expect(getOpenMenuContainer(menu).length).toBe(1);
var btn = getOpenMenuContainer(menu)[0].querySelector('md-button');
btn.click();
waitForMenuClose();
expect(menuActionPerformed).toBeTruthy();
expect(getOpenMenuContainer(menu).length).toBe(0);
});
it('closes via the scope method', function() {
var menu = setup('ng-mouseenter="$mdMenu.open($event)" ng-mouseleave="$mdMenu.close()"');
expect(getOpenMenuContainer(menu).length).toBe(0);
openMenu(menu, 'mouseenter');
expect(getOpenMenuContainer(menu).length).toBe(1);
menu.find('button').triggerHandler('mouseleave');
waitForMenuClose();
expect(getOpenMenuContainer(menu).length).toBe(0);
});
itClosesWithAttributes([
'data-ng-click', 'x-ng-click',
'ui-sref', 'data-ui-sref', 'x-ui-sref',
'ng-href', 'data-ng-href', 'x-ng-href'
]);
function itClosesWithAttributes(types) {
for (var i = 0; i < types.length; ++i) {
it('closes with ' + types[i], testAttribute(types[i]));
}
function testAttribute(attr) {
return inject(function($rootScope, $compile, $timeout) {
var template = '' +
'<md-menu>' +
' <button ng-click="$mdMenu.open($event)">Hello World</button>' +
' <md-menu-content>' +
' <md-menu-item>' +
' <md-button ' + attr + '=""></md-button>' +
' </md-menu-item>' +
' </md-menu-content>' +
'</md-menu>';
var menu = $compile(template)($rootScope);
openMenu(menu);
expect(getOpenMenuContainer(menu).length).toBe(1);
$timeout.flush();
var btn = getOpenMenuContainer(menu)[0].querySelector('md-button');
btn.click();
waitForMenuClose();
expect(getOpenMenuContainer(menu).length).toBe(0);
});
}
}
});
function setup(buttonAttrs, scope) {
var menu,
template = $mdUtil.supplant('' +
'<md-menu>' +
' <button {0}>Hello World</button>' +
' <md-menu-content>' +
' <md-menu-item>' +
' <md-button ng-click="doSomething($event)"></md-button>' +
' </md-menu-item>' +
' </md-menu-content>' +
'</md-menu>', [ buttonAttrs || 'ng-click="$mdMenu.open($event)"' ]);
inject(function($compile, $rootScope) {
$rootScope.doSomething = function($event) {
menuActionPerformed = true;
};
menu = $compile(template)(scope || $rootScope);
});
attachedElements.push(menu);
return menu;
}
});
describe('with $mdMenu service', function() {
var $mdMenu, $rootScope, $compile, $timeout, $log = null;
beforeEach(inject(function($injector) {
$mdMenu = $injector.get('$mdMenu');
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$timeout = $injector.get('$timeout');
$log = $injector.get('$log');
}));
it('should warn when the md-menu-content element is missing', function() {
spyOn($log, 'warn');
var parent = angular.element('<div>');
var menuEl = angular.element(
'<md-menu>' +
' <button ng-click="null">Trigger</button>' +
'</md-menu>'
);
expect($log.warn).not.toHaveBeenCalled();
$mdMenu.show({
scope: $rootScope,
mdMenuCtrl: createFakeMenuController(),
element: menuEl,
target: document.body,
preserveElement: true,
parent: parent
});
$timeout.flush();
expect($log.warn).toHaveBeenCalledTimes(1);
// Close the menu and remove the parent container
$mdMenu.hide();
parent.remove();
});
function createFakeMenuController() {
return {
open: function() {},
close: function() { $mdMenu.hide(); },
positionMode: function() { return { left: 'left', top: 'target' }; },
offsets: function() { return { top: 0, left: 0 }; }
}
}
});
// ********************************************
// Internal methods
// ********************************************
function getOpenMenuContainer(el) {
var res;
el = (el instanceof angular.element) ? el[0] : el;
inject(function($document) {
var container = $document[0].querySelector('.md-open-menu-container');
if (container && container.style.display == 'none') {
res = [];
} else {
res = angular.element(container);
}
});
return res;
}
function openMenu(el, triggerType) {
el.children().eq(0).triggerHandler(triggerType || 'click');
waitForMenuOpen();
}
function closeMenu() {
inject(function($document) {
$document.find('md-backdrop').triggerHandler('click');
waitForMenuClose();
});
}
function waitForMenuOpen() {
inject(function($material) {
$material.flushInterimElement();
});
}
function waitForMenuClose() {
inject(function($material) {
$material.flushInterimElement();
});
}
function pressKey(el, code) {
if (!(el instanceof angular.element)) {
el = angular.element(el);
}
el.triggerHandler({
type: 'keydown',
keyCode: code
});
}
});