blob: 19131ac70afc7d11a86ffd51a8500d3e77c99859 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
function TobagoTestTool(assert) {
this.assert = assert;
this.steps = [];
}
TobagoTestTool.stepType = {
ACTION: 1,
WAIT_RESPONSE: 2,
WAIT_MS: 3,
ASSERTS: 4
};
class JasmineUtils {
static isMsie() {
return navigator.userAgent.indexOf("MSIE") > -1 || navigator.userAgent.indexOf("Trident") > -1;
};
static checkGridCss(element, columnStart, columnEnd, rowStart, rowEnd) {
columnEnd = this.convertGridCss(columnEnd);
rowEnd = this.convertGridCss(rowEnd);
if (this.isMsie()) {
expect(getComputedStyle(element).msGridColumn).toBe(columnStart);
expect(getComputedStyle(element).msGridColumnSpan).toBe(columnEnd);
expect(getComputedStyle(element).msGridRow).toBe(rowStart);
expect(getComputedStyle(element).msGridRowSpan).toBe(rowEnd);
} else {
expect(getComputedStyle(element).gridColumnStart).toBe(columnStart);
expect(getComputedStyle(element).gridColumnEnd).toBe(columnEnd);
expect(getComputedStyle(element).gridRowStart).toBe(rowStart);
expect(getComputedStyle(element).gridRowEnd).toBe(rowEnd);
}
};
static convertGridCss(end) {
if (JasmineTestTool.msie) {
switch (end) {
case "auto":
return "1";
case "span 2":
return "2";
case "span 3":
return "3";
case "span 4":
return "4";
default:
return end;
}
} else {
return end;
}
};
}
TobagoTestTool.prototype = {
action: function (func) {
this.steps.push({
type: TobagoTestTool.stepType.ACTION,
func: func
});
},
waitForResponse: function () {
this.steps.push({
type: TobagoTestTool.stepType.WAIT_RESPONSE
});
},
waitMs: function (ms) {
this.steps.push({
type: TobagoTestTool.stepType.WAIT_MS,
ms: ms ? ms : 0
});
},
asserts: function (numOfAssertions, func) {
this.steps.push({
type: TobagoTestTool.stepType.ASSERTS,
numOfAssertions: numOfAssertions ? numOfAssertions : 0,
func: func
});
},
startTest: function () {
const steps = this.steps.slice(0);
const cycleTiming = 50;
let currentStep = 0;
let testStepTimeout;
function getAssertExpect() {
var expect = 0;
steps.forEach(function (step) {
if (step.type === TobagoTestTool.stepType.ASSERTS) {
expect += step.numOfAssertions;
}
});
return expect;
}
function getAssertAsync() {
var async = 0;
steps.forEach(function (step) {
if (step.type === TobagoTestTool.stepType.ASSERTS) {
async++;
}
});
return async;
}
this.assert.expect(getAssertExpect());
const done = this.assert.async(getAssertAsync());
const assert = this.assert;
function resetTestStepTimeout(additionalMs) {
const timeout = additionalMs ? 20000 + additionalMs : 20000;
testStepTimeout = Date.now() + timeout;
}
let waitForResponse = false;
let ajaxRequestDetected = false;
let ajaxRequestDone = false;
let fullPageReloadDetected = false;
let fullPageReloadDone = false;
function registerAjaxReadyStateListener() {
let oldXHR = document.getElementById("page:testframe").contentWindow.XMLHttpRequest;
function newXHR() {
let realXHR = new oldXHR();
realXHR.addEventListener("readystatechange", function () {
if (realXHR.readyState !== XMLHttpRequest.UNSENT && realXHR.readyState !== XMLHttpRequest.DONE) {
ajaxRequestDetected = true;
} else if (ajaxRequestDetected && realXHR.readyState === XMLHttpRequest.DONE) {
ajaxRequestDone = true;
waitForResponse = false;
}
}, false);
return realXHR;
}
document.getElementById("page:testframe").contentWindow.XMLHttpRequest = newXHR;
}
function fullPageReloadPolling() {
const testframe = document.getElementById("page:testframe");
if (testframe === null
|| testframe.contentWindow.document.readyState !== "complete"
|| testframe.contentWindow.document.querySelector("html") === null) {
fullPageReloadDetected = true;
} else if (fullPageReloadDetected) {
fullPageReloadDone = true;
waitForResponse = false;
}
if (!fullPageReloadDone && !ajaxRequestDone) {
setTimeout(fullPageReloadPolling, cycleTiming);
}
}
function cycle() {
if (currentStep >= steps.length) {
// we are done here
} else if (Date.now() >= testStepTimeout) {
assert.ok(false, "Timeout!");
if (steps[currentStep].stepType === TobagoTestTool.stepType.ASSERTS) {
done();
}
currentStep++;
cycle();
} else if (waitForResponse) {
// we need to wait more
setTimeout(cycle, cycleTiming);
} else if (steps[currentStep].type === TobagoTestTool.stepType.ACTION) {
if (currentStep + 1 < steps.length && steps[currentStep + 1].type === TobagoTestTool.stepType.WAIT_RESPONSE) {
// register listener for ajax before action is executed, otherwise the ajax listener is registered too late
registerAjaxReadyStateListener();
steps[currentStep].func();
currentStep++;
cycle();
} else {
steps[currentStep].func();
currentStep++;
resetTestStepTimeout();
setTimeout(cycle, cycleTiming);
}
} else if (steps[currentStep].type === TobagoTestTool.stepType.WAIT_RESPONSE) {
waitForResponse = true;
ajaxRequestDetected = false;
ajaxRequestDone = false;
fullPageReloadDetected = false;
fullPageReloadDone = false;
registerAjaxReadyStateListener();
fullPageReloadPolling();
currentStep++;
resetTestStepTimeout();
setTimeout(cycle, cycleTiming);
} else if (steps[currentStep].type === TobagoTestTool.stepType.WAIT_MS) {
const ms = steps[currentStep].ms;
currentStep++;
resetTestStepTimeout(ms);
setTimeout(cycle, ms);
} else if (steps[currentStep].type === TobagoTestTool.stepType.ASSERTS) {
steps[currentStep].func();
currentStep++;
done();
resetTestStepTimeout();
setTimeout(cycle, cycleTiming);
}
}
resetTestStepTimeout();
cycle();
}
};
export {TobagoTestTool};
class JasmineTestTool {
static ajaxReadyStateChangeEvent = "tobago.jtt.ajax.readyStateChange";
static ajaxReadyState;
steps = [];
done;
timeout;
lastStepExecution;
/**
* @param done function from Jasmine; must called if all Steps done or timeout
* @param timeout for a single step; default 20000ms
*/
constructor(done, timeout) {
this.done = done;
this.timeout = timeout ? timeout : 20000;
this.registerAjaxReadyStateListener();
}
/**
* The condition function (fn1) defines the desired state before the main test starts. The do function (fn2) will be
* executed if the condition function returns false.
* @param fn1 condition function
* @param fn2 do function
*/
setup(fn1, fn2) {
this.do(() => {
if (!fn1()) {
console.debug("[JasmineTestTool] fn1() returns false, execute fn2()");
fn2();
}
})
this.wait(fn1);
}
/**
* Execute dispatchEvent() on the given element. The result function must return false before dispatch.
* After the dispatch
* @param type of the event
* @param element function
* @param result function
*/
event(type, element, result) {
this.do(() => {
if (result()) {
fail("The result function (" + result + ") returns true BEFORE the '" + type + "' event is dispatched."
+ " Please define a result function that return false before dispatch event and return true after dispatch"
+ " event.");
}
});
this.do(() => element().dispatchEvent(new Event(type, {bubbles: true})));
this.wait(result);
}
do(fn) {
this.steps.push({
type: "do",
func: fn,
done: false
});
}
wait(fn) {
this.steps.push({
type: "wait",
func: fn,
done: false
});
}
start() {
console.debug("[JasmineTestTool] start");
this.resetTimeout();
this.cycle();
}
cycle() {
const nextStep = this.getNextStep();
if (this.isFinished()) {
this.done();
} else if (this.isTimeout()) {
fail("Timeout of '" + nextStep.type + "'-step: " + nextStep.func);
nextStep.done = true;
this.resetTimeout();
window.setTimeout(this.cycle.bind(this), 0);
} else if (!this.isDocumentReady() || !this.isAjaxReady()) {
console.debug("[JasmineTestTool] documentReady: " + this.isDocumentReady()
+ " - ajaxReady: " + this.isAjaxReady());
window.setTimeout(this.cycle.bind(this), 50);
} else if (nextStep.type === "do") {
console.debug("[JasmineTestTool] do-step: " + nextStep.func);
this.registerCustomXmlHttpRequest();
nextStep.func();
nextStep.done = true;
this.resetTimeout();
window.setTimeout(this.cycle.bind(this), 0);
} else if (nextStep.type === "wait") {
console.debug("[JasmineTestTool] wait-step: " + nextStep.func);
if (nextStep.func()) {
nextStep.done = true;
this.resetTimeout();
}
window.setTimeout(this.cycle.bind(this), 50);
} else {
fail("an unexpected error has occurred!");
this.done();
}
}
isFinished() {
for (let step of this.steps) {
if (!step.done) {
return false;
}
}
return true;
}
isDocumentReady() {
return document.getElementById("page:testframe").contentWindow.document.readyState === "complete";
}
registerAjaxReadyStateListener() {
JasmineTestTool.ajaxReadyState = XMLHttpRequest.UNSENT;
window.removeEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
window.addEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
}
static changeAjaxReadyState(event) {
JasmineTestTool.ajaxReadyState = event.detail.readyState;
console.debug("[JasmineTestTool] ajaxReadyState: " + JasmineTestTool.ajaxReadyState);
}
registerCustomXmlHttpRequest() {
class JasmineXMLHttpRequest extends XMLHttpRequest {
constructor() {
super();
this.addEventListener("readystatechange", function () {
window.dispatchEvent(new CustomEvent(JasmineTestTool.ajaxReadyStateChangeEvent,
{detail: {readyState: this.readyState}}));
});
}
}
document.getElementById("page:testframe").contentWindow.XMLHttpRequest = JasmineXMLHttpRequest;
}
isAjaxReady() {
return JasmineTestTool.ajaxReadyState === XMLHttpRequest.UNSENT
|| JasmineTestTool.ajaxReadyState === XMLHttpRequest.DONE;
}
getNextStep() {
for (let step of this.steps) {
if (!step.done) {
return step;
}
}
return null;
}
isTimeout() {
return Date.now() > (this.lastStepExecution + this.timeout);
}
resetTimeout() {
this.lastStepExecution = Date.now();
}
}
export {JasmineUtils, JasmineTestTool};