UNPKG

accessibility-developer-tools

Version:

This is a library of accessibility-related testing and utility code.

1,560 lines (1,337 loc) 93.5 kB
// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.ui.ControlTest'); goog.setTestOnly('goog.ui.ControlTest'); goog.require('goog.a11y.aria'); goog.require('goog.a11y.aria.State'); goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.dom.classlist'); goog.require('goog.events'); goog.require('goog.events.BrowserEvent'); goog.require('goog.events.KeyCodes'); goog.require('goog.html.testing'); goog.require('goog.object'); goog.require('goog.string'); goog.require('goog.style'); goog.require('goog.testing.ExpectedFailures'); goog.require('goog.testing.events'); goog.require('goog.testing.events.Event'); goog.require('goog.testing.jsunit'); goog.require('goog.ui.Component'); goog.require('goog.ui.Control'); goog.require('goog.ui.ControlRenderer'); goog.require('goog.ui.registry'); goog.require('goog.userAgent'); // Disabled due to problems on farm. var testFocus = false; var control; var ALL_EVENTS = goog.object.getValues(goog.ui.Component.EventType); var events = {}; var expectedFailures; var sandbox; var aria = goog.a11y.aria; var State = goog.a11y.aria.State; function setUpPage() { expectedFailures = new goog.testing.ExpectedFailures(); sandbox = document.getElementById('sandbox'); } /** * A dummy renderer, for testing purposes. * @constructor * @extends {goog.ui.ControlRenderer} */ function TestRenderer() { goog.ui.ControlRenderer.call(this); } goog.inherits(TestRenderer, goog.ui.ControlRenderer); /** * Initializes the testcase prior to execution. */ function setUp() { control = new goog.ui.Control('Hello'); control.setDispatchTransitionEvents(goog.ui.Component.State.ALL, true); goog.events.listen(control, ALL_EVENTS, countEvent); } /** * Cleans up after executing the testcase. */ function tearDown() { control.dispose(); expectedFailures.handleTearDown(); goog.dom.removeChildren(sandbox); resetEventCount(); } /** * Resets the global counter for events dispatched by test objects. */ function resetEventCount() { goog.object.clear(events); } /** * Increments the global counter for events of this type. * @param {goog.events.Event} e Event to count. */ function countEvent(e) { var type = e.type; var target = e.target; if (!events[target]) { events[target] = {}; } if (events[target][type]) { events[target][type]++; } else { events[target][type] = 1; } } /** * Returns the number of times test objects dispatched events of the given * type since the global counter was last reset. * @param {goog.ui.Control} target Event target. * @param {string} type Event type. * @return {number} Number of events of this type. */ function getEventCount(target, type) { return events[target] && events[target][type] || 0; } /** * Returns true if no events were dispatched since the last reset. * @return {boolean} Whether no events have been dispatched since the last * reset. */ function noEventsDispatched() { return !events || goog.object.isEmpty(events); } /** * Returns the number of event listeners created by the control. * @param {goog.ui.Control} control Control whose event listers are to be * counted. * @return {number} Number of event listeners. */ function getListenerCount(control) { return control.googUiComponentHandler_ ? goog.object.getCount(control.getHandler().keys_) : 0; } /** * Simulates a mousedown event on the given element, including focusing it. * @param {Element} element Element on which to simulate mousedown. * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button; * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}. * @return {boolean} Whether the event was allowed to proceed. */ function fireMouseDownAndFocus(element, opt_button) { var result = goog.testing.events.fireMouseDownEvent(element, opt_button); if (result) { // Browsers move focus for all buttons, not just the left button. element.focus(); } return result; } /** * @return {boolean} Whether we're on Mac Safari 3.x. */ function isMacSafari3() { return goog.userAgent.WEBKIT && goog.userAgent.MAC && !goog.userAgent.isVersionOrHigher('527'); } /** * Tests the {@link goog.ui.Control} constructor. */ function testConstructor() { assertNotNull('Constructed control must not be null', control); assertEquals( 'Content must have expected value', 'Hello', control.getContent()); assertEquals( 'Renderer must default to the registered renderer', goog.ui.registry.getDefaultRenderer(goog.ui.Control), control.getRenderer()); var content = goog.dom.createDom( goog.dom.TagName.DIV, null, 'Hello', goog.dom.createDom(goog.dom.TagName.B, null, 'World')); var testRenderer = new TestRenderer(); var fakeDomHelper = {}; var foo = new goog.ui.Control(content, testRenderer, fakeDomHelper); assertNotNull('Constructed object must not be null', foo); assertEquals('Content must have expected value', content, foo.getContent()); assertEquals( 'Renderer must have expected value', testRenderer, foo.getRenderer()); assertEquals( 'DOM helper must have expected value', fakeDomHelper, foo.getDomHelper()); foo.dispose(); } /** * Tests {@link goog.ui.Control#getHandler}. */ function testGetHandler() { assertUndefined( 'Event handler must be undefined before getHandler() ' + 'is called', control.googUiComponentHandler_); var handler = control.getHandler(); assertNotNull('Event handler must not be null', handler); assertEquals( 'getHandler() must return the same instance if called again', handler, control.getHandler()); } /** * Tests {@link goog.ui.Control#isHandleMouseEvents}. */ function testIsHandleMouseEvents() { assertTrue( 'Controls must handle their own mouse events by default', control.isHandleMouseEvents()); } /** * Tests {@link goog.ui.Control#setHandleMouseEvents}. */ function testSetHandleMouseEvents() { assertTrue( 'Control must handle its own mouse events by default', control.isHandleMouseEvents()); control.setHandleMouseEvents(false); assertFalse( 'Control must no longer handle its own mouse events', control.isHandleMouseEvents()); control.setHandleMouseEvents(true); assertTrue( 'Control must once again handle its own mouse events', control.isHandleMouseEvents()); control.render(sandbox); assertTrue( 'Rendered control must handle its own mouse events', control.isHandleMouseEvents()); control.setHandleMouseEvents(false); assertFalse( 'Rendered control must no longer handle its own mouse events', control.isHandleMouseEvents()); control.setHandleMouseEvents(true); assertTrue( 'Rendered control must once again handle its own mouse events', control.isHandleMouseEvents()); } /** * Tests {@link goog.ui.Control#getKeyEventTarget}. */ function testGetKeyEventTarget() { assertNull( 'Key event target of control without DOM must be null', control.getKeyEventTarget()); control.createDom(); assertEquals( 'Key event target of control with DOM must be its element', control.getElement(), control.getKeyEventTarget()); } /** * Tests {@link goog.ui.Control#getKeyHandler}. */ function testGetKeyHandler() { assertUndefined( 'Key handler must be undefined before getKeyHandler() ' + 'is called', control.keyHandler_); var keyHandler = control.getKeyHandler(); assertNotNull('Key handler must not be null', keyHandler); assertEquals( 'getKeyHandler() must return the same instance if called ' + 'again', keyHandler, control.getKeyHandler()); } /** * Tests {@link goog.ui.Control#getRenderer}. */ function testGetRenderer() { assertEquals( 'Renderer must be the default registered renderer', goog.ui.registry.getDefaultRenderer(goog.ui.Control), control.getRenderer()); } /** * Tests {@link goog.ui.Control#setRenderer}. */ function testSetRenderer() { control.createDom(); assertNotNull('Control must have a DOM', control.getElement()); assertFalse('Control must not be in the document', control.isInDocument()); assertEquals( 'Renderer must be the default registered renderer', goog.ui.registry.getDefaultRenderer(goog.ui.Control), control.getRenderer()); var testRenderer = new TestRenderer(); control.setRenderer(testRenderer); assertNull( 'Control must not have a DOM after its renderer is reset', control.getElement()); assertFalse( 'Control still must not be in the document', control.isInDocument()); assertEquals( 'Renderer must have expected value', testRenderer, control.getRenderer()); control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); assertThrows( 'Resetting the renderer after the control has entered ' + 'the document must throw error', function() { control.setRenderer({}); }); } /** * Tests {@link goog.ui.Control#getExtraClassNames}. */ function testGetExtraClassNames() { assertNull( 'Control must not have any extra class names by default', control.getExtraClassNames()); } /** * Tests {@link goog.ui.Control#addExtraClassName} and * {@link goog.ui.Control#removeExtraClassName}. */ function testAddRemoveClassName() { assertNull( 'Control must not have any extra class names by default', control.getExtraClassNames()); control.addClassName('foo'); assertArrayEquals( 'Control must have expected extra class names', ['foo'], control.getExtraClassNames()); assertNull('Control must not have a DOM', control.getElement()); control.createDom(); assertSameElements( 'Control\'s element must have expected class names', ['goog-control', 'foo'], goog.dom.classlist.get(control.getElement())); control.addClassName('bar'); assertArrayEquals( 'Control must have expected extra class names', ['foo', 'bar'], control.getExtraClassNames()); assertSameElements( 'Control\'s element must have expected class names', ['goog-control', 'foo', 'bar'], goog.dom.classlist.get(control.getElement())); control.addClassName('bar'); assertArrayEquals( 'Adding the same class name again must be a no-op', ['foo', 'bar'], control.getExtraClassNames()); assertSameElements( 'Adding the same class name again must be a no-op', ['goog-control', 'foo', 'bar'], goog.dom.classlist.get(control.getElement())); control.addClassName(null); assertArrayEquals( 'Adding null class name must be a no-op', ['foo', 'bar'], control.getExtraClassNames()); assertSameElements( 'Adding null class name must be a no-op', ['goog-control', 'foo', 'bar'], goog.dom.classlist.get(control.getElement())); control.removeClassName(null); assertArrayEquals( 'Removing null class name must be a no-op', ['foo', 'bar'], control.getExtraClassNames()); assertSameElements( 'Removing null class name must be a no-op', ['goog-control', 'foo', 'bar'], goog.dom.classlist.get(control.getElement())); control.removeClassName('foo'); assertArrayEquals( 'Control must have expected extra class names', ['bar'], control.getExtraClassNames()); assertSameElements( 'Control\'s element must have expected class names', ['goog-control', 'bar'], goog.dom.classlist.get(control.getElement())); control.removeClassName('bar'); assertNull( 'Control must not have any extra class names', control.getExtraClassNames()); assertSameElements( 'Control\'s element must have expected class names', ['goog-control'], goog.dom.classlist.get(control.getElement())); } /** * Tests {@link goog.ui.Control#enableClassName}. */ function testEnableClassName() { assertNull( 'Control must not have any extra class names by default', control.getExtraClassNames()); control.enableClassName('foo', true); assertArrayEquals( 'Control must have expected extra class names', ['foo'], control.getExtraClassNames()); control.enableClassName('bar', true); assertArrayEquals( 'Control must have expected extra class names', ['foo', 'bar'], control.getExtraClassNames()); control.enableClassName('bar', true); assertArrayEquals( 'Enabling the same class name again must be a no-op', ['foo', 'bar'], control.getExtraClassNames()); control.enableClassName(null); assertArrayEquals( 'Enabling null class name must be a no-op', ['foo', 'bar'], control.getExtraClassNames()); control.enableClassName('foo', false); assertArrayEquals( 'Control must have expected extra class names', ['bar'], control.getExtraClassNames()); control.enableClassName('bar', false); assertNull( 'Control must not have any extra class names', control.getExtraClassNames()); } /** * Tests {@link goog.ui.Control#createDom}. */ function testCreateDom() { assertNull('Control must not have a DOM by default', control.getElement()); assertFalse( 'Control must not allow text selection by default', control.isAllowTextSelection()); assertTrue('Control must be visible by default', control.isVisible()); control.createDom(); assertNotNull('Control must have a DOM', control.getElement()); assertTrue( 'Control\'s element must be unselectable', goog.style.isUnselectable(control.getElement())); assertTrue( 'Control\'s element must be visible', control.getElement().style.display != 'none'); control.setAllowTextSelection(true); control.createDom(); assertFalse( 'Control\'s element must be selectable', goog.style.isUnselectable(control.getElement())); control.setVisible(false); control.createDom(); assertTrue( 'Control\'s element must be hidden', control.getElement().style.display == 'none'); } /** * Tests {@link goog.ui.Control#getContentElement}. */ function testGetContentElement() { assertNull( 'Unrendered control must not have a content element', control.getContentElement()); control.createDom(); assertEquals( 'Control\'s content element must equal its root element', control.getElement(), control.getContentElement()); } /** * Tests {@link goog.ui.Control#canDecorate}. */ function testCanDecorate() { assertTrue(control.canDecorate(goog.dom.createElement(goog.dom.TagName.DIV))); } /** * Tests {@link goog.ui.Control#decorateInternal}. */ function testDecorateInternal() { sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>'; var foo = goog.dom.getElement('foo'); control.decorate(foo); assertEquals( 'Decorated control\'s element must have expected value', foo, control.getElement()); assertTrue( 'Element must be unselectable', goog.style.isUnselectable(control.getElement())); assertTrue( 'Element must be visible', control.getElement().style.display != 'none'); } /** * Tests {@link goog.ui.Control#decorateInternal} with a control that * allows text selection. */ function testDecorateInternalForSelectableControl() { sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>'; var foo = goog.dom.getElement('foo'); control.setAllowTextSelection(true); control.decorate(foo); assertEquals( 'Decorated control\'s element must have expected value', foo, control.getElement()); assertFalse( 'Element must be selectable', goog.style.isUnselectable(control.getElement())); assertTrue('Control must be visible', control.isVisible()); } /** * Tests {@link goog.ui.Control#decorateInternal} with a hidden element. */ function testDecorateInternalForHiddenElement() { sandbox.innerHTML = '<div id="foo" style="display:none">Hello!</div>'; var foo = goog.dom.getElement('foo'); control.decorate(foo); assertEquals( 'Decorated control\'s element must have expected value', foo, control.getElement()); assertTrue( 'Element must be unselectable', goog.style.isUnselectable(control.getElement())); assertFalse('Control must be hidden', control.isVisible()); } /** * Tests {@link goog.ui.Control#enterDocument}. */ function testEnterDocument() { control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9)) { assertEquals( 'Control must have 5 mouse & 3 key event listeners on IE8', 8, getListenerCount(control)); } else { assertEquals( 'Control must have 4 mouse and 3 key event listeners', 7, getListenerCount(control)); } assertEquals( 'Control\'s key event handler must be attached to its ' + 'key event target', control.getKeyEventTarget(), control.getKeyHandler().element_); } /** * Tests {@link goog.ui.Control#enterDocument} for a control that doesn't * handle mouse events. */ function testEnterDocumentForControlWithoutMouseHandling() { control.setHandleMouseEvents(false); control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); assertEquals( 'Control must have 3 key event listeners', 3, getListenerCount(control)); assertEquals( 'Control\'s key event handler must be attached to its ' + 'key event target', control.getKeyEventTarget(), control.getKeyHandler().element_); } /** * Tests {@link goog.ui.Control#enterDocument} for a control that isn't * focusable. */ function testEnterDocumentForNonFocusableControl() { control.setSupportedState(goog.ui.Component.State.FOCUSED, false); control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9)) { assertEquals( 'Control must have 5 mouse event listeners on IE8', 5, getListenerCount(control)); } else { assertEquals( 'Control must have 4 mouse event listeners', 4, getListenerCount(control)); } assertUndefined( 'Control must not have a key event handler', control.keyHandler_); } /** * Tests {@link goog.ui.Control#enterDocument} for a control that doesn't * need to do any event handling. */ function testEnterDocumentForControlWithoutEventHandlers() { control.setHandleMouseEvents(false); control.setSupportedState(goog.ui.Component.State.FOCUSED, false); control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); assertEquals( 'Control must have 0 event listeners', 0, getListenerCount(control)); assertUndefined( 'Control must not have an event handler', control.googUiComponentHandler_); assertUndefined( 'Control must not have a key event handler', control.keyHandler_); } /** * Tests {@link goog.ui.Control#exitDocument}. */ function testExitDocument() { control.render(sandbox); assertTrue('Control must be in the document', control.isInDocument()); if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9)) { assertEquals( 'Control must have 5 mouse & 3 key event listeners on IE8', 8, getListenerCount(control)); } else { assertEquals( 'Control must have 4 mouse and 3 key event listeners', 7, getListenerCount(control)); } assertEquals( 'Control\'s key event handler must be attached to its ' + 'key event target', control.getKeyEventTarget(), control.getKeyHandler().element_); // Expected to fail on Mac Safari prior to version 527. expectedFailures.expectFailureFor(isMacSafari3()); try { assertTrue( 'Control\'s element must support keyboard focus', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); } catch (e) { expectedFailures.handleException(e); } control.exitDocument(); assertFalse( 'Control must no longer be in the document', control.isInDocument()); assertEquals( 'Control must have no event listeners', 0, getListenerCount(control)); assertNull( 'Control\'s key event handler must be unattached', control.getKeyHandler().element_); assertFalse( 'Control\'s element must no longer support keyboard focus', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); } /** * Tests {@link goog.ui.Control#dispose}. */ function testDispose() { control.render(sandbox); var handler = control.getHandler(); var keyHandler = control.getKeyHandler(); control.dispose(); assertFalse( 'Control must no longer be in the document', control.isInDocument()); assertTrue('Control must have been disposed of', control.isDisposed()); assertUndefined('Renderer must have been deleted', control.getRenderer()); assertNull('Content must be nulled out', control.getContent()); assertTrue('Event handler must have been disposed of', handler.isDisposed()); assertUndefined( 'Event handler must have been deleted', control.googUiComponentHandler_); assertTrue('Key handler must have been disposed of', keyHandler.isDisposed()); assertUndefined('Key handler must have been deleted', control.keyHandler_); assertNull( 'Extra class names must have been nulled out', control.getExtraClassNames()); } /** * Tests {@link goog.ui.Control#getContent}. */ function testGetContent() { assertNull( 'Empty control must have null content', (new goog.ui.Control(null)).getContent()); assertEquals( 'Control must have expected content', 'Hello', control.getContent()); control.render(sandbox); assertEquals( 'Control must have expected content after rendering', 'Hello', control.getContent()); } /** * Tests {@link goog.ui.Control#getContent}. */ function testGetContentForDecoratedControl() { sandbox.innerHTML = '<div id="empty"></div>\n' + '<div id="text">Hello, world!</div>\n' + '<div id="element"><span>Foo</span></div>\n' + '<div id="nodelist">Hello, <b>world</b>!</div>\n'; var empty = new goog.ui.Control(null); empty.decorate(goog.dom.getElement('empty')); assertNull( 'Content of control decorating empty DIV must be null', empty.getContent()); empty.dispose(); var text = new goog.ui.Control(null); text.decorate(goog.dom.getElement('text')); assertEquals( 'Content of control decorating DIV with text contents ' + 'must be as expected', 'Hello, world!', text.getContent().nodeValue); text.dispose(); var element = new goog.ui.Control(null); element.decorate(goog.dom.getElement('element')); assertEquals( 'Content of control decorating DIV with element child ' + 'must be as expected', goog.dom.getElement('element').firstChild, element.getContent()); element.dispose(); var nodelist = new goog.ui.Control(null); nodelist.decorate(goog.dom.getElement('nodelist')); assertSameElements( 'Content of control decorating DIV with mixed ' + 'contents must be as expected', goog.dom.getElement('nodelist').childNodes, nodelist.getContent()); nodelist.dispose(); } /** * Tests {@link goog.ui.Control#setAriaLabel}. */ function testSetAriaLabel_render() { assertNull( 'Controls must not have any aria label by default', control.getAriaLabel()); control.setAriaLabel('label'); assertEquals('Control must have aria label', 'label', control.getAriaLabel()); control.render(sandbox); var elem = control.getElementStrict(); assertEquals( 'Element must have control\'s aria label after rendering', 'label', goog.a11y.aria.getLabel(elem)); control.setAriaLabel('new label'); assertEquals( 'Element must have the new aria label', 'new label', goog.a11y.aria.getLabel(elem)); } /** * Tests {@link goog.ui.Control#setAriaLabel}. */ function testSetAriaLabel_decorate() { assertNull( 'Controls must not have any aria label by default', control.getAriaLabel()); control.setAriaLabel('label'); assertEquals('Control must have aria label', 'label', control.getAriaLabel()); sandbox.innerHTML = '<div id="nodelist" role="button">' + 'Hello, <b>world</b>!</div>'; control.decorate(goog.dom.getElement('nodelist')); var elem = control.getElementStrict(); assertEquals( 'Element must have control\'s aria label after rendering', 'label', goog.a11y.aria.getLabel(elem)); assertEquals( 'Element must have the correct role', 'button', elem.getAttribute('role')); control.setAriaLabel('new label'); assertEquals( 'Element must have the new aria label', 'new label', goog.a11y.aria.getLabel(elem)); } /** * Tests {@link goog.ui.Control#setContent}. */ function testSetContent() { control.setContent('Bye'); assertEquals( 'Unrendered control control must have expected contents', 'Bye', control.getContent()); assertNull('No DOM must be created by setContent', control.getElement()); control.createDom(); assertEquals( 'Rendered control\'s DOM must have expected contents', 'Bye', control.getElement().innerHTML); control.setContent(null); assertNull( 'Rendered control must have expected contents', control.getContent()); assertEquals( 'Rendered control\'s DOM must have expected contents', '', control.getElement().innerHTML); control.setContent([ goog.dom.createDom( goog.dom.TagName.DIV, null, goog.dom.createDom(goog.dom.TagName.SPAN, null, 'Hello')), 'World' ]); assertHTMLEquals( 'Control\'s DOM must be updated', '<div><span>Hello</span></div>World', control.getElement().innerHTML); } /** * Tests {@link goog.ui.Control#setContentInternal}. */ function testSetContentInternal() { control.render(sandbox); assertEquals( 'Control must have expected content after rendering', 'Hello', control.getContent()); control.setContentInternal('Bye'); assertEquals( 'Control must have expected contents', 'Bye', control.getContent()); assertEquals( 'Control\'s DOM must be unchanged', 'Hello', control.getElement().innerHTML); } /** * Tests {@link goog.ui.Control#getCaption}. */ function testGetCaption() { assertEquals( 'Empty control\'s caption must be empty string', '', (new goog.ui.Control(null)).getCaption()); assertEquals( 'Caption must have expected value', 'Hello', control.getCaption()); sandbox.innerHTML = '<div id="nodelist">Hello, <b>world</b>!</div>'; control.decorate(goog.dom.getElement('nodelist')); assertEquals( 'Caption must have expected value', 'Hello, world!', control.getCaption()); var arrayContent = goog.array.clone( goog.dom.safeHtmlToNode( goog.html.testing.newSafeHtmlForTest( ' <b> foo</b><i> bar</i> ')).childNodes); control.setContent(arrayContent); assertEquals( 'whitespaces must be normalized in the caption', 'foo bar', control.getCaption()); control.setContent('\xa0foo'); assertEquals( 'indenting spaces must be kept', '\xa0foo', control.getCaption()); } /** * Tests {@link goog.ui.Control#setCaption}. */ function testSetCaption() { control.setCaption('Hello, world!'); assertEquals( 'Control must have a string caption "Hello, world!"', 'Hello, world!', control.getCaption()); } /** * Tests {@link goog.ui.Control#setRightToLeft}. */ function testSetRightToLeft() { control.createDom(); assertFalse( 'Control\'s element must not have right-to-left class', goog.dom.classlist.contains(control.getElement(), 'goog-control-rtl')); control.setRightToLeft(true); assertTrue( 'Control\'s element must have right-to-left class', goog.dom.classlist.contains(control.getElement(), 'goog-control-rtl')); control.render(sandbox); assertThrows( 'Changing the render direction of a control already in ' + 'the document is an error', function() { control.setRightToLeft(false); }); } /** * Tests {@link goog.ui.Control#isAllowTextSelection}. */ function testIsAllowTextSelection() { assertFalse( 'Controls must not allow text selection by default', control.isAllowTextSelection()); } /** * Tests {@link goog.ui.Control#setAllowTextSelection}. */ function testSetAllowTextSelection() { assertFalse( 'Controls must not allow text selection by default', control.isAllowTextSelection()); control.setAllowTextSelection(true); assertTrue( 'Control must allow text selection', control.isAllowTextSelection()); control.setAllowTextSelection(false); assertFalse( 'Control must no longer allow text selection', control.isAllowTextSelection()); control.render(sandbox); assertFalse( 'Control must not allow text selection even after rendered', control.isAllowTextSelection()); control.setAllowTextSelection(true); assertTrue( 'Control must once again allow text selection', control.isAllowTextSelection()); } /** * Tests {@link goog.ui.Control#isVisible}. */ function testIsVisible() { assertTrue('Controls must be visible by default', control.isVisible()); } /** * Tests {@link goog.ui.Control#setVisible} before it is rendered. */ function testSetVisible() { assertFalse( 'setVisible(true) must return false if already visible', control.setVisible(true)); assertTrue('No events must have been dispatched', noEventsDispatched()); assertTrue( 'setVisible(false) must return true if previously visible', control.setVisible(false)); assertEquals( 'One HIDE event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); assertFalse('Control must no longer be visible', control.isVisible()); assertTrue( 'setVisible(true) must return true if previously hidden', control.setVisible(true)); assertEquals( 'One SHOW event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.SHOW)); assertTrue('Control must be visible', control.isVisible()); } /** * Tests {@link goog.ui.Control#setVisible} after it is rendered. */ function testSetVisibleForRenderedControl() { control.render(sandbox); assertTrue( 'No events must have been dispatched during rendering', noEventsDispatched()); assertFalse( 'setVisible(true) must return false if already visible', control.setVisible(true)); assertTrue('No events must have been dispatched', noEventsDispatched()); assertTrue( 'Control\'s element must be visible', control.getElement().style.display != 'none'); assertTrue( 'setVisible(false) must return true if previously visible', control.setVisible(false)); assertEquals( 'One HIDE event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); assertFalse('Control must no longer be visible', control.isVisible()); assertTrue( 'Control\'s element must be hidden', control.getElement().style.display == 'none'); assertTrue( 'setVisible(true) must return true if previously hidden', control.setVisible(true)); assertEquals( 'One SHOW event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.SHOW)); assertTrue('Control must be visible', control.isVisible()); assertTrue( 'Control\'s element must be visible', control.getElement().style.display != 'none'); } /** * Tests {@link goog.ui.Control#setVisible} for disabled non-focusable * controls. */ function testSetVisibleForDisabledNonFocusableControl() { // Hidden, disabled, non-focusable control becoming visible. control.setEnabled(false); control.setSupportedState(goog.ui.Component.State.FOCUSED, false); control.render(sandbox); assertTrue('Control must be visible', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); // Visible, disabled, non-focusable control becoming hidden. control.getKeyEventTarget().focus(); assertEquals( 'Control must not have dispatched FOCUS', 0, getEventCount(control, goog.ui.Component.EventType.FOCUS)); assertFalse('Control must not have keyboard focus', control.isFocused()); control.setVisible(false); assertFalse('Control must be hidden', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); assertEquals( 'Control must have dispatched HIDE', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); assertEquals( 'Control must not have dispatched BLUR', 0, getEventCount(control, goog.ui.Component.EventType.BLUR)); } /** * Tests {@link goog.ui.Control#setVisible} for disabled focusable controls. */ function testSetVisibleForDisabledFocusableControl() { // Hidden, disabled, focusable control becoming visible. control.setEnabled(false); control.setSupportedState(goog.ui.Component.State.FOCUSED, true); control.render(sandbox); assertTrue('Control must be visible', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); // Visible, disabled, focusable control becoming hidden. control.getKeyEventTarget().focus(); assertEquals( 'Control must not have dispatched FOCUS', 0, getEventCount(control, goog.ui.Component.EventType.FOCUS)); assertFalse('Control must not have keyboard focus', control.isFocused()); control.setVisible(false); assertFalse('Control must be hidden', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); assertEquals( 'Control must have dispatched HIDE', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); assertEquals( 'Control must not have dispatched BLUR', 0, getEventCount(control, goog.ui.Component.EventType.BLUR)); } /** * Tests {@link goog.ui.Control#setVisible} for enabled non-focusable * controls. */ function testSetVisibleForEnabledNonFocusableControl() { // Hidden, enabled, non-focusable control becoming visible. control.setEnabled(true); control.setSupportedState(goog.ui.Component.State.FOCUSED, false); control.render(sandbox); assertTrue('Control must be visible', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); if (testFocus) { // Visible, enabled, non-focusable control becoming hidden. control.getKeyEventTarget().focus(); assertEquals( 'Control must not have dispatched FOCUS', 0, getEventCount(control, goog.ui.Component.EventType.FOCUS)); assertFalse('Control must not have keyboard focus', control.isFocused()); control.setVisible(false); assertFalse('Control must be hidden', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); assertEquals( 'Control must have dispatched HIDE', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); assertEquals( 'Control must not have dispatched BLUR', 0, getEventCount(control, goog.ui.Component.EventType.BLUR)); } } /** * Tests {@link goog.ui.Control#setVisible} for enabled focusable controls. */ function testSetVisibleForEnabledFocusableControl() { // Hidden, enabled, focusable control becoming visible. control.setEnabled(true); control.setSupportedState(goog.ui.Component.State.FOCUSED, true); control.render(sandbox); assertTrue('Control must be visible', control.isVisible()); if (testFocus) { // Expected to fail on Mac Safari prior to version 527. expectedFailures.expectFailureFor(isMacSafari3()); try { // Mac Safari currently doesn't support tabIndex on arbitrary // elements. assertTrue( 'Control must have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); } catch (e) { expectedFailures.handleException(e); } // Visible, enabled, focusable control becoming hidden. control.getKeyEventTarget().focus(); // Expected to fail on IE. expectedFailures.expectFailureFor(goog.userAgent.IE); try { // IE dispatches focus and blur events asynchronously! assertEquals( 'Control must have dispatched FOCUS', 1, getEventCount(control, goog.ui.Component.EventType.FOCUS)); assertTrue('Control must have keyboard focus', control.isFocused()); } catch (e) { expectedFailures.handleException(e); } control.setVisible(false); assertFalse('Control must be hidden', control.isVisible()); assertFalse( 'Control must not have a tab index', goog.dom.isFocusableTabIndex(control.getKeyEventTarget())); assertEquals( 'Control must have dispatched HIDE', 1, getEventCount(control, goog.ui.Component.EventType.HIDE)); // Expected to fail on IE. expectedFailures.expectFailureFor(goog.userAgent.IE); try { // IE dispatches focus and blur events asynchronously! assertEquals( 'Control must have dispatched BLUR', 1, getEventCount(control, goog.ui.Component.EventType.BLUR)); assertFalse( 'Control must no longer have keyboard focus', control.isFocused()); } catch (e) { expectedFailures.handleException(e); } } } /** * Tests {@link goog.ui.Control#isEnabled}. */ function testIsEnabled() { assertTrue('Controls must be enabled by default', control.isEnabled()); } /** * Tests {@link goog.ui.Control#setEnabled}. */ function testSetEnabled() { control.render(sandbox); control.setHighlighted(true); control.setActive(true); control.getKeyEventTarget().focus(); resetEventCount(); control.setEnabled(true); assertTrue('No events must have been dispatched', noEventsDispatched()); assertTrue('Control must be enabled', control.isEnabled()); assertTrue('Control must be highlighted', control.isHighlighted()); assertTrue('Control must be active', control.isActive()); var elem = control.getElementStrict(); assertTrue( 'Control element must not have aria-disabled', goog.string.isEmptyOrWhitespace(aria.getState(elem, State.DISABLED))); assertEquals( 'Control element must have a tabIndex of 0', 0, goog.string.toNumber(elem.getAttribute('tabIndex') || '')); if (testFocus) { // Expected to fail on IE and Mac Safari 3. IE calls focus handlers // asynchronously, and Mac Safari 3 doesn't support keyboard focus. expectedFailures.expectFailureFor(goog.userAgent.IE); expectedFailures.expectFailureFor(isMacSafari3()); try { assertTrue('Control must be focused', control.isFocused()); } catch (e) { expectedFailures.handleException(e); } } resetEventCount(); control.setEnabled(false); assertEquals( 'One DISABLE event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.DISABLE)); assertFalse('Control must be disabled', control.isEnabled()); assertFalse('Control must not be highlighted', control.isHighlighted()); assertFalse('Control must not be active', control.isActive()); assertFalse('Control must not be focused', control.isFocused()); assertEquals( 'Control element must have aria-disabled true', 'true', aria.getState(control.getElementStrict(), State.DISABLED)); assertNull( 'Control element must not have a tabIndex', control.getElement().getAttribute('tabIndex')); control.setEnabled(true); control.exitDocument(); var cssClass = goog.getCssName(goog.ui.ControlRenderer.CSS_CLASS, 'disabled'); var element = goog.dom.createDom(goog.dom.TagName.DIV, {tabIndex: 0}); element.className = cssClass; goog.dom.appendChild(sandbox, element); control.decorate(element); assertEquals( 'Control element must have aria-disabled true', 'true', aria.getState(control.getElementStrict(), State.DISABLED)); assertNull( 'Control element must not have a tabIndex', control.getElement().getAttribute('tabIndex')); control.setEnabled(true); elem = control.getElementStrict(); assertEquals( 'Control element must have aria-disabled false', 'false', aria.getState(elem, State.DISABLED)); assertEquals( 'Control element must have tabIndex 0', 0, goog.string.toNumber(elem.getAttribute('tabIndex') || '')); } /** * Tests {@link goog.ui.Control#setState} when using * goog.ui.Component.State.DISABLED. */ function testSetStateWithDisabled() { control.render(sandbox); control.setHighlighted(true); control.setActive(true); control.getKeyEventTarget().focus(); resetEventCount(); control.setState(goog.ui.Component.State.DISABLED, false); assertTrue('No events must have been dispatched', noEventsDispatched()); assertTrue('Control must be enabled', control.isEnabled()); assertTrue('Control must be highlighted', control.isHighlighted()); assertTrue('Control must be active', control.isActive()); assertTrue( 'Control element must not have aria-disabled', goog.string.isEmptyOrWhitespace( aria.getState(control.getElementStrict(), State.DISABLED))); assertEquals( 'Control element must have a tabIndex of 0', 0, goog.string.toNumber( control.getElement().getAttribute('tabIndex') || '')); if (testFocus) { // Expected to fail on IE and Mac Safari 3. IE calls focus handlers // asynchronously, and Mac Safari 3 doesn't support keyboard focus. expectedFailures.expectFailureFor(goog.userAgent.IE); expectedFailures.expectFailureFor(isMacSafari3()); try { assertTrue('Control must be focused', control.isFocused()); } catch (e) { expectedFailures.handleException(e); } } resetEventCount(); control.setState(goog.ui.Component.State.DISABLED, true); assertEquals( 'One DISABLE event must have been dispatched', 1, getEventCount(control, goog.ui.Component.EventType.DISABLE)); assertFalse('Control must be disabled', control.isEnabled()); assertFalse('Control must not be highlighted', control.isHighlighted()); assertFalse('Control must not be active', control.isActive()); assertFalse('Control must not be focused', control.isFocused()); assertEquals( 'Control element must have aria-disabled true', 'true', aria.getState(control.getElementStrict(), State.DISABLED)); assertNull( 'Control element must not have a tabIndex', control.getElement().getAttribute('tabIndex')); control.setState(goog.ui.Component.State.DISABLED, false); control.exitDocument(); var cssClass = goog.getCssName(goog.ui.ControlRenderer.CSS_CLASS, 'disabled'); var element = goog.dom.createDom(goog.dom.TagName.DIV, {tabIndex: 0}); element.className = cssClass; goog.dom.appendChild(sandbox, element); control.decorate(element); assertEquals( 'Control element must have aria-disabled true', 'true', aria.getState(control.getElementStrict(), State.DISABLED)); assertNull( 'Control element must not have a tabIndex', control.getElement().getAttribute('tabIndex')); control.setState(goog.ui.Component.State.DISABLED, false); elem = control.getElementStrict(); assertEquals( 'Control element must have aria-disabled false', 'false', aria.getState(elem, State.DISABLED)); assertEquals( 'Control element must have tabIndex 0', 0, goog.string.toNumber(elem.getAttribute('tabIndex') || '')); } /** * Tests {@link goog.ui.Control#setEnabled} when the control has a parent. */ function testSetEnabledWithParent() { var child = new goog.ui.Control(null); child.setDispatchTransitionEvents(goog.ui.Component.State.ALL, true); control.addChild(child, true /* opt_render */); control.setEnabled(false); resetEventCount(); assertFalse('Parent must be disabled', control.isEnabled()); assertTrue('Child must be enabled', child.isEnabled()); child.setEnabled(false); assertTrue( 'No events must have been dispatched when child is disabled', noEventsDispatched()); assertTrue('Child must still be enabled', child.isEnabled()); resetEventCount(); control.setEnabled(true); assertEquals( 'One ENABLE event must have been dispatched by the parent', 1, getEventCount(control, goog.ui.Component.EventType.ENABLE)); assertTrue('Parent must be enabled', control.isEnabled()); assertTrue('Child must still be enabled', child.isEnabled()); resetEventCount(); child.setEnabled(false); assertEquals( 'One DISABLE event must have been dispatched by the child', 1, getEventCount(child, goog.ui.Component.EventType.DISABLE)); assertTrue('Parent must still be enabled', control.isEnabled()); assertFalse('Child must now be disabled', child.isEnabled()); resetEventCount(); control.setEnabled(false); assertEquals( 'One DISABLE event must have been dispatched by the parent', 1, getEventCount(control, goog.ui.Component.EventType.DISABLE)); assertFalse('Parent must now be disabled', control.isEnabled()); assertFalse('Child must still be disabled', child.isEnabled()); child.dispose(); } /** * Tests {@link goog.ui.Control#isHighlighted}. */ function testIsHighlighted() { assertFalse( 'Controls must not be highlighted by default', control.isHighlighted()); } /** * Tests {@link goog.ui.Control#setHighlighted}. */ function testSetHighlighted() { control.setSupportedState(goog.ui.Component.State.HOVER, false); control.setHighlighted(true); assertFalse( 'Control must not be highlighted, because it isn\'t ' + 'highlightable', control.isHighlighted()); assertTrue( 'Control must not have dispatched any events', noEventsDispatched()); control.setSupportedState(goog.ui.Component.State.HOVER, true); control.setHighlighted(true); assertTrue('Control must be highlighted', control.isHighlighted()); assertEquals( 'Control must have dispatched a HIGHLIGHT event', 1, getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT)); control.setHighlighted(true); assertTrue('Control must still be highlighted', control.isHighlighted()); assertEquals( 'Control must not dispatch more HIGHLIGHT events', 1, getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT)); control.setHighlighted(false); assertFalse('Control must not be highlighted', control.isHighlighted()); assertEquals( 'Control must have dispatched an UNHIGHLIGHT event', 1, getEventCount(control, goog.ui.Component.EventType.UNHIGHLIGHT)); control.setEnabled(false); assertFalse('Control must be disabled', control.isEnabled()); control.setHighlighted(true); assertTrue( 'Control must be highlighted, even when disabled', control.isHighlighted()); assertEquals( 'Control must have dispatched another HIGHLIGHT event', 2, getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT)); } /** * Tests {@link goog.ui.Control#isActive}. */ function testIsActive() { assertFalse('Controls must not be active by default', control.isActive()); } /** * Tests {@link goog.ui.Control#setActive}. */ function testSetActive() { control.setSupportedState(goog.ui.Component.State.ACTIVE, false); control.setActive(true); assertFalse( 'Control must not be active, because it isn\'t activateable', control.isActive()); assertTrue( 'Control must not have dispatched any events', noEventsDispatched()); control.setSupportedState(goog.ui.Component.State.ACTIVE, true); control.setActive(true); assertTrue('Control must be active', control.isActive()); assertEquals( 'Control must have dispatched an ACTIVATE event', 1, getEventCount(control, goog.ui.Component.EventType.ACTIVATE)); control.setActive(true); assertTrue('Control must still be active', control.isActive()); assertEquals( 'Control must not dispatch more ACTIVATE events', 1, getEventCount(control, goog.ui.Component.EventType.ACTIVATE)); control.setEnabled(false); assertFalse('Control must be disabled', control.isEnabled()); assertFalse('Control must not be active', control.isActive()); assertEquals( 'Control must have dispatched a DEACTIVATE event', 1, getEventCount(control, goog.ui.Component.EventType.DEACTIVATE)); } /** * Tests disposing the control from an action event handler. */ function testDisposeOnAction() { goog.events.listen(control, goog.ui.Component.EventType.ACTION, function(e) { control.dispose(); }); // Control must not throw an exception if disposed of in an ACTION event // handler. control.performActionInternal(); control.setActive(true); assertTrue('Control should have been disposed of', control.isDisposed()); } /** * Tests {@link goog.ui.Control#isSelected}. */ function testIsSelected() { assertFalse('Control