blob: 8c1c4ee1a97681f82f2584211fb323d171365a54 [file] [log] [blame]
/**
* @license
* Copyright (C) 2017 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.
*/
// This should be the first import to install handler before any other code
import './source-map-support-install';
// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
import '../scripts/bundled-polymer';
import '@polymer/iron-test-helpers/iron-test-helpers';
import './test-router';
import {_testOnlyInitAppContext} from './test-app-context-init';
import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader';
import {_testOnlyResetRestApi} from '../elements/shared/gr-js-api-interface/gr-plugin-rest-api';
import {_testOnlyResetGrRestApiSharedObjects} from '../elements/shared/gr-rest-api-interface/gr-rest-api-interface';
import {
cleanupTestUtils,
getCleanupsCount,
registerTestCleanup,
addIronOverlayBackdropStyleEl,
removeIronOverlayBackdropStyleEl,
TestKeyboardShortcutBinder,
} from './test-utils';
import {flushDebouncers} from '@polymer/polymer/lib/utils/debounce';
import {_testOnly_getShortcutManagerInstance} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import sinon, {SinonSpy} from 'sinon/pkg/sinon-esm';
import {safeTypesBridge} from '../utils/safe-types-util';
import {_testOnly_initGerritPluginApi} from '../elements/shared/gr-js-api-interface/gr-gerrit';
import {initGlobalVariables} from '../elements/gr-app-global-var-init';
import 'chai/chai';
import {
_testOnly_defaultResinReportHandler,
installPolymerResin,
} from '../scripts/polymer-resin-install';
import {hasOwnProperty} from '../utils/common-util';
declare global {
interface Window {
assert: typeof chai.assert;
expect: typeof chai.expect;
fixture: typeof fixtureImpl;
stub: typeof stubImpl;
sinon: typeof sinon;
}
let assert: typeof chai.assert;
let expect: typeof chai.expect;
let stub: typeof stubImpl;
let sinon: typeof sinon;
}
window.assert = chai.assert;
window.expect = chai.expect;
window.sinon = sinon;
installPolymerResin(safeTypesBridge, (isViolation, fmt, ...args) => {
const log = _testOnly_defaultResinReportHandler;
log(isViolation, fmt, ...args);
if (isViolation) {
// This will cause the test to fail if there is a data binding
// violation.
throw new Error('polymer-resin violation: ' + fmt + JSON.stringify(args));
}
});
interface TestFixtureElement extends HTMLElement {
restore(): void;
create(model?: unknown): HTMLElement | HTMLElement[];
}
function getFixtureElementById(fixtureId: string) {
return document.getElementById(fixtureId) as TestFixtureElement;
}
// For karma always set our implementation
// (karma doesn't provide the fixture method)
function fixtureImpl(fixtureId: string, model: unknown) {
// This method is inspired by web-component-tester method
registerTestCleanup(() => getFixtureElementById(fixtureId).restore());
return getFixtureElementById(fixtureId).create(model);
}
window.fixture = fixtureImpl;
setup(() => {
window.Gerrit = {};
initGlobalVariables();
addIronOverlayBackdropStyleEl();
// If the following asserts fails - then window.stub is
// overwritten by some other code.
assert.equal(getCleanupsCount(), 0);
// The following calls is nessecary to avoid influence of previously executed
// tests.
TestKeyboardShortcutBinder.push();
_testOnlyInitAppContext();
_testOnly_initGerritPluginApi();
const mgr = _testOnly_getShortcutManagerInstance();
assert.isTrue(mgr._testOnly_isEmpty());
const selection = document.getSelection();
if (selection) {
selection.removeAllRanges();
}
const pl = _testOnly_resetPluginLoader();
// For testing, always init with empty plugin list
// Since when serve in gr-app, we always retrieve the list
// from project config and init loading after that, all
// `awaitPluginsLoaded` will rely on that to kick off,
// in testing, we want to kick start this earlier.
// You still can manually call _testOnly_resetPluginLoader
// to reset this behavior if you need to test something specific.
pl.loadPlugins([]);
_testOnlyResetGrRestApiSharedObjects();
_testOnlyResetRestApi();
});
// For karma always set our implementation
// (karma doesn't provide the stub method)
function stubImpl<T extends keyof HTMLElementTagNameMap>(
tagName: T,
implementation: Partial<HTMLElementTagNameMap[T]>
) {
// This method is inspired by web-component-tester method
const proto = document.createElement(tagName).constructor
.prototype as HTMLElementTagNameMap[T];
let key: keyof HTMLElementTagNameMap[T];
const stubs: SinonSpy[] = [];
for (key in implementation) {
if (hasOwnProperty(implementation, key)) {
stubs.push(sinon.stub(proto, key).callsFake(implementation[key]));
}
}
registerTestCleanup(() => {
stubs.forEach(stub => {
stub.restore();
});
});
}
window.stub = stubImpl;
// Very simple function to catch unexpected elements in documents body.
// It can't catch everything, but in most cases it is enough.
function checkChildAllowed(element: Element) {
const allowedTags = ['SCRIPT', 'IRON-A11Y-ANNOUNCER'];
if (allowedTags.includes(element.tagName)) {
return;
}
if (element.tagName === 'TEST-FIXTURE') {
if (
element.children.length === 0 ||
(element.children.length === 1 &&
element.children[0].tagName === 'TEMPLATE')
) {
return;
}
assert.fail(
`Test fixture
${element.outerHTML}` +
"isn't resotred after the test is finished. Please ensure that " +
'restore() method is called for this test-fixture. Usually the call' +
'happens automatically.'
);
return;
}
if (
element.tagName === 'DIV' &&
element.id === 'gr-hovercard-container' &&
element.childNodes.length === 0
) {
return;
}
assert.fail(
`The following node remains in document after the test:
${element.tagName}
Outer HTML:
${element.outerHTML}`
);
}
function checkGlobalSpace() {
for (const child of document.body.children) {
checkChildAllowed(child);
}
}
teardown(() => {
sinon.restore();
cleanupTestUtils();
TestKeyboardShortcutBinder.pop();
checkGlobalSpace();
removeIronOverlayBackdropStyleEl();
// Clean Polymer debouncer queue, so next tests will not be affected.
flushDebouncers();
});