blob: 39c79d1112e1681f7668059da3ba5cee1bb01f89 [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* 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.
*/
import './common-test-setup';
import '@polymer/test-fixture/test-fixture';
import 'chai/chai';
declare global {
interface Window {
flush: typeof flushImpl;
fixtureFromTemplate: typeof fixtureFromTemplateImpl;
fixtureFromElement: typeof fixtureFromElementImpl;
}
let flush: typeof flushImpl;
let fixtureFromTemplate: typeof fixtureFromTemplateImpl;
let fixtureFromElement: typeof fixtureFromElementImpl;
}
// Workaround for https://github.com/karma-runner/karma-mocha/issues/227
let unhandledError: ErrorEvent;
window.addEventListener('error', e => {
// For uncaught error mochajs doesn't print the full stack trace.
// We should print it ourselves.
console.error('Uncaught error:');
console.error(e.error.stack.toString());
unhandledError = e;
});
let originalOnBeforeUnload: typeof window.onbeforeunload;
suiteSetup(() => {
// This suiteSetup() method is called only once before all tests
// Can't use window.addEventListener("beforeunload",...) here,
// the handler is raised too late.
originalOnBeforeUnload = window.onbeforeunload;
window.onbeforeunload = function (e: BeforeUnloadEvent) {
// If a test reloads a page, we can't prevent it.
// However we can print an error and the stack trace with assert.fail
try {
throw new Error();
} catch (e) {
console.error('Page reloading attempt detected.');
console.error(e.stack.toString());
}
if (originalOnBeforeUnload) {
originalOnBeforeUnload.call(window, e);
}
};
});
suiteTeardown(() => {
// This suiteTeardown() method is called only once after all tests
window.onbeforeunload = originalOnBeforeUnload;
if (unhandledError) {
throw unhandledError;
}
});
// Tests can use fake timers (sandbox.useFakeTimers)
// Keep the original one for use in test utils methods.
const nativeSetTimeout = window.setTimeout;
function flushImpl(): Promise<void>;
function flushImpl(callback: () => void): void;
/**
* Triggers a flush of any pending events, observations, etc and calls you back
* after they have been processed if callback is passed; otherwise returns
* promise.
*/
function flushImpl(callback?: () => void): Promise<void> | void {
// Ideally, this function would be a call to Polymer.dom.flush, but that
// doesn't support a callback yet
// (https://github.com/Polymer/polymer-dev/issues/851)
// The type is used only in one place, disable eslint warning instead of
// creating an interface
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).Polymer.dom.flush();
if (callback) {
nativeSetTimeout(callback, 0);
} else {
return new Promise(resolve => {
nativeSetTimeout(resolve, 0);
});
}
}
self.flush = flushImpl;
class TestFixtureIdProvider {
public static readonly instance: TestFixtureIdProvider =
new TestFixtureIdProvider();
private fixturesCount = 1;
generateNewFixtureId() {
this.fixturesCount++;
return `fixture-${this.fixturesCount}`;
}
}
interface TagTestFixture<T extends Element> {
instantiate(model?: unknown): T;
}
class TestFixture {
constructor(readonly fixtureId: string) {}
/**
* Create an instance of a fixture's template.
*
* @param model - see Data-bound sections at
* https://www.webcomponents.org/element/@polymer/test-fixture
* @return - if the fixture's template contains
* a single element, returns the appropriated instantiated element.
* Otherwise, it return an array of all instantiated elements from the
* template.
*/
instantiate(model?: unknown): HTMLElement | HTMLElement[] {
// The window.fixture method is defined in common-test-setup.js
return window.fixture(this.fixtureId, model);
}
}
/**
* Wraps provided template to a test-fixture tag and adds test-fixture to
* the document. You can use the html function to create a template.
*
* Example:
* import {html} from '@polymer/polymer/lib/utils/html-tag.js';
*
* // Create fixture at the root level of a test file
* const basicTestFixture = fixtureFromTemplate(html`
* <gr-cursor-manager cursor-target-class="targeted"></gr-cursor-manager>
* <ul>
* <li>A</li>
* <li>B</li>
* <li>C</li>
* <li>D</li>
* </ul>
* `);
* ...
* // Instantiate fixture when needed:
*
* suite('example') {
* let elements;
* setup(() => {
* elements = basicTestFixture.instantiate();
* });
* }
*
* @param template - a template for a fixture
*/
function fixtureFromTemplateImpl(template: HTMLTemplateElement): TestFixture {
const fixtureId = TestFixtureIdProvider.instance.generateNewFixtureId();
const testFixture = document.createElement('test-fixture');
testFixture.setAttribute('id', fixtureId);
testFixture.appendChild(template);
document.body.appendChild(testFixture);
return new TestFixture(fixtureId);
}
/**
* Wraps provided tag to a test-fixture/template tags and adds test-fixture
* to the document.
*
* Example:
*
* // Create fixture at the root level of a test file
* const basicTestFixture = fixtureFromElement('gr-diff-view');
* ...
* // Instantiate fixture when needed:
*
* suite('example') {
* let element;
* setup(() => {
* element = basicTestFixture.instantiate();
* });
* }
*
* @param tagName - a template for a fixture is <tagName></tagName>
*/
function fixtureFromElementImpl<T extends keyof HTMLElementTagNameMap>(
tagName: T
): TagTestFixture<HTMLElementTagNameMap[T]> {
const template = document.createElement('template');
template.innerHTML = `<${tagName}></${tagName}>`;
return fixtureFromTemplate(template) as unknown as TagTestFixture<
HTMLElementTagNameMap[T]
>;
}
window.fixtureFromTemplate = fixtureFromTemplateImpl;
window.fixtureFromElement = fixtureFromElementImpl;