blob: cbf1785306882a232986d9f08280b297f82bbdce [file]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// clang-format off
// #import {afterNextRender, beforeNextRender, flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
// clang-format on
cr.define('test_util', function() {
/**
* Observes an HTML attribute and fires a promise when it matches a given
* value.
* @param {!HTMLElement} target
* @param {string} attributeName
* @param {*} attributeValue
* @return {!Promise}
*/
/* #export */ function whenAttributeIs(
target, attributeName, attributeValue) {
function isDone() {
return target.getAttribute(attributeName) == attributeValue;
}
return isDone() ? Promise.resolve() : new Promise(function(resolve) {
new MutationObserver(function(mutations, observer) {
for (const mutation of mutations) {
assertEquals('attributes', mutation.type);
if (mutation.attributeName == attributeName && isDone()) {
observer.disconnect();
resolve();
return;
}
}
}).observe(target, {
attributes: true,
childList: false,
characterData: false
});
});
}
/**
* Converts an event occurrence to a promise.
* @param {string} eventType
* @param {!HTMLElement} target
* @return {!Promise} A promise firing once the event occurs.
*/
/* #export */ function eventToPromise(eventType, target) {
return new Promise(function(resolve, reject) {
target.addEventListener(eventType, function f(e) {
target.removeEventListener(eventType, f);
resolve(e);
});
});
}
/**
* Data-binds two Polymer properties using the property-changed events and
* set/notifyPath API. Useful for testing components which would normally be
* used together.
* @param {!HTMLElement} el1
* @param {!HTMLElement} el2
* @param {string} property
*/
/* #export */ function fakeDataBind(el1, el2, property) {
const forwardChange = function(el, event) {
if (event.detail.hasOwnProperty('path')) {
el.notifyPath(event.detail.path, event.detail.value);
} else {
el.set(property, event.detail.value);
}
};
// Add the listeners symmetrically. Polymer will prevent recursion.
el1.addEventListener(property + '-changed', forwardChange.bind(null, el2));
el2.addEventListener(property + '-changed', forwardChange.bind(null, el1));
}
/**
* Converts beforeNextRender() API to promise-based.
* @param {!Element} element
* @return {!Promise}
*/
/* #export */ function waitBeforeNextRender(element) {
return new Promise(resolve => {
Polymer.RenderStatus.beforeNextRender(element, resolve);
});
}
/**
* @param {!HTMLElement} element
* @return {!Promise} Promise that resolves when an afterNextRender()
* callback on |element| is run.
*/
/* #export */ function waitAfterNextRender(element) {
return new Promise(resolve => {
Polymer.RenderStatus.afterNextRender(element, resolve);
});
}
/*
* Waits for queued up tasks to finish before proceeding. Inspired by:
* https://github.com/Polymer/web-component-tester/blob/master/browser/environment/helpers.js#L97
*/
/* #export */ function flushTasks() {
Polymer.dom.flush();
// Promises have microtask timing, so we use setTimeout to explicitly force
// a new task.
return new Promise(function(resolve, reject) {
window.setTimeout(resolve, 0);
});
}
/**
* Returns whether or not the element specified is visible.
* @param {!HTMLElement} element
* @return {boolean}
*/
/* #export */ function isVisible(element) {
const rect = element ? element.getBoundingClientRect() : null;
return (!!rect && rect.width * rect.height > 0);
}
/**
* Searches the DOM of the parentEl element for a child matching the provided
* selector then checks the visibility of the child.
* @param {!HTMLElement} parentEl
* @param {string} selector
* @param {boolean=} checkLightDom
* @return {boolean}
*/
/* #export */ function isChildVisible(parentEl, selector, checkLightDom) {
const element = (checkLightDom ? parentEl.querySelector : parentEl.$$)
.call(parentEl, selector);
return isVisible(element);
}
// #cr_define_end
return {
eventToPromise: eventToPromise,
fakeDataBind: fakeDataBind,
flushTasks: flushTasks,
isVisible: isVisible,
isChildVisible: isChildVisible,
waitAfterNextRender: waitAfterNextRender,
waitBeforeNextRender: waitBeforeNextRender,
whenAttributeIs: whenAttributeIs,
};
});