/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
goog.module('goog.ui.ControlTest');
goog.setTestOnly();
const BrowserEvent = goog.require('goog.events.BrowserEvent');
const Component = goog.require('goog.ui.Component');
const Control = goog.require('goog.ui.Control');
const ControlRenderer = goog.require('goog.ui.ControlRenderer');
const ExpectedFailures = goog.require('goog.testing.ExpectedFailures');
const GoogTestingEvent = goog.require('goog.testing.events.Event');
const KeyCodes = goog.require('goog.events.KeyCodes');
const PointerFallbackEventType = goog.require('goog.events.PointerFallbackEventType');
const State = goog.require('goog.a11y.aria.State');
const TagName = goog.require('goog.dom.TagName');
const aria = goog.require('goog.a11y.aria');
const classlist = goog.require('goog.dom.classlist');
const dom = goog.require('goog.dom');
const googArray = goog.require('goog.array');
const googEvents = goog.require('goog.events');
const googObject = goog.require('goog.object');
const googString = goog.require('goog.string');
const registry = goog.require('goog.ui.registry');
const style = goog.require('goog.style');
const testSuite = goog.require('goog.testing.testSuite');
const testing = goog.require('goog.html.testing');
const testingEvents = goog.require('goog.testing.events');
const userAgent = goog.require('goog.userAgent');
// Disabled due to problems on farm.
const testFocus = false;
let control;
const ALL_EVENTS = googObject.getValues(Component.EventType);
const events = {};
let expectedFailures;
let sandbox;
/** A dummy renderer, for testing purposes. */
class TestRenderer extends ControlRenderer {
constructor() {
super();
ControlRenderer.call(this);
}
}
/** Resets the global counter for events dispatched by test objects. */
function resetEventCount() {
googObject.clear(events);
}
/**
* Increments the global counter for events of this type.
* @param {googEvents.Event} e Event to count.
*/
function countEvent(e) {
const type = e.type;
const 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 {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 || googObject.isEmpty(events);
}
/**
* Returns the number of event listeners created by the control.
* @param {Control} control Control whose event listers are to be counted.
* @return {number} Number of event listeners.
* @suppress {visibility} suppression added to enable type checking
*/
function getListenerCount(control) {
return control.googUiComponentHandler_ ?
googObject.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 {BrowserEvent.MouseButton=} button Mouse button; defaults to
* `BrowserEvent.MouseButton.LEFT`.
* @return {boolean} Whether the event was allowed to proceed.
*/
function fireMouseDownAndFocus(element, button = undefined) {
/** @suppress {checkTypes} suppression added to enable type checking */
const result = testingEvents.fireMouseDownEvent(element, button);
if (result) {
// Browsers move focus for all buttons, not just the left button.
element.focus();
}
return result;
}
function assertClickSequenceFires(msg) {
const actionCount = getEventCount(control, Component.EventType.ACTION);
testingEvents.fireClickSequence(control.getKeyEventTarget());
assertEquals(
msg, actionCount + 1, getEventCount(control, Component.EventType.ACTION));
}
function assertIsolatedClickFires(msg) {
const actionCount = getEventCount(control, Component.EventType.ACTION);
testingEvents.fireClickEvent(control.getKeyEventTarget());
assertEquals(
msg, actionCount + 1, getEventCount(control, Component.EventType.ACTION));
}
function assertIsolatedClickDoesNotFire(msg) {
const actionCount = getEventCount(control, Component.EventType.ACTION);
testingEvents.fireClickEvent(control.getKeyEventTarget());
assertEquals(
msg, actionCount, getEventCount(control, Component.EventType.ACTION));
}
testSuite({
setUpPage() {
expectedFailures = new ExpectedFailures();
sandbox = document.getElementById('sandbox');
},
/** Initializes the testcase prior to execution. */
setUp() {
control = new Control('Hello');
control.setDispatchTransitionEvents(Component.State.ALL, true);
googEvents.listen(control, ALL_EVENTS, countEvent);
},
/** Cleans up after executing the testcase. */
tearDown() {
control.dispose();
expectedFailures.handleTearDown();
dom.removeChildren(sandbox);
resetEventCount();
},
/** Tests the {@link Control} constructor. */
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',
registry.getDefaultRenderer(Control), control.getRenderer());
const content = dom.createDom(
TagName.DIV, null, 'Hello', dom.createDom(TagName.B, null, 'World'));
const testRenderer = new TestRenderer();
const fakeDomHelper = {};
/** @suppress {checkTypes} suppression added to enable type checking */
const foo = new 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 Control#getHandler}.
* @suppress {visibility} suppression added
* to enable type checking
*/
testGetHandler() {
assertUndefined(
'Event handler must be undefined before getHandler() ' +
'is called',
control.googUiComponentHandler_);
/** @suppress {visibility} suppression added to enable type checking */
const 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 Control#isHandleMouseEvents}. */
testIsHandleMouseEvents() {
assertTrue(
'Controls must handle their own mouse events by default',
control.isHandleMouseEvents());
},
/** Tests {@link Control#setHandleMouseEvents}. */
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 Control#getKeyEventTarget}. */
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 Control#getKeyHandler}.
* @suppress {visibility} suppression
* added to enable type checking
*/
testGetKeyHandler() {
assertUndefined(
'Key handler must be undefined before getKeyHandler() ' +
'is called',
control.keyHandler_);
/** @suppress {visibility} suppression added to enable type checking */
const 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 Control#getRenderer}. */
testGetRenderer() {
assertEquals(
'Renderer must be the default registered renderer',
registry.getDefaultRenderer(Control), control.getRenderer());
},
/** Tests {@link Control#setRenderer}. */
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',
registry.getDefaultRenderer(Control), control.getRenderer());
const 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',
() => {
control.setRenderer({});
});
},
/** Tests {@link Control#getExtraClassNames}. */
testGetExtraClassNames() {
assertNull(
'Control must not have any extra class names by default',
control.getExtraClassNames());
},
/**
* Tests {@link Control#addExtraClassName} and
* {@link Control#removeExtraClassName}.
*/
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'], 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'], 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'], 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'], 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'], 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'], 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'],
classlist.get(control.getElement()));
},
/** Tests {@link Control#enableClassName}. */
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 Control#createDom}. */
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',
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',
style.isUnselectable(control.getElement()));
control.setVisible(false);
control.createDom();
assertTrue(
'Control\'s element must be hidden',
control.getElement().style.display == 'none');
},
/** Tests {@link Control#getContentElement}. */
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 Control#canDecorate}. */
testCanDecorate() {
assertTrue(control.canDecorate(dom.createElement(TagName.DIV)));
},
/** Tests {@link Control#decorateInternal}. */
testDecorateInternal() {
sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>';
const foo = dom.getElement('foo');
control.decorate(foo);
assertEquals(
'Decorated control\'s element must have expected value', foo,
control.getElement());
assertTrue(
'Element must be unselectable',
style.isUnselectable(control.getElement()));
assertTrue(
'Element must be visible',
control.getElement().style.display != 'none');
},
/**
* Tests {@link Control#decorateInternal} with a control that
* allows text selection.
*/
testDecorateInternalForSelectableControl() {
sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>';
const foo = 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',
style.isUnselectable(control.getElement()));
assertTrue('Control must be visible', control.isVisible());
},
/** Tests {@link Control#decorateInternal} with a hidden element. */
testDecorateInternalForHiddenElement() {
sandbox.innerHTML = '<div id="foo" style="display:none">Hello!</div>';
const foo = dom.getElement('foo');
control.decorate(foo);
assertEquals(
'Decorated control\'s element must have expected value', foo,
control.getElement());
assertTrue(
'Element must be unselectable',
style.isUnselectable(control.getElement()));
assertFalse('Control must be hidden', control.isVisible());
},
/**
* Tests {@link Control#enterDocument}.
* @suppress {visibility} suppression
* added to enable type checking
*/
testEnterDocument() {
control.render(sandbox);
assertTrue('Control must be in the document', control.isInDocument());
if (userAgent.IE && !userAgent.isVersionOrHigher(9)) {
assertEquals(
'Control must have 6 mouse & 3 key event listeners on IE8', 9,
getListenerCount(control));
} else {
assertEquals(
'Control must have 5 mouse and 3 key event listeners', 8,
getListenerCount(control));
}
assertEquals(
'Control\'s key event handler must be attached to its ' +
'key event target',
control.getKeyEventTarget(), control.getKeyHandler().element_);
},
/**
* Tests {@link Control#enterDocument} for a control that doesn't
* handle mouse events.
* @suppress {visibility} suppression added to enable type checking
*/
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 Control#enterDocument} for a control that isn't
* focusable.
* @suppress {visibility} suppression added to enable type checking
*/
testEnterDocumentForNonFocusableControl() {
control.setSupportedState(Component.State.FOCUSED, false);
control.render(sandbox);
assertTrue('Control must be in the document', control.isInDocument());
if (userAgent.IE && !userAgent.isVersionOrHigher(9)) {
assertEquals(
'Control must have 6 mouse event listeners on IE8', 6,
getListenerCount(control));
} else {
assertEquals(
'Control must have 5 mouse event listeners', 5,
getListenerCount(control));
}
assertUndefined(
'Control must not have a key event handler', control.keyHandler_);
},
/**
* Tests {@link Control#enterDocument} for a control that doesn't
* need to do any event handling.
* @suppress {visibility} suppression added to enable type checking
*/
testEnterDocumentForControlWithoutEventHandlers() {
control.setHandleMouseEvents(false);
control.setSupportedState(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 Control#exitDocument}.
* @suppress {visibility} suppression
* added to enable type checking
*/
testExitDocument() {
control.render(sandbox);
assertTrue('Control must be in the document', control.isInDocument());
if (userAgent.IE && !userAgent.isVersionOrHigher(9)) {
assertEquals(
'Control must have 6 mouse & 3 key event listeners on IE8', 9,
getListenerCount(control));
} else {
assertEquals(
'Control must have 5 mouse and 3 key event listeners', 8,
getListenerCount(control));
}
assertEquals(
'Control\'s key event handler must be attached to its ' +
'key event target',
control.getKeyEventTarget(), control.getKeyHandler().element_);
assertTrue(
'Control\'s element must support keyboard focus',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
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',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
},
/**
* Tests {@link Control#dispose}.
* @suppress {visibility} suppression added to
* enable type checking
*/
testDispose() {
control.render(sandbox);
/** @suppress {visibility} suppression added to enable type checking */
const handler = control.getHandler();
/** @suppress {visibility} suppression added to enable type checking */
const 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 Control#getContent}. */
testGetContent() {
assertNull(
'Empty control must have null content',
(new 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 Control#getContent}.
* @suppress {checkTypes} suppression added
* to enable type checking
*/
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';
const empty = new Control(null);
empty.decorate(dom.getElement('empty'));
assertNull(
'Content of control decorating empty DIV must be null',
empty.getContent());
empty.dispose();
const text = new Control(null);
text.decorate(dom.getElement('text'));
assertEquals(
'Content of control decorating DIV with text contents ' +
'must be as expected',
'Hello, world!', text.getContent().nodeValue);
text.dispose();
const element = new Control(null);
element.decorate(dom.getElement('element'));
assertEquals(
'Content of control decorating DIV with element child ' +
'must be as expected',
dom.getElement('element').firstChild, element.getContent());
element.dispose();
const nodelist = new Control(null);
nodelist.decorate(dom.getElement('nodelist'));
assertSameElements(
'Content of control decorating DIV with mixed ' +
'contents must be as expected',
dom.getElement('nodelist').childNodes, nodelist.getContent());
nodelist.dispose();
},
/** Tests {@link Control#setAriaLabel}. */
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);
const elem = control.getElementStrict();
assertEquals(
'Element must have control\'s aria label after rendering', 'label',
aria.getLabel(elem));
control.setAriaLabel('new label');
assertEquals(
'Element must have the new aria label', 'new label',
aria.getLabel(elem));
},
/** Tests {@link Control#setAriaLabel}. */
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(dom.getElement('nodelist'));
const elem = control.getElementStrict();
assertEquals(
'Element must have control\'s aria label after rendering', 'label',
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',
aria.getLabel(elem));
},
/** Tests {@link Control#setContent}. */
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([
dom.createDom(
TagName.DIV, null, dom.createDom(TagName.SPAN, null, 'Hello')),
'World',
]);
assertHTMLEquals(
'Control\'s DOM must be updated', '<div><span>Hello</span></div>World',
control.getElement().innerHTML);
},
/** Tests {@link Control#setContentInternal}. */
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 Control#getCaption}. */
testGetCaption() {
assertEquals(
'Empty control\'s caption must be empty string', '',
(new Control(null)).getCaption());
assertEquals(
'Caption must have expected value', 'Hello', control.getCaption());
sandbox.innerHTML = '<div id="nodelist">Hello, <b>world</b>!</div>';
control.decorate(dom.getElement('nodelist'));
assertEquals(
'Caption must have expected value', 'Hello, world!',
control.getCaption());
const arrayContent =
googArray.clone(dom.safeHtmlToNode(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 Control#setCaption}. */
testSetCaption() {
control.setCaption('Hello, world!');
assertEquals(
'Control must have a string caption "Hello, world!"', 'Hello, world!',
control.getCaption());
},
/** Tests {@link Control#setRightToLeft}. */
testSetRightToLeft() {
control.createDom();
assertFalse(
'Control\'s element must not have right-to-left class',
classlist.contains(control.getElement(), 'goog-control-rtl'));
control.setRightToLeft(true);
assertTrue(
'Control\'s element must have right-to-left class',
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',
() => {
control.setRightToLeft(false);
});
},
/** Tests {@link Control#isAllowTextSelection}. */
testIsAllowTextSelection() {
assertFalse(
'Controls must not allow text selection by default',
control.isAllowTextSelection());
},
/** Tests {@link Control#setAllowTextSelection}. */
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 Control#isVisible}. */
testIsVisible() {
assertTrue('Controls must be visible by default', control.isVisible());
},
/** Tests {@link Control#setVisible} before it is rendered. */
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, 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, Component.EventType.SHOW));
assertTrue('Control must be visible', control.isVisible());
},
/** Tests {@link Control#setVisible} after it is rendered. */
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, 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, Component.EventType.SHOW));
assertTrue('Control must be visible', control.isVisible());
assertTrue(
'Control\'s element must be visible',
control.getElement().style.display != 'none');
},
/**
* Tests {@link Control#setVisible} for disabled non-focusable
* controls.
*/
testSetVisibleForDisabledNonFocusableControl() {
// Hidden, disabled, non-focusable control becoming visible.
control.setEnabled(false);
control.setSupportedState(Component.State.FOCUSED, false);
control.render(sandbox);
assertTrue('Control must be visible', control.isVisible());
assertFalse(
'Control must not have a tab index',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
// Visible, disabled, non-focusable control becoming hidden.
control.getKeyEventTarget().focus();
assertEquals(
'Control must not have dispatched FOCUS', 0,
getEventCount(control, 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',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
assertEquals(
'Control must have dispatched HIDE', 1,
getEventCount(control, Component.EventType.HIDE));
assertEquals(
'Control must not have dispatched BLUR', 0,
getEventCount(control, Component.EventType.BLUR));
},
/** Tests {@link Control#setVisible} for disabled focusable controls. */
testSetVisibleForDisabledFocusableControl() {
// Hidden, disabled, focusable control becoming visible.
control.setEnabled(false);
control.setSupportedState(Component.State.FOCUSED, true);
control.render(sandbox);
assertTrue('Control must be visible', control.isVisible());
assertFalse(
'Control must not have a tab index',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
// Visible, disabled, focusable control becoming hidden.
control.getKeyEventTarget().focus();
assertEquals(
'Control must not have dispatched FOCUS', 0,
getEventCount(control, 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',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
assertEquals(
'Control must have dispatched HIDE', 1,
getEventCount(control, Component.EventType.HIDE));
assertEquals(
'Control must not have dispatched BLUR', 0,
getEventCount(control, Component.EventType.BLUR));
},
/**
* Tests {@link Control#setVisible} for enabled non-focusable
* controls.
*/
testSetVisibleForEnabledNonFocusableControl() {
// Hidden, enabled, non-focusable control becoming visible.
control.setEnabled(true);
control.setSupportedState(Component.State.FOCUSED, false);
control.render(sandbox);
assertTrue('Control must be visible', control.isVisible());
assertFalse(
'Control must not have a tab index',
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, 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',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
assertEquals(
'Control must have dispatched HIDE', 1,
getEventCount(control, Component.EventType.HIDE));
assertEquals(
'Control must not have dispatched BLUR', 0,
getEventCount(control, Component.EventType.BLUR));
}
},
/** Tests {@link Control#setVisible} for enabled focusable controls. */
testSetVisibleForEnabledFocusableControl() {
// Hidden, enabled, focusable control becoming visible.
control.setEnabled(true);
control.setSupportedState(Component.State.FOCUSED, true);
control.render(sandbox);
assertTrue('Control must be visible', control.isVisible());
if (testFocus) {
// Mac Safari currently doesn't support tabIndex on arbitrary
// elements.
assertTrue(
'Control must have a tab index',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
// Visible, enabled, focusable control becoming hidden.
control.getKeyEventTarget().focus();
// Expected to fail on IE.
expectedFailures.expectFailureFor(userAgent.IE);
try {
// IE dispatches focus and blur events asynchronously!
assertEquals(
'Control must have dispatched FOCUS', 1,
getEventCount(control, 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',
dom.isFocusableTabIndex(control.getKeyEventTarget()));
assertEquals(
'Control must have dispatched HIDE', 1,
getEventCount(control, Component.EventType.HIDE));
// Expected to fail on IE.
expectedFailures.expectFailureFor(userAgent.IE);
try {
// IE dispatches focus and blur events asynchronously!
assertEquals(
'Control must have dispatched BLUR', 1,
getEventCount(control, Component.EventType.BLUR));
assertFalse(
'Control must no longer have keyboard focus', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
},
/** Tests {@link Control#isEnabled}. */
testIsEnabled() {
assertTrue('Controls must be enabled by default', control.isEnabled());
},
/** Tests {@link Control#setEnabled}. */
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());
let elem = control.getElementStrict();
assertTrue(
'Control element must not have aria-disabled',
googString.isEmptyOrWhitespace(aria.getState(elem, State.DISABLED)));
assertEquals(
'Control element must have a tabIndex of 0', 0,
googString.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(userAgent.IE);
assertTrue('Control must be focused', control.isFocused());
}
resetEventCount();
control.setEnabled(false);
assertEquals(
'One DISABLE event must have been dispatched', 1,
getEventCount(control, 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();
const cssClass = goog.getCssName(ControlRenderer.CSS_CLASS, 'disabled');
const element = dom.createDom(TagName.DIV, {tabIndex: 0});
element.className = cssClass;
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,
googString.toNumber(elem.getAttribute('tabIndex') || ''));
},
/**
* Tests {@link Control#setState} when using
* Component.State.DISABLED.
*/
testSetStateWithDisabled() {
control.render(sandbox);
control.setHighlighted(true);
control.setActive(true);
control.getKeyEventTarget().focus();
resetEventCount();
control.setState(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',
googString.isEmptyOrWhitespace(
aria.getState(control.getElementStrict(), State.DISABLED)));
assertEquals(
'Control element must have a tabIndex of 0', 0,
googString.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(userAgent.IE);
try {
assertTrue('Control must be focused', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
resetEventCount();
control.setState(Component.State.DISABLED, true);
assertEquals(
'One DISABLE event must have been dispatched', 1,
getEventCount(control, 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(Component.State.DISABLED, false);
control.exitDocument();
const cssClass = goog.getCssName(ControlRenderer.CSS_CLASS, 'disabled');
const element = dom.createDom(TagName.DIV, {tabIndex: 0});
element.className = cssClass;
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(Component.State.DISABLED, false);
let 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,
googString.toNumber(elem.getAttribute('tabIndex') || ''));
},
/** Tests {@link Control#setEnabled} when the control has a parent. */
testSetEnabledWithParent() {
const child = new Control(null);
child.setDispatchTransitionEvents(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, 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, 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, Component.EventType.DISABLE));
assertFalse('Parent must now be disabled', control.isEnabled());
assertFalse('Child must still be disabled', child.isEnabled());
child.dispose();
},
/** Tests {@link Control#isHighlighted}. */
testIsHighlighted() {
assertFalse(
'Controls must not be highlighted by default', control.isHighlighted());
},
/** Tests {@link Control#setHighlighted}. */
testSetHighlighted() {
control.setSupportedState(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(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, 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, 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, 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, Component.EventType.HIGHLIGHT));
},
/** Tests {@link Control#isActive}. */
testIsActive() {
assertFalse('Controls must not be active by default', control.isActive());
},
/** Tests {@link Control#setActive}. */
testSetActive() {
control.setSupportedState(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(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, 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, 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, Component.EventType.DEACTIVATE));
},
/**
Tests disposing the control from an action event handler.
@suppress {visibility} suppression added to enable type checking
*/
testDisposeOnAction() {
googEvents.listen(control, Component.EventType.ACTION, (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 Control#isSelected}. */
testIsSelected() {
assertFalse(
'Controls must not be selected by default', control.isSelected());
},
/** Tests {@link Control#setSelected}. */
testSetSelected() {
control.setSupportedState(Component.State.SELECTED, false);
control.setSelected(true);
assertFalse(
'Control must not be selected, because it isn\'t selectable',
control.isSelected());
assertTrue(
'Control must not have dispatched any events', noEventsDispatched());
control.setSupportedState(Component.State.SELECTED, true);
control.setSelected(true);
assertTrue('Control must be selected', control.isSelected());
assertEquals(
'Control must have dispatched a SELECT event', 1,
getEventCount(control, Component.EventType.SELECT));
control.setSelected(true);
assertTrue('Control must still be selected', control.isSelected());
assertEquals(
'Control must not dispatch more SELECT events', 1,
getEventCount(control, Component.EventType.SELECT));
control.setSelected(false);
assertFalse('Control must not be selected', control.isSelected());
assertEquals(
'Control must have dispatched an UNSELECT event', 1,
getEventCount(control, Component.EventType.UNSELECT));
control.setEnabled(false);
assertFalse('Control must be disabled', control.isEnabled());
control.setSelected(true);
assertTrue(
'Control must be selected, even when disabled', control.isSelected());
assertEquals(
'Control must have dispatched another SELECT event', 2,
getEventCount(control, Component.EventType.SELECT));
},
/** Tests {@link Control#isChecked}. */
testIsChecked() {
assertFalse('Controls must not be checked by default', control.isChecked());
},
/** Tests {@link Control#setChecked}. */
testSetChecked() {
control.setSupportedState(Component.State.CHECKED, false);
control.setChecked(true);
assertFalse(
'Control must not be checked, because it isn\'t checkable',
control.isChecked());
assertTrue(
'Control must not have dispatched any events', noEventsDispatched());
control.setSupportedState(Component.State.CHECKED, true);
control.setChecked(true);
assertTrue('Control must be checked', control.isChecked());
assertEquals(
'Control must have dispatched a CHECK event', 1,
getEventCount(control, Component.EventType.CHECK));
control.setChecked(true);
assertTrue('Control must still be checked', control.isChecked());
assertEquals(
'Control must not dispatch more CHECK events', 1,
getEventCount(control, Component.EventType.CHECK));
control.setChecked(false);
assertFalse('Control must not be checked', control.isChecked());
assertEquals(
'Control must have dispatched an UNCHECK event', 1,
getEventCount(control, Component.EventType.UNCHECK));
control.setEnabled(false);
assertFalse('Control must be disabled', control.isEnabled());
control.setChecked(true);
assertTrue(
'Control must be checked, even when disabled', control.isChecked());
assertEquals(
'Control must have dispatched another CHECK event', 2,
getEventCount(control, Component.EventType.CHECK));
},
/** Tests {@link Control#isFocused}. */
testIsFocused() {
assertFalse('Controls must not be focused by default', control.isFocused());
},
/** Tests {@link Control#setFocused}. */
testSetFocused() {
control.setSupportedState(Component.State.FOCUSED, false);
control.setFocused(true);
assertFalse(
'Control must not be focused, because it isn\'t focusable',
control.isFocused());
assertTrue(
'Control must not have dispatched any events', noEventsDispatched());
control.setSupportedState(Component.State.FOCUSED, true);
control.setFocused(true);
assertTrue('Control must be focused', control.isFocused());
assertEquals(
'Control must have dispatched a FOCUS event', 1,
getEventCount(control, Component.EventType.FOCUS));
control.setFocused(true);
assertTrue('Control must still be focused', control.isFocused());
assertEquals(
'Control must not dispatch more FOCUS events', 1,
getEventCount(control, Component.EventType.FOCUS));
control.setFocused(false);
assertFalse('Control must not be focused', control.isFocused());
assertEquals(
'Control must have dispatched an BLUR event', 1,
getEventCount(control, Component.EventType.BLUR));
control.setEnabled(false);
assertFalse('Control must be disabled', control.isEnabled());
control.setFocused(true);
assertTrue(
'Control must be focused, even when disabled', control.isFocused());
assertEquals(
'Control must have dispatched another FOCUS event', 2,
getEventCount(control, Component.EventType.FOCUS));
},
/** Tests {@link Control#isOpen}. */
testIsOpen() {
assertFalse('Controls must not be open by default', control.isOpen());
},
/** Tests {@link Control#setOpen}. */
testSetOpen() {
control.setSupportedState(Component.State.OPENED, false);
control.setOpen(true);
assertFalse(
'Control must not be opened, because it isn\'t openable',
control.isOpen());
assertTrue(
'Control must not have dispatched any events', noEventsDispatched());
control.setSupportedState(Component.State.OPENED, true);
control.setOpen(true);
assertTrue('Control must be opened', control.isOpen());
assertEquals(
'Control must have dispatched a OPEN event', 1,
getEventCount(control, Component.EventType.OPEN));
control.setOpen(true);
assertTrue('Control must still be opened', control.isOpen());
assertEquals(
'Control must not dispatch more OPEN events', 1,
getEventCount(control, Component.EventType.OPEN));
control.setOpen(false);
assertFalse('Control must not be opened', control.isOpen());
assertEquals(
'Control must have dispatched an CLOSE event', 1,
getEventCount(control, Component.EventType.CLOSE));
control.setEnabled(false);
assertFalse('Control must be disabled', control.isEnabled());
control.setOpen(true);
assertTrue('Control must be opened, even when disabled', control.isOpen());
assertEquals(
'Control must have dispatched another OPEN event', 2,
getEventCount(control, Component.EventType.OPEN));
},
/** Tests {@link Control#getState}. */
testGetState() {
assertEquals(
'Controls must be in the default state', 0x00, control.getState());
},
/**
* Tests {@link Control#hasState}.
* @suppress {checkTypes} suppression added
* to enable type checking
*/
testHasState() {
assertFalse(
'Control must not be disabled',
control.hasState(Component.State.DISABLED));
assertFalse(
'Control must not be in the HOVER state',
control.hasState(Component.State.HOVER));
assertFalse(
'Control must not be active', control.hasState(Component.State.ACTIVE));
assertFalse(
'Control must not be selected',
control.hasState(Component.State.SELECTED));
assertFalse(
'Control must not be checked',
control.hasState(Component.State.CHECKED));
assertFalse(
'Control must not be focused',
control.hasState(Component.State.FOCUSED));
assertFalse(
'Control must not be open', control.hasState(Component.State.OPEN));
},
/** Tests {@link Control#setState}. */
testSetState() {
control.createDom();
control.setSupportedState(Component.State.ACTIVE, false);
assertFalse(
'Control must not be active', control.hasState(Component.State.ACTIVE));
control.setState(Component.State.ACTIVE, true);
assertFalse(
'Control must still be inactive (because it doesn\'t ' +
'support the ACTIVE state)',
control.hasState(Component.State.ACTIVE));
control.setSupportedState(Component.State.ACTIVE, true);
control.setState(Component.State.ACTIVE, true);
assertTrue(
'Control must be active', control.hasState(Component.State.ACTIVE));
assertTrue(
'Control must have the active CSS style',
classlist.contains(control.getElement(), 'goog-control-active'));
control.setState(Component.State.ACTIVE, true);
assertTrue(
'Control must still be active',
control.hasState(Component.State.ACTIVE));
assertTrue(
'Control must still have the active CSS style',
classlist.contains(control.getElement(), 'goog-control-active'));
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/** Tests {@link Control#setStateInternal}. */
testSetStateInternal() {
control.setStateInternal(0x00);
assertEquals('State should be 0x00', 0x00, control.getState());
control.setStateInternal(0x17);
assertEquals('State should be 0x17', 0x17, control.getState());
},
/** Tests {@link Control#isSupportedState}. */
testIsSupportedState() {
assertTrue(
'Control must support DISABLED',
control.isSupportedState(Component.State.DISABLED));
assertTrue(
'Control must support HOVER',
control.isSupportedState(Component.State.HOVER));
assertTrue(
'Control must support ACTIVE',
control.isSupportedState(Component.State.ACTIVE));
assertTrue(
'Control must support FOCUSED',
control.isSupportedState(Component.State.FOCUSED));
assertFalse(
'Control must no support SELECTED',
control.isSupportedState(Component.State.SELECTED));
assertFalse(
'Control must no support CHECKED',
control.isSupportedState(Component.State.CHECKED));
assertFalse(
'Control must no support OPENED',
control.isSupportedState(Component.State.OPENED));
},
/** Tests {@link Control#setSupportedState}. */
testSetSupportedState() {
control.setSupportedState(Component.State.HOVER, true);
assertTrue(
'Control must still support HOVER',
control.isSupportedState(Component.State.HOVER));
control.setSupportedState(Component.State.HOVER, false);
assertFalse(
'Control must no longer support HOVER',
control.isSupportedState(Component.State.HOVER));
control.setState(Component.State.ACTIVE, true);
control.setSupportedState(Component.State.ACTIVE, false);
assertFalse(
'Control must no longer support ACTIVE',
control.isSupportedState(Component.State.ACTIVE));
assertFalse(
'Control must no longer be in the ACTIVE state',
control.hasState(Component.State.ACTIVE));
control.render(sandbox);
control.setSupportedState(Component.State.FOCUSED, true);
control.setState(Component.State.FOCUSED, true);
assertThrows(
'Must not be able to disable support for the FOCUSED ' +
'state for a control that\'s already in the document and focused',
() => {
control.setSupportedState(Component.State.FOCUSED, false);
});
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/** Tests {@link Control#isAutoState}. */
testIsAutoState() {
assertTrue(
'Control must have DISABLED as an auto-state',
control.isAutoState(Component.State.DISABLED));
assertTrue(
'Control must have HOVER as an auto-state',
control.isAutoState(Component.State.HOVER));
assertTrue(
'Control must have ACTIVE as an auto-state',
control.isAutoState(Component.State.ACTIVE));
assertTrue(
'Control must have FOCUSED as an auto-state',
control.isAutoState(Component.State.FOCUSED));
assertFalse(
'Control must not have SELECTED as an auto-state',
control.isAutoState(Component.State.SELECTED));
assertFalse(
'Control must not have CHECKED as an auto-state',
control.isAutoState(Component.State.CHECKED));
assertFalse(
'Control must not have OPENED as an auto-state',
control.isAutoState(Component.State.OPENED));
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/** Tests {@link Control#setAutoStates}. */
testSetAutoStates() {
control.setAutoStates(Component.State.HOVER, false);
assertFalse(
'Control must not have HOVER as an auto-state',
control.isAutoState(Component.State.HOVER));
control.setAutoStates(
Component.State.ACTIVE | Component.State.FOCUSED, false);
assertFalse(
'Control must not have ACTIVE as an auto-state',
control.isAutoState(Component.State.ACTIVE));
assertFalse(
'Control must not have FOCUSED as an auto-state',
control.isAutoState(Component.State.FOCUSED));
control.setSupportedState(Component.State.FOCUSED, false);
control.setAutoStates(Component.State.FOCUSED, true);
assertFalse(
'Control must not have FOCUSED as an auto-state if it no ' +
'longer supports FOCUSED',
control.isAutoState(Component.State.FOCUSED));
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/** Tests {@link Control#isDispatchTransitionEvents}. */
testIsDispatchTransitionEvents() {
assertTrue(
'Control must dispatch DISABLED transition events',
control.isDispatchTransitionEvents(Component.State.DISABLED));
assertTrue(
'Control must dispatch HOVER transition events',
control.isDispatchTransitionEvents(Component.State.HOVER));
assertTrue(
'Control must dispatch ACTIVE transition events',
control.isDispatchTransitionEvents(Component.State.ACTIVE));
assertTrue(
'Control must dispatch FOCUSED transition events',
control.isDispatchTransitionEvents(Component.State.FOCUSED));
assertFalse(
'Control must not dispatch SELECTED transition events',
control.isDispatchTransitionEvents(Component.State.SELECTED));
assertFalse(
'Control must not dispatch CHECKED transition events',
control.isDispatchTransitionEvents(Component.State.CHECKED));
assertFalse(
'Control must not dispatch OPENED transition events',
control.isDispatchTransitionEvents(Component.State.OPENED));
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/** Tests {@link Control#setDispatchTransitionEvents}. */
testSetDispatchTransitionEvents() {
control.setDispatchTransitionEvents(Component.State.HOVER, false);
assertFalse(
'Control must not dispatch HOVER transition events',
control.isDispatchTransitionEvents(Component.State.HOVER));
control.setSupportedState(Component.State.SELECTED, true);
control.setDispatchTransitionEvents(Component.State.SELECTED, true);
assertTrue(
'Control must dispatch SELECTED transition events',
control.isDispatchTransitionEvents(Component.State.SELECTED));
assertTrue('No events must have been dispatched', noEventsDispatched());
},
/**
* Tests {@link Control#isTransitionAllowed}.
* @suppress {visibility} suppression added to enable type checking
*/
testIsTransitionAllowed() {
assertTrue(
'Control must support the HOVER state',
control.isSupportedState(Component.State.HOVER));
assertFalse(
'Control must not be in the HOVER state',
control.hasState(Component.State.HOVER));
assertTrue(
'Control must dispatch HOVER transition events',
control.isDispatchTransitionEvents(Component.State.HOVER));
assertTrue(
'Control must be allowed to transition to the HOVER state',
control.isTransitionAllowed(Component.State.HOVER, true));
assertEquals(
'Control must have dispatched one HIGHLIGHT event', 1,
getEventCount(control, Component.EventType.HIGHLIGHT));
assertFalse(
'Control must not be highlighted',
control.hasState(Component.State.HOVER));
control.setState(Component.State.HOVER, true);
control.setDispatchTransitionEvents(Component.State.HOVER, false);
assertTrue(
'Control must be allowed to transition from the HOVER state',
control.isTransitionAllowed(Component.State.HOVER, false));
assertEquals(
'Control must not have dispatched any UNHIGHLIGHT events', 0,
getEventCount(control, Component.EventType.UNHIGHLIGHT));
assertTrue(
'Control must still be highlighted',
control.hasState(Component.State.HOVER));
control.setSupportedState(Component.State.FOCUSED, false);
resetEventCount();
assertFalse(
'Control doesn\'t support the FOCUSED state',
control.isSupportedState(Component.State.FOCUSED));
assertFalse(
'Control must not be FOCUSED',
control.hasState(Component.State.FOCUSED));
assertFalse(
'Control must not be allowed to transition to the FOCUSED ' +
'state',
control.isTransitionAllowed(Component.State.FOCUSED, true));
assertEquals(
'Control must not have dispatched any FOCUS events', 0,
getEventCount(control, Component.EventType.FOCUS));
control.setEnabled(false);
resetEventCount();
assertTrue(
'Control must support the DISABLED state',
control.isSupportedState(Component.State.DISABLED));
assertTrue(
'Control must be DISABLED', control.hasState(Component.State.DISABLED));
assertFalse(
'Control must not be allowed to transition to the DISABLED ' +
'state, because it is already there',
control.isTransitionAllowed(Component.State.DISABLED, true));
assertEquals(
'Control must not have dispatched any ENABLE events', 0,
getEventCount(control, Component.EventType.ENABLE));
},
/** Tests {@link Control#handleKeyEvent}. */
testHandleKeyEvent() {
control.render();
control.isVisible = control.isEnabled = () => true;
testingEvents.fireKeySequence(control.getKeyEventTarget(), KeyCodes.A);
assertEquals(
'Control must not have dispatched an ACTION event', 0,
getEventCount(control, Component.EventType.ACTION));
testingEvents.fireKeySequence(control.getKeyEventTarget(), KeyCodes.ENTER);
assertEquals(
'Control must have dispatched an ACTION event', 1,
getEventCount(control, Component.EventType.ACTION));
},
/**
* Tests {@link Control#performActionInternal}.
* @suppress {visibility} suppression added to enable type checking
*/
testPerformActionInternal() {
assertFalse('Control must not be checked', control.isChecked());
assertFalse('Control must not be selected', control.isSelected());
assertFalse('Control must not be open', control.isOpen());
control.performActionInternal();
assertFalse('Control must not be checked', control.isChecked());
assertFalse('Control must not be selected', control.isSelected());
assertFalse('Control must not be open', control.isOpen());
assertEquals(
'Control must have dispatched an ACTION event', 1,
getEventCount(control, Component.EventType.ACTION));
control.setSupportedState(Component.State.CHECKED, true);
control.setSupportedState(Component.State.SELECTED, true);
control.setSupportedState(Component.State.OPENED, true);
control.performActionInternal();
assertTrue('Control must be checked', control.isChecked());
assertTrue('Control must be selected', control.isSelected());
assertTrue('Control must be open', control.isOpen());
assertEquals(
'Control must have dispatched a CHECK event', 1,
getEventCount(control, Component.EventType.CHECK));
assertEquals(
'Control must have dispatched a SELECT event', 1,
getEventCount(control, Component.EventType.SELECT));
assertEquals(
'Control must have dispatched a OPEN event', 1,
getEventCount(control, Component.EventType.OPEN));
assertEquals(
'Control must have dispatched another ACTION event', 2,
getEventCount(control, Component.EventType.ACTION));
control.performActionInternal();
assertFalse('Control must not be checked', control.isChecked());
assertTrue('Control must be selected', control.isSelected());
assertFalse('Control must not be open', control.isOpen());
assertEquals(
'Control must have dispatched an UNCHECK event', 1,
getEventCount(control, Component.EventType.UNCHECK));
assertEquals(
'Control must not have dispatched an UNSELECT event', 0,
getEventCount(control, Component.EventType.UNSELECT));
assertEquals(
'Control must have dispatched a CLOSE event', 1,
getEventCount(control, Component.EventType.CLOSE));
assertEquals(
'Control must have dispatched another ACTION event', 3,
getEventCount(control, Component.EventType.ACTION));
},
/** Tests {@link Control#handleMouseOver}. */
testHandleMouseOver() {
control.setContent(dom.createDom(TagName.SPAN, {id: 'caption'}, 'Hello'));
control.render(sandbox);
const element = control.getElement();
const caption = dom.getElement('caption');
// Verify baseline assumptions.
assertTrue(
'Caption must be contained within the control',
dom.contains(element, caption));
assertTrue('Control must be enabled', control.isEnabled());
assertTrue(
'HOVER must be an auto-state',
control.isAutoState(Component.State.HOVER));
assertFalse(
'Control must not start out highlighted', control.isHighlighted());
// Scenario 1: relatedTarget is contained within the control's DOM.
testingEvents.fireMouseOverEvent(element, caption);
assertTrue(
'No events must have been dispatched for internal mouse move',
noEventsDispatched());
assertFalse(
'Control must not be highlighted for internal mouse move',
control.isHighlighted());
resetEventCount();
// Scenario 2: preventDefault() is called on the ENTER event.
const key = googEvents.listen(control, Component.EventType.ENTER, (e) => {
e.preventDefault();
});
testingEvents.fireMouseOverEvent(element, sandbox);
assertEquals(
'Control must have dispatched 1 ENTER event', 1,
getEventCount(control, Component.EventType.ENTER));
assertFalse(
'Control must not be highlighted if ENTER is canceled',
control.isHighlighted());
googEvents.unlistenByKey(key);
resetEventCount();
// Scenario 3: Control is disabled.
control.setEnabled(false);
testingEvents.fireMouseOverEvent(element, sandbox);
assertEquals(
'Control must dispatch ENTER event on mouseover even if ' +
'disabled',
1, getEventCount(control, Component.EventType.ENTER));
assertFalse(
'Control must not be highlighted if it is disabled',
control.isHighlighted());
control.setEnabled(true);
resetEventCount();
// Scenario 4: HOVER is not an auto-state.
control.setAutoStates(Component.State.HOVER, false);
testingEvents.fireMouseOverEvent(element, sandbox);
assertEquals(
'Control must dispatch ENTER event on mouseover even if ' +
'HOVER is not an auto-state',
1, getEventCount(control, Component.EventType.ENTER));
assertFalse(
'Control must not be highlighted if HOVER isn\'t an auto-' +
'state',
control.isHighlighted());
control.setAutoStates(Component.State.HOVER, true);
resetEventCount();
// Scenario 5: All is well.
testingEvents.fireMouseOverEvent(element, sandbox);
assertEquals(
'Control must dispatch ENTER event on mouseover', 1,
getEventCount(control, Component.EventType.ENTER));
assertEquals(
'Control must dispatch HIGHLIGHT event on mouseover', 1,
getEventCount(control, Component.EventType.HIGHLIGHT));
assertTrue('Control must be highlighted', control.isHighlighted());
resetEventCount();
// Scenario 6: relatedTarget is null
control.setHighlighted(false);
testingEvents.fireMouseOverEvent(element, null);
assertEquals(
'Control must dispatch ENTER event on mouseover', 1,
getEventCount(control, Component.EventType.ENTER));
assertEquals(
'Control must dispatch HIGHLIGHT event on mouseover', 1,
getEventCount(control, Component.EventType.HIGHLIGHT));
assertTrue('Control must be highlighted', control.isHighlighted());
resetEventCount();
},
/** Tests {@link Control#handleMouseOut}. */
testHandleMouseOut() {
control.setContent(dom.createDom(TagName.SPAN, {id: 'caption'}, 'Hello'));
control.setHighlighted(true);
control.setActive(true);
resetEventCount();
control.render(sandbox);
const element = control.getElement();
const caption = dom.getElement('caption');
// Verify baseline assumptions.
assertTrue(
'Caption must be contained within the control',
dom.contains(element, caption));
assertTrue('Control must be enabled', control.isEnabled());
assertTrue(
'HOVER must be an auto-state',
control.isAutoState(Component.State.HOVER));
assertTrue(
'ACTIVE must be an auto-state',
control.isAutoState(Component.State.ACTIVE));
assertTrue('Control must start out highlighted', control.isHighlighted());
assertTrue('Control must start out active', control.isActive());
// Scenario 1: relatedTarget is contained within the control's DOM.
testingEvents.fireMouseOutEvent(element, caption);
assertTrue(
'No events must have been dispatched for internal mouse move',
noEventsDispatched());
assertTrue(
'Control must not be un-highlighted for internal mouse move',
control.isHighlighted());
assertTrue(
'Control must not be deactivated for internal mouse move',
control.isActive());
resetEventCount();
// Scenario 2: preventDefault() is called on the LEAVE event.
const key = googEvents.listen(control, Component.EventType.LEAVE, (e) => {
e.preventDefault();
});
testingEvents.fireMouseOutEvent(element, sandbox);
assertEquals(
'Control must have dispatched 1 LEAVE event', 1,
getEventCount(control, Component.EventType.LEAVE));
assertTrue(
'Control must not be un-highlighted if LEAVE is canceled',
control.isHighlighted());
assertTrue(
'Control must not be deactivated if LEAVE is canceled',
control.isActive());
googEvents.unlistenByKey(key);
resetEventCount();
// Scenario 3: ACTIVE is not an auto-state.
control.setAutoStates(Component.State.ACTIVE, false);
testingEvents.fireMouseOutEvent(element, sandbox);
assertEquals(
'Control must dispatch LEAVE event on mouseout even if ' +
'ACTIVE is not an auto-state',
1, getEventCount(control, Component.EventType.LEAVE));
assertTrue(
'Control must not be deactivated if ACTIVE isn\'t an auto-' +
'state',
control.isActive());
assertFalse(
'Control must be un-highlighted even if ACTIVE isn\'t an ' +
'auto-state',
control.isHighlighted());
control.setAutoStates(Component.State.ACTIVE, true);
control.setHighlighted(true);
resetEventCount();
// Scenario 4: HOVER is not an auto-state.
control.setAutoStates(Component.State.HOVER, false);
testingEvents.fireMouseOutEvent(element, sandbox);
assertEquals(
'Control must dispatch LEAVE event on mouseout even if ' +
'HOVER is not an auto-state',
1, getEventCount(control, Component.EventType.LEAVE));
assertFalse(
'Control must be deactivated even if HOVER isn\'t an auto-' +
'state',
control.isActive());
assertTrue(
'Control must not be un-highlighted if HOVER isn\'t an auto-' +
'state',
control.isHighlighted());
control.setAutoStates(Component.State.HOVER, true);
control.setActive(true);
resetEventCount();
// Scenario 5: All is well.
testingEvents.fireMouseOutEvent(element, sandbox);
assertEquals(
'Control must dispatch LEAVE event on mouseout', 1,
getEventCount(control, Component.EventType.LEAVE));
assertEquals(
'Control must dispatch DEACTIVATE event on mouseout', 1,
getEventCount(control, Component.EventType.DEACTIVATE));
assertEquals(
'Control must dispatch UNHIGHLIGHT event on mouseout', 1,
getEventCount(control, Component.EventType.UNHIGHLIGHT));
assertFalse('Control must be deactivated', control.isActive());
assertFalse('Control must be unhighlighted', control.isHighlighted());
resetEventCount();
// Scenario 6: relatedTarget is null
control.setActive(true);
control.setHighlighted(true);
testingEvents.fireMouseOutEvent(element, null);
assertEquals(
'Control must dispatch LEAVE event on mouseout', 1,
getEventCount(control, Component.EventType.LEAVE));
assertEquals(
'Control must dispatch DEACTIVATE event on mouseout', 1,
getEventCount(control, Component.EventType.DEACTIVATE));
assertEquals(
'Control must dispatch UNHIGHLIGHT event on mouseout', 1,
getEventCount(control, Component.EventType.UNHIGHLIGHT));
assertFalse('Control must be deactivated', control.isActive());
assertFalse('Control must be unhighlighted', control.isHighlighted());
resetEventCount();
},
/**
@suppress {visibility,checkTypes} suppression added to enable type
checking
*/
testIsMouseEventWithinElement() {
const child = dom.createElement(TagName.DIV);
const parent = dom.createDom(TagName.DIV, null, child);
const notChild = dom.createElement(TagName.DIV);
let event = new GoogTestingEvent('mouseout');
event.relatedTarget = child;
assertTrue(
'Event is within element',
Control.isMouseEventWithinElement_(event, parent));
event = new GoogTestingEvent('mouseout');
event.relatedTarget = notChild;
assertFalse(
'Event is not within element',
Control.isMouseEventWithinElement_(event, parent));
},
testHandleMouseDown() {
control.render(sandbox);
assertFalse(
'preventDefault() must have been called for control that ' +
'doesn\'t support text selection',
fireMouseDownAndFocus(control.getElement()));
assertTrue('Control must be highlighted', control.isHighlighted());
assertTrue('Control must be active', control.isActive());
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(userAgent.IE);
try {
assertTrue('Control must be focused', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
},
testHandleMouseDownForDisabledControl() {
control.setEnabled(false);
control.render(sandbox);
assertFalse(
'preventDefault() must have been called for control that ' +
'doesn\'t support text selection',
fireMouseDownAndFocus(control.getElement()));
assertFalse('Control must not be highlighted', control.isHighlighted());
assertFalse('Control must not be active', control.isActive());
if (testFocus) {
assertFalse('Control must not be focused', control.isFocused());
}
},
testHandleMouseDownForNoHoverAutoState() {
control.setAutoStates(Component.State.HOVER, false);
control.render(sandbox);
assertFalse(
'preventDefault() must have been called for control that ' +
'doesn\'t support text selection',
fireMouseDownAndFocus(control.getElement()));
assertFalse('Control must not be highlighted', control.isHighlighted());
assertTrue('Control must be active', control.isActive());
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(userAgent.IE);
try {
assertTrue('Control must be focused', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
},
testHandleMouseDownForRightMouseButton() {
control.render(sandbox);
assertTrue(
'preventDefault() must not have been called for right ' +
'mouse button',
fireMouseDownAndFocus(
control.getElement(), BrowserEvent.MouseButton.RIGHT));
assertTrue('Control must be highlighted', control.isHighlighted());
assertFalse('Control must not be active', control.isActive());
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(userAgent.IE);
try {
assertTrue('Control must be focused', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
},
testHandleMouseDownForNoActiveAutoState() {
control.setAutoStates(Component.State.ACTIVE, false);
control.render(sandbox);
assertFalse(
'preventDefault() must have been called for control that ' +
'doesn\'t support text selection',
fireMouseDownAndFocus(control.getElement()));
assertTrue('Control must be highlighted', control.isHighlighted());
assertFalse('Control must not be active', control.isActive());
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(userAgent.IE);
try {
assertTrue('Control must be focused', control.isFocused());
} catch (e) {
expectedFailures.handleException(e);
}
}
},
testHandleMouseDownForNonFocusableControl() {
control.setSupportedState(Component.State.FOCUSED, false);
control.render(sandbox);
assertFalse(
'preventDefault() must have been called for control that ' +
'doesn\'t support text selection',
fireMouseDownAndFocus(control.getElement()));
assertTrue('Control must be highlighted', control.isHighlighted());
assertTrue('Control must be active', control.isActive());
assertFalse('Control must not be focused', control.isFocused());
},
// TODO(attila): Find out why this is flaky on FF2/Linux and FF1.5/Win.
// function testHandleMouseDownForSelectableControl() {
// control.setAllowTextSelection(true);
// control.render(sandbox);
// assertTrue('preventDefault() must not have been called for control ' +
// 'that supports text selection',
// fireMouseDownAndFocus(control.getElement()));
// assertTrue('Control must be highlighted', control.isHighlighted());
// assertTrue('Control must be active', control.isActive());
// // 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);
// try {
// assertTrue('Control must be focused', control.isFocused());
// } catch (e) {
// expectedFailures.handleException(e);
// }
//}
/** Tests {@link Control#handleMouseUp}. */
testHandleMouseUp() {
control.setActive(true);
// Override performActionInternal() for testing purposes.
let actionPerformed = false;
/** @suppress {visibility} suppression added to enable type checking */
control.performActionInternal = () => {
actionPerformed = true;
return true;
};
resetEventCount();
control.render(sandbox);
const element = control.getElement();
// Verify baseline assumptions.
assertTrue('Control must be enabled', control.isEnabled());
assertTrue(
'HOVER must be an auto-state',
control.isAutoState(Component.State.HOVER));
assertTrue(
'ACTIVE must be an auto-state',
control.isAutoState(Component.State.ACTIVE));
assertFalse(
'Control must not start out highlighted', control.isHighlighted());
assertTrue('Control must start out active', control.isActive());
// Scenario 1: Control is disabled.
control.setEnabled(false);
testingEvents.fireMouseUpEvent(element);
assertFalse(
'Disabled control must not highlight on mouseup',
control.isHighlighted());
assertFalse('No action must have been performed', actionPerformed);
control.setActive(true);
control.setEnabled(true);
// Scenario 2: HOVER is not an auto-state.
control.setAutoStates(Component.State.HOVER, false);
testingEvents.fireMouseUpEvent(element);
assertFalse(
'Control must not highlight on mouseup if HOVER isn\'t an ' +
'auto-state',
control.isHighlighted());
assertTrue(
'Action must have been performed even if HOVER isn\'t an ' +
'auto-state',
actionPerformed);
assertFalse(
'Control must have been deactivated on mouseup even if ' +
'HOVER isn\'t an auto-state',
control.isActive());
actionPerformed = false;
control.setActive(true);
control.setAutoStates(Component.State.HOVER, true);
// Scenario 3: Control is not active.
control.setActive(false);
testingEvents.fireMouseUpEvent(element);
assertTrue(
'Control must highlight on mouseup, even if inactive',
control.isHighlighted());
assertFalse(
'No action must have been performed if control is inactive',
actionPerformed);
assertFalse(
'Inactive control must remain inactive after mouseup',
control.isActive());
control.setHighlighted(false);
control.setActive(true);
// Scenario 4: performActionInternal() returns false.
/**
* @suppress {visibility,duplicate} suppression added to enable type
* checking
*/
control.performActionInternal = () => {
actionPerformed = true;
return false;
};
testingEvents.fireMouseUpEvent(element);
assertTrue(
'Control must highlight on mouseup, even if no action is ' +
'performed',
control.isHighlighted());
assertTrue('performActionInternal must have been called', actionPerformed);
assertTrue(
'Control must not deactivate if performActionInternal ' +
'returns false',
control.isActive());
control.setHighlighted(false);
actionPerformed = false;
/**
* @suppress {visibility,duplicate} suppression added to enable type
* checking
*/
control.performActionInternal = () => {
actionPerformed = true;
return true;
};
// Scenario 5: ACTIVE is not an auto-state.
control.setAutoStates(Component.State.ACTIVE, false);
testingEvents.fireMouseUpEvent(element);
assertTrue(
'Control must highlight on mouseup even if ACTIVE isn\'t an ' +
'auto-state',
control.isHighlighted());
assertTrue(
'Action must have been performed even if ACTIVE isn\'t an ' +
'auto-state',
actionPerformed);
assertTrue(
'Control must not have been deactivated on mouseup if ' +
'ACTIVE isn\'t an auto-state',
control.isActive());
actionPerformed = false;
control.setHighlighted(false);
control.setAutoStates(Component.State.ACTIVE, true);
// Scenario 6: All is well.
testingEvents.fireMouseUpEvent(element);
assertTrue('Control must highlight on mouseup', control.isHighlighted());
assertTrue('Action must have been performed', actionPerformed);
assertFalse('Control must have been deactivated', control.isActive());
},
testDefaultConstructor() {
const control = new Control();
assertNull(control.getContent());
},
testIeMouseEventSequenceSimulator() {
control.render(sandbox);
// Click sequences and isolated clicks must be handled correctly in any
// order.
assertClickSequenceFires('ACTION event expected after a click sequence');
assertClickSequenceFires(
'ACTION event expected after a second consecutive click sequence');
if (userAgent.IE) {
// For some reason in IE8 and perhaps earlier, isolated clicks do not
// result a detectable dispatch of an ACTION event, so we'll only assert
// the desired handling of isolated clicks in IE9 and higher.
if (userAgent.isVersionOrHigher(9)) {
assertIsolatedClickFires(
'ACTION event expected after an isolated click immediately ' +
'following a click sequence');
assertIsolatedClickFires(
'ACTION event expected after second consecutive isolated click');
} else {
// For IE8-and-lower, fire an isolated click event in preparation for
// our final assertion.
testingEvents.fireClickEvent(control.getKeyEventTarget());
}
} else {
assertIsolatedClickDoesNotFire(
'No ACTION event expected after an isolated click immediately ' +
'following a click sequence');
assertIsolatedClickDoesNotFire(
'No ACTION event expected after second consecutive isolated click');
}
assertClickSequenceFires(
'ACTION event expected after click sequence immediately following ' +
'an isolated click ');
},
testIeMouseEventSequenceSimulator_withPointerEvents() {
control.setPointerEventsEnabled(true);
control.render(sandbox);
if (userAgent.IE) {
// For some reason in IE8 and perhaps earlier, isolated clicks do not
// result a detectable dispatch of an ACTION event, so we'll only assert
// the desired handling of isolated clicks in IE9 and higher.
if (userAgent.isVersionOrHigher(9)) {
assertIsolatedClickFires(
'ACTION event expected after an isolated click immediately ' +
'following a click sequence');
assertIsolatedClickFires(
'ACTION event expected after second consecutive isolated click');
} else {
// For IE8-and-lower, fire an isolated click event in preparation for
// our final assertion.
testingEvents.fireClickEvent(control.getKeyEventTarget());
}
} else {
assertIsolatedClickDoesNotFire(
'No ACTION event expected after an isolated click immediately ' +
'following a click sequence');
assertIsolatedClickDoesNotFire(
'No ACTION event expected after second consecutive isolated click');
}
},
testIeMouseEventSequenceSimulatorStrictMode() {
if (!document.createEvent) {
return;
}
control.render(sandbox);
const actionCount = getEventCount(control, Component.EventType.ACTION);
const e = document.createEvent('MouseEvents');
e.initMouseEvent(
'click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false,
0, null);
control.getElementStrict().dispatchEvent(e);
if (userAgent.IE) {
assertEquals(
'ACTION event expected after an isolated click', actionCount + 1,
getEventCount(control, Component.EventType.ACTION));
} else {
assertEquals(
'No ACTION event expected after an isolated click', actionCount,
getEventCount(control, Component.EventType.ACTION));
}
},
testSetPointerEventsEnabled() {
control.setPointerEventsEnabled(true);
control.render(sandbox);
assertFalse(
'Control should not be active before pointerdown event.',
control.isActive());
const pointerdown = new GoogTestingEvent(
PointerFallbackEventType.POINTERDOWN, control.getElement());
pointerdown.button = BrowserEvent.MouseButton.LEFT;
testingEvents.fireBrowserEvent(pointerdown);
assertTrue(
'Control should be active after pointerdown event.',
control.isActive());
const pointerup = new GoogTestingEvent(
PointerFallbackEventType.POINTERUP, control.getElement());
pointerup.button = BrowserEvent.MouseButton.LEFT;
testingEvents.fireBrowserEvent(pointerup);
assertFalse(
'Control should not be active after pointerup event.',
control.isActive());
},
});