UNPKG

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

491 lines (432 loc) 17.2 kB
describe('material.components.menuBar', function() { var attachedMenuElements = []; beforeEach(module('material.components.menuBar')); afterEach(function() { attachedMenuElements.forEach(function(element) { element.remove(); }); attachedMenuElements = []; }); describe('MenuBar', function() { describe('MenuBar Directive', function() { it('should have `._md` class indicator', function() { var element = setup(); expect(element.hasClass('_md')).toBe(true); }); it('sets md-position-mode to "bottom left" on nested menus', function() { var menuBar = setup(); var nestedMenu = menuBar[0].querySelector('md-menu'); expect(nestedMenu.getAttribute('md-position-mode')).toBe('left bottom'); }); it('should close when clicking on the wrapping toolbar', inject(function($compile, $rootScope, $timeout, $material) { var ctrl = null; var menuCtrl = null; var toolbar = $compile( '<md-toolbar>' + '<md-menu-bar>' + '<md-menu ng-repeat="i in [1, 2, 3]">' + '<button ng-click></button>' + '<md-menu-content></md-menu-content>' + '</md-menu>' + '</md-menu-bar>' + '</md-toolbar>' )($rootScope); $rootScope.$digest(); attachedMenuElements.push(toolbar); // ensure it gets cleaned up ctrl = toolbar.find('md-menu-bar').controller('mdMenuBar'); menuCtrl = toolbar.find('md-menu').eq(0).controller('mdMenu'); menuCtrl.open(); $timeout.flush(); expect(toolbar).toHaveClass('md-has-open-menu'); spyOn(menuCtrl, 'close').and.callThrough(); toolbar.triggerHandler('click'); $material.flushInterimElement(); // flush out the scroll mask expect(toolbar).not.toHaveClass('md-has-open-menu'); expect(ctrl.getOpenMenuIndex()).toBe(-1); expect(menuCtrl.close).toHaveBeenCalledWith(true, { closeAll: true }); })); describe('ARIA', function() { it('sets role="menubar" on the menubar', function() { var menuBar = setup(); var ariaRole = menuBar[0].getAttribute('role'); expect(ariaRole).toBe('menubar'); }); it('should set the role on the menu trigger correctly', inject(function($compile, $rootScope) { var el = $compile( '<md-menu-bar>' + '<md-menu ng-repeat="i in [1, 2, 3]">' + '<md-button id="triggerButton" ng-click="lastClicked = $index"></md-button>' + '<md-menu-content></md-menu-content>' + '</md-menu>' + '</md-menu-bar>' )($rootScope); $rootScope.$digest(); expect(el[0].querySelector('#triggerButton').getAttribute('role')).toBe('menuitem'); })); }); describe('nested menus', function() { var menuBar, menus, subMenuOpen, ctrl; it('opens consecutive nested menus', function() { menuBar = setup(); ctrl = menuBar.controller('mdMenuBar'); menus = menuBar[0].querySelectorAll('md-menu md-menu'); angular.element(document.body).append(menuBar); // Open the menu-bar menu ctrl.focusMenu(1); ctrl.openFocusedMenu(); waitForMenuOpen(); // Open the first nested menu openSubMenu(0); waitForMenuOpen(); expect(getOpenSubMenu().text().trim()).toBe('Sub 1 - Content'); // Open the second nested menu, the first menu should close openSubMenu(1); waitForMenuClose(); // Then the second menu should become visible waitForMenuOpen(); expect(getOpenSubMenu().text().trim()).toBe('Sub 2 - Content'); menuBar.remove(); }); function openSubMenu(index) { // If a menu is already open, trigger the mouse leave to close it if (subMenuOpen) { subMenuOpen.triggerHandler({ type: 'mouseleave', target: subMenuOpen[0], currentTarget: subMenuOpen[0] }); } // Set the currently open sub-menu and trigger the mouse enter subMenuOpen = angular.element(menus[index]); subMenuOpen.triggerHandler({ type: 'mouseenter', target: subMenuOpen[0], currentTarget: subMenuOpen[0] }); } function getOpenSubMenu() { var containers = document.body.querySelectorAll('.md-open-menu-container.md-active'); var lastContainer = containers.item(containers.length - 1); return angular.element(lastContainer.querySelector('md-menu-content')); } function setup(){ var el; inject(function($compile, $rootScope) { el = $compile([ '<md-menu-bar>', ' <md-menu>', ' <md-menu-item>', ' <button ng-click="clicked=true">Button {{i}}</button>', ' </md-menu-item>', ' <md-menu-content class="test-submenu">', ' <md-menu ng-repeat="i in [1, 2]">', ' <md-menu-item>', ' <button ng-click="subclicked=true">Sub Button{{i}}</button>', ' </md-menu-item>', ' <md-menu-content>Sub {{i}} - Content</md-menu-content>', ' </md-menu>', ' </md-menu-content>', ' </md-menu>', '</md-menu-bar>' ].join(''))($rootScope); $rootScope.$digest(); }); attachedMenuElements.push(el); return el; } }); }); describe('MenuBarCtrl', function() { var menuBar, ctrl; beforeEach(function() { menuBar = setup(); ctrl = menuBar.controller('mdMenuBar'); }); describe('#getMenus', function() { it('gets the menus in the menubar', function() { var menus = ctrl.getMenus(); expect(Array.isArray(menus)).toBe(true); expect(menus.length).toBe(3); expect(menus[0].nodeName).toBe('MD-MENU'); }); }); describe('#getFocusedMenuIndex', function() { it('gets the focused menu index', function() { var menus = ctrl.getMenus(); ctrl.$document = [{ activeElement: menus[1].querySelector('button') }]; expect(ctrl.getFocusedMenuIndex()).toBe(1); }); }); describe('#getFocusedMenu', function() { it('gets the menu at the focused index', function() { var menus = [{}, {}, {}]; spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(1); spyOn(ctrl, 'getMenus').and.returnValue(menus); expect(ctrl.getFocusedMenu()).toBe(menus[1]); }); }); describe('#focusMenu', function() { var focused; beforeEach(function() { focused = false; }); it('focuses the first menu if none is focused', function() { var menus = mockButtonAtIndex(0); spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(-1); spyOn(ctrl, 'getMenus').and.returnValue(menus); ctrl.focusMenu(1); expect(focused).toBe(true); }); it('focuses the next menu', function() { var menus = mockButtonAtIndex(1); spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(0); spyOn(ctrl, 'getMenus').and.returnValue(menus); ctrl.focusMenu(1); expect(focused).toBe(true); }); it('focuses the previous menu', function() { var menus = mockButtonAtIndex(1); spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(2); spyOn(ctrl, 'getMenus').and.returnValue(menus); ctrl.focusMenu(-1); expect(focused).toBe(true); }); it('does not focus prev at the start of the array', function() { var menus = mockButtonAtIndex(0); spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(0); spyOn(ctrl, 'getMenus').and.returnValue(menus); ctrl.focusMenu(-1); expect(focused).toBe(false); }); it('does not focus next at the end of the array', function() { var menus = mockButtonAtIndex(2); spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(2); spyOn(ctrl, 'getMenus').and.returnValue(menus); ctrl.focusMenu(1); expect(focused).toBe(false); }); function mockButtonAtIndex(index) { var result = []; var mockButton = { querySelector: function() { return { focus: function() { focused = true; } }; }, // TODO: This may need to become more complex if more of the tests use it classList: { contains: function() { return false; } } }; for (var i = 0; i < 3; ++i) { if (i == index) { result.push(mockButton); } else { result.push({ classList: mockButton.classList }); } } return result; } }); describe('#openFocusedMenu', function() { it('clicks the focused menu', function() { var opened = false; spyOn(ctrl, 'getFocusedMenu').and.returnValue({ querySelector: function() { return true; } }); spyOn(angular, 'element').and.returnValue({ controller: function() { return { open: function() { opened = true; } }; } }); ctrl.openFocusedMenu(); expect(opened).toBe(true); }); }); describe('#handleKeyDown', function() { var keyCodes, call, called, calledWith; beforeEach(inject(function($injector) { var $mdConstant = $injector.get('$mdConstant'); keyCodes = $mdConstant.KEY_CODE; called = false; call = function(arg) { called = true; calledWith = arg; }; })); describe('DOWN_ARROW', function() { it('opens the currently focused menu', function() { spyOn(ctrl, 'openFocusedMenu').and.callFake(call); ctrl.handleKeyDown({ keyCode: keyCodes.DOWN_ARROW }); expect(called).toBe(true); }); }); describe('RIGHT_ARROW', function() { it('focuses the next menu', function() { spyOn(ctrl, 'focusMenu').and.callFake(call); ctrl.handleKeyDown({ keyCode: keyCodes.RIGHT_ARROW }); expect(called).toBe(true); expect(calledWith).toBe(1); }); }); describe('LEFT_ARROW', function() { it('focuses the previous menu', function() { spyOn(ctrl, 'focusMenu').and.callFake(call); ctrl.handleKeyDown({ keyCode: keyCodes.LEFT_ARROW }); expect(called).toBe(true); expect(calledWith).toBe(-1); }); }); }); }); function setup() { var el; inject(function($compile, $rootScope) { el = $compile([ '<md-menu-bar>', '<md-menu ng-repeat="i in [1, 2, 3]">', '<button ng-click="lastClicked = $index"></button>', '<md-menu-content></md-menu-content>', '</md-menu>', '</md-menu-bar>' ].join(''))($rootScope); $rootScope.$digest(); }); attachedMenuElements.push(el); return el; } }); describe('md-menu-divider directive', function() { it('sets role="separator"', function() { var el = setup(); expect(el.attr('role')).toBe('separator'); }); function setup() { var divider; inject(function($compile, $rootScope) { divider = $compile('<md-menu-divider></md-menu-divider>')($rootScope); }); return divider; } }); describe('md-menu-item directive', function() { describe('type="checkbox"', function() { function setup(attrs) { return setupMenuItem(attrs + ' type="checkbox"'); } it('compiles', function() { var menuItem = setup('ng-model="test"')[0]; expect(menuItem.classList.contains('md-indent')).toBe(true); var children = menuItem.children; expect(children[0].nodeName).toBe('MD-ICON'); expect(children[1].nodeName).toBe('MD-BUTTON'); }); it('compiles with ng-repeat', function() { var menuItem = setup('ng-repeat="i in [1, 2, 3]"')[0]; expect(menuItem.classList.contains('md-indent')).toBe(true); var children = menuItem.children; expect(children[0].nodeName).toBe('MD-ICON'); expect(children[1].nodeName).toBe('MD-BUTTON'); }); it('sets aria role', function() { var menuItem = setup()[0].querySelector('md-button'); expect(menuItem.getAttribute('role')).toBe('menuitemcheckbox'); }); it('toggles on click', function() { var menuItem = setup('ng-model="test"')[0]; var button = menuItem.querySelector('md-button'); button.click(); inject(function($rootScope) { expect($rootScope.test).toBe(true); }); }); it('does not toggle if disabled', function() { var menuItem = setup('ng-model="test" ng-disabled="true"')[0]; var button = menuItem.querySelector('md-button'); button.click(); inject(function($rootScope) { expect($rootScope.test).toBeFalsy(); }); }); it('reflects the ng-model value', inject(function($rootScope) { var menuItem = setup('ng-model="test"')[0]; var button = menuItem.querySelector('md-button'); expect(button.getAttribute('aria-checked')).toBe('false'); expect(menuItem.children[0].style.display).toBe('none'); $rootScope.test = true; $rootScope.$digest(); expect(menuItem.children[0].style.display).toBe(''); expect(button.getAttribute('aria-checked')).toBe('true'); })); }); describe('type="radio"', function() { function setup(attrs) { return setupMenuItem(attrs + ' type="radio"'); } it('compiles', function() { var menuItem = setup('ng-model="test"')[0]; expect(menuItem.classList.contains('md-indent')).toBe(true); var children = menuItem.children; expect(children[0].nodeName).toBe('MD-ICON'); expect(children[1].nodeName).toBe('MD-BUTTON'); }); it('compiles with ng-repeat', function() { var menuItem = setup('ng-repeat="i in [1, 2, 3]"')[0]; expect(menuItem.classList.contains('md-indent')).toBe(true); var children = menuItem.children; expect(children[0].nodeName).toBe('MD-ICON'); expect(children[1].nodeName).toBe('MD-BUTTON'); }); it('sets aria role', function() { var menuItem = setup()[0].querySelector('md-button'); expect(menuItem.getAttribute('role')).toBe('menuitemradio'); }); it('toggles on click', function() { var menuItem = setup('ng-model="test" value="hello"')[0]; var button = menuItem.querySelector('md-button'); button.click(); inject(function($rootScope) { expect($rootScope.test).toBe("hello"); }); }); it('does not toggle if disabled', function() { var menuItem = setup('ng-model="test" value="hello" ng-disabled="true"')[0]; var button = menuItem.querySelector('md-button'); button.click(); inject(function($rootScope) { expect($rootScope.test).toBeFalsy(); }); }); it('reflects the ng-model value', inject(function($rootScope) { $rootScope.test = 'apple'; var menuItem = setup('ng-model="test" value="hello"')[0]; var button = menuItem.querySelector('md-button'); expect(button.getAttribute('aria-checked')).toBe('false'); expect(menuItem.children[0].style.display).toBe('none'); $rootScope.test = 'hello'; $rootScope.$digest(); expect(menuItem.children[0].style.display).toBeFalsy(); expect(button.getAttribute('aria-checked')).toBe('true'); })); }); function setupMenuItem(attrs) { // We need to have a `md-menu-bar` as a parent of our menu item, because the menu-item // is only wrapping and indenting the content if it's inside of a menu bar. var menuBar; var template = '<md-menu-bar>' + '<md-menu-item ' + (attrs = attrs || '') + '>Test Item</md-menu-item>' + '</md-menu-bar>'; inject(function($compile, $rootScope) { menuBar = $compile(template)($rootScope); $rootScope.$digest(); }); return menuBar.find('md-menu-item'); } }); function waitForMenuOpen() { inject(function($material) { $material.flushInterimElement(); }); } function waitForMenuClose() { inject(function($material) { $material.flushInterimElement(); }); } });