1586 lines
54 KiB
HTML
1586 lines
54 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Session Configuration - Web Platform Test</title>
|
|
<link rel="stylesheet" href="css/bulma-0.7.5/bulma.min.css" />
|
|
<link rel="stylesheet" href="css/fontawesome-5.7.2.min.css" />
|
|
<script src="lib/utils.js"></script>
|
|
<script src="lib/wave-service.js"></script>
|
|
<script src="lib/ui.js"></script>
|
|
<style>
|
|
.site-logo {
|
|
max-width: 300px;
|
|
margin: 0 0 30px -15px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<script>
|
|
// var apis = [
|
|
// { title: "2D Context", path: "/2dcontext" },
|
|
// { title: "Content Security Policy", path: "/content-security-policy" },
|
|
// { title: "CSS", path: "/css" },
|
|
// { title: "DOM", path: "/dom" },
|
|
// { title: "ECMAScript", path: "/ecmascript" },
|
|
// { title: "Encrypted media", path: "/encrypted-media" },
|
|
// { title: "Fetch", path: "/fetch" },
|
|
// { title: "FileAPI", path: "/FileAPI" },
|
|
// { title: "Fullscreen", path: "/fullscreen" },
|
|
// { title: "WebGL", path: "/webgl" },
|
|
// { title: "HTML", path: "/html" },
|
|
// { title: "IndexedDB", path: "/IndexedDB" },
|
|
// { title: "Media Source", path: "/media-source" },
|
|
// { title: "Notifications", path: "/notifications" },
|
|
// { title: "Page Visibility", path: "/page-visibility" },
|
|
// { title: "Service Workers", path: "/service-workers" },
|
|
// { title: "UI Events", path: "/uievents" },
|
|
// { title: "WAVE Extra", path: "/wave-extra" },
|
|
// { title: "Webaudio", path: "/webaudio" },
|
|
// { title: "WebCryptoAPI", path: "/WebCryptoAPI" },
|
|
// { title: "Webmessaging", path: "/webmessaging" },
|
|
// { title: "Websockets", path: "/websockets" },
|
|
// { title: "Webstorage", path: "/webstorage" },
|
|
// { title: "Workers", path: "/workers" },
|
|
// { title: "XHR", path: "/xhr" }
|
|
// ];
|
|
|
|
// var referenceSessions = [
|
|
// {
|
|
// title: "Edge 44.17763",
|
|
// engine: "",
|
|
// token: "b2924d20-6a93-11e9-98b4-a11fb92a6d1c",
|
|
// icon: "fab fa-edge"
|
|
// },
|
|
// {
|
|
// title: "Firefox 64.0",
|
|
// engine: "Gecko 64.0",
|
|
// token: "bb7aafa0-6a92-11e9-8ec2-04f58dad2e4f",
|
|
// icon: "fab fa-firefox"
|
|
// },
|
|
// {
|
|
// title: "WebKit 605.1.15",
|
|
// engine: "Revision 239158",
|
|
// token: "caf823e0-6a92-11e9-b732-3188d0065ebc",
|
|
// icon: "fab fa-safari"
|
|
// },
|
|
// {
|
|
// title: "Chromium 73.0.3640.0",
|
|
// engine: "Blink 537.36",
|
|
// token: "a50c6db0-6a94-11e9-8d1b-e23fc4555885",
|
|
// icon: "fab fa-chrome"
|
|
// }
|
|
// ];
|
|
|
|
var testFileSelectionEnabled = true;
|
|
|
|
window.onload = function () {
|
|
new ConfigurationView();
|
|
};
|
|
|
|
function ConfigurationView() {
|
|
const query = utils.parseQuery(location.search);
|
|
var token = query.token;
|
|
if (token) WaveService.addRecentSession(token);
|
|
var referenceSessions = [];
|
|
var apis = [];
|
|
var testTypeSelectionEnabled = true;
|
|
var testFileSelectionEnabled = false;
|
|
|
|
var types = [
|
|
{ title: "Automatic", value: "automatic" },
|
|
{ title: "Manual", value: "manual" },
|
|
];
|
|
var state = {};
|
|
loadServerStatus();
|
|
loadSessionData(token, function () {
|
|
loadPublicSessionData(function () {
|
|
renderReferencesField();
|
|
});
|
|
});
|
|
render();
|
|
|
|
function loadServerStatus() {
|
|
WaveService.readStatus(function (status) {
|
|
testTypeSelectionEnabled = status.testTypeSelectionEnabled;
|
|
testFileSelectionEnabled = status.testFileSelectionEnabled;
|
|
renderSessionConfiguration();
|
|
});
|
|
}
|
|
|
|
function loadSessionData(token, callback) {
|
|
if (!token) {
|
|
state.expired = true;
|
|
return;
|
|
}
|
|
WaveService.readSessionStatus(
|
|
token,
|
|
function (status) {
|
|
if (status.status !== "pending") {
|
|
openResultsPage(token);
|
|
return;
|
|
}
|
|
state.status = status;
|
|
|
|
WaveService.readSession(token, function (configuration) {
|
|
if (
|
|
configuration.tests.include.findIndex(
|
|
(test) => test === "/"
|
|
) !== -1
|
|
) {
|
|
configuration.tests.include = apis.map((api) => api.path);
|
|
}
|
|
state.configurationBackup = utils.copyObject(configuration);
|
|
state.configuration = configuration;
|
|
setTimeout(
|
|
handleExpiration,
|
|
status.expirationDate.getTime() - Date.now()
|
|
);
|
|
renderSessionConfiguration();
|
|
callback();
|
|
WaveService.readAvailableApis(function (available_apis) {
|
|
available_apis = available_apis.sort((apiA, apiB) =>
|
|
apiA.title.toLowerCase() > apiB.title.toLowerCase() ? 1 : -1
|
|
);
|
|
apis = available_apis;
|
|
selectAllTests();
|
|
renderSessionConfiguration();
|
|
});
|
|
});
|
|
},
|
|
function () {
|
|
state.expired = true;
|
|
renderSessionConfiguration();
|
|
renderExcludedTests();
|
|
renderResumeView();
|
|
}
|
|
);
|
|
}
|
|
|
|
function loadPublicSessionData(callback) {
|
|
WaveService.readPublicSessions(function (tokens) {
|
|
WaveService.readMultipleSessions(tokens, function (configurations) {
|
|
console.log(configurations);
|
|
referenceSessions = configurations
|
|
.sort((confA, confB) =>
|
|
confA.browser.name.toLowerCase() >
|
|
confB.browser.name.toLowerCase()
|
|
? 1
|
|
: -1
|
|
)
|
|
.map((configuration) => {
|
|
var icon = "";
|
|
switch (configuration.browser.name.toLowerCase()) {
|
|
case "firefox":
|
|
icon = "fab fa-firefox";
|
|
break;
|
|
case "webkit":
|
|
case "safari":
|
|
icon = "fab fa-safari";
|
|
break;
|
|
case "edge":
|
|
icon = "fab fa-edge";
|
|
break;
|
|
case "chrome":
|
|
case "chromium":
|
|
icon = "fab fa-chrome";
|
|
break;
|
|
}
|
|
return {
|
|
title:
|
|
configuration.browser.name +
|
|
" " +
|
|
configuration.browser.version,
|
|
token: configuration.token,
|
|
icon,
|
|
engine: configuration.browser.engine || "",
|
|
};
|
|
});
|
|
callback(referenceSessions);
|
|
});
|
|
});
|
|
}
|
|
|
|
function handleConfigureSession() {
|
|
const tokenFragmentInput = UI.getElement("token-fragment");
|
|
const fragment = tokenFragmentInput.value;
|
|
findSession(fragment, function (session) {
|
|
if (!session) {
|
|
const errorBox = UI.getElement("find-error");
|
|
errorBox.setAttribute("style", "display: block");
|
|
return;
|
|
}
|
|
tokenFragmentInput.value = "";
|
|
const errorBox = UI.getElement("find-error");
|
|
errorBox.setAttribute("style", "display: none");
|
|
const path = location.pathname + "?token=" + session.token;
|
|
location.href = path;
|
|
});
|
|
}
|
|
|
|
function findSession(fragment, callback) {
|
|
if (!fragment || fragment.length < 8) {
|
|
callback(null);
|
|
return;
|
|
}
|
|
WaveService.findToken(
|
|
fragment,
|
|
function (token) {
|
|
WaveService.readSession(token, function (session) {
|
|
WaveService.readSessionStatus(token, function (status) {
|
|
session.status = status.status;
|
|
session.dateStarted = status.dateStarted;
|
|
session.dateFinished = status.dateFinished;
|
|
callback(session);
|
|
});
|
|
});
|
|
},
|
|
function () {
|
|
callback(null);
|
|
}
|
|
);
|
|
}
|
|
|
|
function hasIncludedTest(path) {
|
|
var tests = state.configuration.tests;
|
|
//var index = tests.include.findIndex(function (test) {
|
|
// return test.match(new RegExp("^" + path));
|
|
//});
|
|
var index = tests.include.indexOf(path);
|
|
return index !== -1;
|
|
}
|
|
|
|
function handleIncludedTestToggle(path) {
|
|
var configuration = state.configuration;
|
|
if (hasIncludedTest(path)) {
|
|
handleRemoveIncludedTest(path);
|
|
} else {
|
|
handleAddIncludedTest(path);
|
|
}
|
|
}
|
|
|
|
function handleAddIncludedTest(path) {
|
|
var tests = state.configuration.tests;
|
|
if (state.tests && state.tests[path.substr(1)]) {
|
|
tests.include = tests.include.filter(function (test) {
|
|
return !test.match(new RegExp("^" + path + "/"));
|
|
});
|
|
tests.include = tests.include.concat(state.tests[path.substr(1)]);
|
|
} else {
|
|
tests.include.push(path);
|
|
}
|
|
}
|
|
|
|
function handleRemoveIncludedTest(path) {
|
|
var tests = state.configuration.tests;
|
|
|
|
if (state.tests && state.tests[path.substr(1)]) {
|
|
tests.include = tests.include.filter(function (test) {
|
|
return !test.match(new RegExp("^" + path + "/"));
|
|
});
|
|
} else {
|
|
var index = tests.include.findIndex((test) => test === path);
|
|
tests.include.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
function getIncludedRatio(path) {
|
|
var includedTests = state.configuration.tests.include;
|
|
if (!state.tests) {
|
|
return includedTests.indexOf(path) !== -1 ? 1 : 0;
|
|
}
|
|
var count = 0;
|
|
for (var test of includedTests) {
|
|
if (!test.match(new RegExp("^" + path))) continue;
|
|
count++;
|
|
}
|
|
return count / state.tests[path.substr(1)].length;
|
|
}
|
|
|
|
function selectAllTests() {
|
|
var tests = state.configuration.tests;
|
|
if (state.tests) {
|
|
tests.include = [];
|
|
for (var api in state.tests) {
|
|
tests.include = tests.include.concat(state.tests[api]);
|
|
}
|
|
} else {
|
|
tests.include = apis.map((api) => api.path);
|
|
}
|
|
}
|
|
|
|
function deselectAllTests() {
|
|
var configuration = state.configuration;
|
|
configuration.tests.include = [];
|
|
}
|
|
|
|
function hasTestType(value) {
|
|
var configuration = state.configuration;
|
|
var index = configuration.types.findIndex((type) => type === value);
|
|
return index !== -1;
|
|
}
|
|
|
|
function handleTestTypeToggle(value) {
|
|
var configuration = state.configuration;
|
|
if (hasTestType(value)) {
|
|
var index = configuration.types.findIndex((type) => type === value);
|
|
configuration.types.splice(index, 1);
|
|
} else {
|
|
configuration.types.push(value);
|
|
}
|
|
}
|
|
|
|
function selectAllTestTypes() {
|
|
var configuration = state.configuration;
|
|
configuration.types = types.map((type) => type.value);
|
|
}
|
|
|
|
function deselectAllTestTypes() {
|
|
var configuration = state.configuration;
|
|
configuration.types = [];
|
|
}
|
|
|
|
function hasRefSession(session) {
|
|
var configuration = state.configuration;
|
|
var index = configuration.referenceTokens.findIndex(
|
|
(token) => token === session.token
|
|
);
|
|
return index !== -1;
|
|
}
|
|
|
|
function handleRefSessionToggle(session) {
|
|
var configuration = state.configuration;
|
|
if (hasRefSession(session)) {
|
|
var index = configuration.referenceTokens.findIndex(
|
|
(token) => token === session.token
|
|
);
|
|
configuration.referenceTokens.splice(index, 1);
|
|
} else {
|
|
configuration.referenceTokens.push(session.token);
|
|
}
|
|
}
|
|
|
|
function selectAllRefSessions() {
|
|
var configuration = state.configuration;
|
|
configuration.referenceTokens = referenceSessions.map(
|
|
(session) => session.token
|
|
);
|
|
}
|
|
|
|
function deselectAllRefSessions() {
|
|
var configuration = state.configuration;
|
|
configuration.referenceTokens = [];
|
|
}
|
|
|
|
function isTestListValid() {
|
|
var configuration = state.configuration;
|
|
return configuration.tests.include.length > 0;
|
|
}
|
|
|
|
function isTestTypeListValid() {
|
|
var configuration = state.configuration;
|
|
return configuration.types.length > 0;
|
|
}
|
|
|
|
function isConfigurationValid() {
|
|
if (!isTestListValid()) return false;
|
|
if (!isTestTypeListValid()) return false;
|
|
return true;
|
|
}
|
|
|
|
function isSessionStarting() {
|
|
return state.isStarting;
|
|
}
|
|
|
|
function checkApiList() {
|
|
var apiErrorElement = UI.getElement("api-error");
|
|
apiErrorElement.innerHTML = "";
|
|
if (!isTestListValid()) {
|
|
apiErrorElement.appendChild(
|
|
UI.createElement(
|
|
createErrorMessage(
|
|
"Select at least one API or at least one test within an API"
|
|
)
|
|
)
|
|
);
|
|
}
|
|
renderButtonsField();
|
|
}
|
|
|
|
function handleStart() {
|
|
if (isSessionStarting()) return;
|
|
var configuration = state.configuration;
|
|
var token = configuration.token;
|
|
WaveService.updateSession(token, configuration, function () {
|
|
WaveService.updateLabels(token, configuration.labels, function () {
|
|
WaveService.startSession(token, function () {
|
|
openResultsPage(token);
|
|
});
|
|
});
|
|
});
|
|
state.isStarting = true;
|
|
}
|
|
|
|
function handleDiscardChanges() {
|
|
state.configuration = utils.copyObject(state.configurationBackup);
|
|
}
|
|
|
|
function handleExpiration() {
|
|
state.expired = true;
|
|
renderSessionConfiguration();
|
|
renderResumeView();
|
|
}
|
|
|
|
function openResultsPage(token) {
|
|
location.href = WEB_ROOT + "results.html?token=" + token;
|
|
}
|
|
|
|
function handleAddExludedTestsRaw() {
|
|
var excludedTestsTextArea = UI.getElement("excluded-tests-text");
|
|
var tests = excludedTestsTextArea.value.split("\n");
|
|
var configuration = state.configuration;
|
|
var excludedTests = configuration.tests.exclude;
|
|
for (var test of tests) {
|
|
if (!test) continue;
|
|
if (test.startsWith("#")) continue;
|
|
if (excludedTests.indexOf(test) !== -1) continue;
|
|
excludedTests.push(test);
|
|
}
|
|
|
|
excludedTestsTextArea.value = "";
|
|
renderExcludedTests();
|
|
}
|
|
|
|
function handleAddExludedTestsMalfunctioning() {
|
|
var excludedTestsTextArea = UI.getElement("excluded-tests-text");
|
|
var token = excludedTestsTextArea.value;
|
|
var tokenRegExp = new RegExp(
|
|
"^[a-f0-9]{8}(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,12}|$)"
|
|
);
|
|
var configuration = state.configuration;
|
|
var excludedTests = configuration.tests.exclude;
|
|
if (tokenRegExp.test(token)) {
|
|
WaveService.findToken(
|
|
token,
|
|
function (token) {
|
|
WaveService.readMalfunctioningTests(token, function (
|
|
malfunctioningTests
|
|
) {
|
|
for (var test of malfunctioningTests) {
|
|
if (!test) continue;
|
|
if (excludedTests.indexOf(test) !== -1) continue;
|
|
excludedTests.push(test);
|
|
}
|
|
renderExcludedTests();
|
|
});
|
|
},
|
|
function () {
|
|
state.excludedTestError = "Session not found";
|
|
renderExcludedTests();
|
|
}
|
|
);
|
|
} else {
|
|
state.excludedTestError = "Invalid session token";
|
|
renderExcludedTests();
|
|
}
|
|
}
|
|
|
|
function handleAddExludedTestsExcluded() {
|
|
var excludedTestsTextArea = UI.getElement("excluded-tests-text");
|
|
var token = excludedTestsTextArea.value;
|
|
var tokenRegExp = new RegExp(
|
|
"^[a-f0-9]{8}(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,4}|$)(-[a-f0-9]{0,12}|$)"
|
|
);
|
|
var configuration = state.configuration;
|
|
var excludedTests = configuration.tests.exclude;
|
|
if (tokenRegExp.test(token)) {
|
|
WaveService.findToken(
|
|
token,
|
|
function (token) {
|
|
WaveService.readSession(token, function (sessionConfig) {
|
|
var prevExcludedTests = sessionConfig.tests.exclude;
|
|
for (var test of prevExcludedTests) {
|
|
if (!test) continue;
|
|
if (excludedTests.indexOf(test) !== -1) continue;
|
|
excludedTests.push(test);
|
|
}
|
|
renderExcludedTests();
|
|
});
|
|
},
|
|
function () {
|
|
state.excludedTestError = "Session not found";
|
|
renderExcludedTests();
|
|
}
|
|
);
|
|
} else {
|
|
state.excludedTestError = "Invalid session token";
|
|
renderExcludedTests();
|
|
}
|
|
}
|
|
|
|
function handleRemoveExcludedTest(test) {
|
|
var configuration = state.configuration;
|
|
var excludedTests = configuration.tests.exclude;
|
|
var index = excludedTests.indexOf(test);
|
|
excludedTests.splice(index, 1);
|
|
renderExcludedTests();
|
|
}
|
|
|
|
function handleExcludeInputChange(type) {
|
|
if (state.activeExcludeInput === type) {
|
|
state.activeExcludeInput = null;
|
|
} else {
|
|
state.activeExcludeInput = type;
|
|
}
|
|
renderExcludedTests();
|
|
}
|
|
|
|
function showAddLabel() {
|
|
state.addLabelVisible = true;
|
|
renderLabelsField();
|
|
UI.getElement("session-label-input").focus();
|
|
}
|
|
|
|
function hideAddLabel() {
|
|
state.addLabelVisible = false;
|
|
renderLabelsField();
|
|
}
|
|
|
|
function addLabel() {
|
|
var label = UI.getElement("session-label-input").value;
|
|
if (!label) return;
|
|
state.configuration.labels.push(label);
|
|
renderLabelsField();
|
|
UI.getElement("session-label-input").focus();
|
|
}
|
|
|
|
function removeLabel(index) {
|
|
const { configuration } = state;
|
|
configuration.labels.splice(index, 1);
|
|
renderLabelsField();
|
|
}
|
|
|
|
function resumeSession() {
|
|
var resumeToken = UI.getElement("resume-token").value;
|
|
if (!resumeToken) return;
|
|
|
|
WaveService.resumeSession(
|
|
state.configuration.token,
|
|
resumeToken,
|
|
function () {
|
|
openResultsPage(resumeToken);
|
|
}
|
|
);
|
|
}
|
|
|
|
function render() {
|
|
const configurationView = UI.createElement({
|
|
element: "section",
|
|
className: "section",
|
|
children: [
|
|
{
|
|
className: "container",
|
|
style: "margin-bottom: 2em",
|
|
children: [
|
|
{
|
|
element: "img",
|
|
src: "res/wavelogo_2016.jpg",
|
|
className: "site-logo",
|
|
},
|
|
{ className: "title", text: "Session Configuration" },
|
|
],
|
|
},
|
|
{
|
|
id: "session-configuration",
|
|
},
|
|
{
|
|
id: "resume-view",
|
|
className: "container",
|
|
style: "margin-bottom: 2em",
|
|
},
|
|
],
|
|
});
|
|
const root = UI.getRoot();
|
|
root.innerHTML = "";
|
|
root.appendChild(configurationView);
|
|
renderSessionConfiguration();
|
|
renderResumeView();
|
|
}
|
|
|
|
function renderSessionConfiguration() {
|
|
var configuration = state.configuration;
|
|
var status = state.status;
|
|
var sessionConfigurationView = UI.createElement({});
|
|
var sessionConfiguration = UI.getElement("session-configuration");
|
|
sessionConfiguration.innerHTML = "";
|
|
|
|
if (state.expired) {
|
|
var expiredIndicator = UI.createElement({
|
|
className: "level container",
|
|
style: "max-width: 500px",
|
|
children: {
|
|
element: "span",
|
|
className: "level-item field column",
|
|
children: [
|
|
{
|
|
element: "article",
|
|
className: "message is-danger",
|
|
id: "find-error",
|
|
children: [
|
|
{
|
|
text:
|
|
"Could not find any sessions! Try adding more characters of the token.",
|
|
className: "message-body",
|
|
},
|
|
],
|
|
style: "display: none",
|
|
},
|
|
{
|
|
className: "label has-text-weight-normal",
|
|
text: "Session token:",
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
children: {
|
|
style: "display: flex; margin-bottom: 10px;",
|
|
children: [
|
|
{
|
|
element: "input",
|
|
inputType: "text",
|
|
className: "input is-family-monospace",
|
|
id: "token-fragment",
|
|
placeholder:
|
|
"First 8 characters or more of session token",
|
|
onKeyDown: function (event) {
|
|
if (event.key === "Enter") {
|
|
handleConfigureSession();
|
|
}
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
className: "field is-grouped is-grouped-right",
|
|
children: {
|
|
className: "control",
|
|
children: {
|
|
className: "button is-dark is-outlined",
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "icon",
|
|
children: [
|
|
{
|
|
element: "i",
|
|
className: "fas fa-cog",
|
|
},
|
|
],
|
|
},
|
|
{ text: "Configure Session", element: "span" },
|
|
],
|
|
onclick: function () {
|
|
handleConfigureSession();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
sessionConfigurationView.appendChild(expiredIndicator);
|
|
sessionConfiguration.appendChild(sessionConfigurationView);
|
|
return;
|
|
}
|
|
|
|
if (!configuration) {
|
|
var loadingIndicator = createLoadingIndicator(
|
|
"Loading configuration ..."
|
|
);
|
|
sessionConfigurationView.appendChild(loadingIndicator);
|
|
sessionConfiguration.appendChild(sessionConfigurationView);
|
|
return;
|
|
}
|
|
|
|
sessionConfiguration.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "session-configuration",
|
|
className: "container",
|
|
style: "margin-bottom: 2em",
|
|
children: [
|
|
{
|
|
id: "token-field",
|
|
},
|
|
{
|
|
id: "expiration-field",
|
|
},
|
|
{
|
|
id: "labels-field",
|
|
},
|
|
{
|
|
id: "apis-field",
|
|
},
|
|
{
|
|
id: "exclude-field",
|
|
},
|
|
{
|
|
id: "types-field",
|
|
},
|
|
{
|
|
id: "references-field",
|
|
},
|
|
{
|
|
id: "buttons-field",
|
|
},
|
|
],
|
|
}),
|
|
sessionConfiguration
|
|
);
|
|
|
|
renderTokenField();
|
|
renderExpirationField();
|
|
renderLabelsField();
|
|
renderApisField();
|
|
renderExcludeField();
|
|
renderTypesField();
|
|
renderReferencesField();
|
|
renderButtonsField();
|
|
}
|
|
|
|
function renderTokenField() {
|
|
var configuration = state.configuration;
|
|
var tokenField = UI.getElement("token-field");
|
|
tokenField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "token-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Token" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
text: configuration.token,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
tokenField
|
|
);
|
|
}
|
|
|
|
function renderExpirationField() {
|
|
var status = state.status;
|
|
var expirationField = UI.getElement("expiration-field");
|
|
expirationField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "expiration-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Expires" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
text: status.expirationDate.toLocaleString(),
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
expirationField
|
|
);
|
|
}
|
|
|
|
function renderLabelsField() {
|
|
var addLabelVisible = state.addLabelVisible;
|
|
var configuration = state.configuration;
|
|
var labelsField = UI.getElement("labels-field");
|
|
|
|
labelsField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "labels-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Labels" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field is-grouped is-grouped-multiline",
|
|
children: configuration.labels
|
|
.map((label, index) => ({
|
|
className: "control",
|
|
children: {
|
|
className: "tags has-addons",
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "tag is-info",
|
|
text: label,
|
|
},
|
|
{
|
|
element: "a",
|
|
className: "tag is-delete",
|
|
onClick: () => removeLabel(index),
|
|
},
|
|
],
|
|
},
|
|
}))
|
|
.concat(
|
|
addLabelVisible
|
|
? [
|
|
{
|
|
className: "control field is-grouped",
|
|
children: [
|
|
{
|
|
element: "input",
|
|
className: "input is-small control",
|
|
style: "width: 10rem",
|
|
id: "session-label-input",
|
|
type: "text",
|
|
onKeyUp: (event) =>
|
|
event.keyCode === 13 ? addLabel() : null,
|
|
},
|
|
{
|
|
className:
|
|
"button is-dark is-outlined is-small is-rounded control",
|
|
text: "save",
|
|
onClick: addLabel,
|
|
},
|
|
{
|
|
className:
|
|
"button is-dark is-outlined is-small is-rounded control",
|
|
text: "cancel",
|
|
onClick: hideAddLabel,
|
|
},
|
|
],
|
|
},
|
|
]
|
|
: [
|
|
{
|
|
className: "button is-rounded is-small",
|
|
text: "Add",
|
|
onClick: showAddLabel,
|
|
},
|
|
]
|
|
),
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
labelsField
|
|
);
|
|
}
|
|
|
|
function renderApisField() {
|
|
var apisField = UI.getElement("apis-field");
|
|
apisField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "apis-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: [
|
|
{ className: "label", text: "APIs" },
|
|
createSelectDeselectButtons(
|
|
function () {
|
|
selectAllTests();
|
|
renderApisField();
|
|
},
|
|
function () {
|
|
deselectAllTests();
|
|
renderApisField();
|
|
}
|
|
),
|
|
],
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
children: [
|
|
{
|
|
id: "api-error",
|
|
},
|
|
{
|
|
element: "ul",
|
|
className: "menu-list",
|
|
children: apis.map(function (api) {
|
|
return UI.createElement({
|
|
element: "li",
|
|
id: api.title,
|
|
});
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
apisField
|
|
);
|
|
renderApisList(apis);
|
|
checkApiList();
|
|
}
|
|
|
|
function renderApisList(apis) {
|
|
for (var api of apis) {
|
|
renderApiList(api);
|
|
}
|
|
}
|
|
|
|
function renderApiList(api) {
|
|
var listItem = UI.getElement(api.title);
|
|
var includedRatio = getIncludedRatio(api.path);
|
|
var apiListItem = {
|
|
element: "a",
|
|
onClick: function (event) {
|
|
if (!testFileSelectionEnabled) return;
|
|
if (!state.expandedApis) state.expandedApis = {};
|
|
state.expandedApis[api.path] = !state.expandedApis[api.path];
|
|
renderApiList(api);
|
|
},
|
|
children: [
|
|
{
|
|
element: "input",
|
|
type: "checkbox",
|
|
style: "width: 1.3em; height: 1.3em;vertical-align: middle;",
|
|
checked: includedRatio > 0,
|
|
indeterminate: includedRatio > 0 && includedRatio < 1,
|
|
onclick: function (event) {
|
|
event.stopPropagation();
|
|
if (includedRatio > 0) {
|
|
handleRemoveIncludedTest(api.path);
|
|
} else {
|
|
handleAddIncludedTest(api.path);
|
|
}
|
|
renderApiList(api);
|
|
},
|
|
},
|
|
testFileSelectionEnabled
|
|
? {
|
|
element: "span",
|
|
style:
|
|
"display: inline-block;vertical-align: middle;margin-left:0.3em;width: 0.7em",
|
|
children: {
|
|
element: "i",
|
|
className:
|
|
state.expandedApis && state.expandedApis[api.path]
|
|
? "fas fa-angle-down"
|
|
: "fas fa-angle-right",
|
|
},
|
|
}
|
|
: null,
|
|
{
|
|
style:
|
|
"display: inline-block;vertical-align: middle;margin-left:0.3em;width: 90%",
|
|
text: api.title,
|
|
},
|
|
],
|
|
};
|
|
listItem.innerHTML = "";
|
|
listItem.appendChild(UI.createElement(apiListItem));
|
|
if (state.expandedApis && state.expandedApis[api.path]) {
|
|
listItem.appendChild(createApiTestsList(api));
|
|
}
|
|
checkApiList();
|
|
}
|
|
|
|
function createApiTestsList(api) {
|
|
if (!state.tests) {
|
|
WaveService.readTestList(
|
|
function (readTests) {
|
|
state.tests = readTests;
|
|
for (var api in state.tests) {
|
|
if (hasIncludedTest("/" + api)) {
|
|
handleRemoveIncludedTest("/" + api);
|
|
handleAddIncludedTest("/" + api);
|
|
}
|
|
}
|
|
renderApiList(this.api);
|
|
}.bind({ api: api })
|
|
);
|
|
return createLoadingIndicator("Loading tests ...");
|
|
} else {
|
|
var tests = state.tests[api.path.substr(1)];
|
|
var testListView = {
|
|
element: "ul",
|
|
children: [],
|
|
};
|
|
|
|
testListView.children = testListView.children.concat(
|
|
tests
|
|
.sort()
|
|
.map(function (test) {
|
|
return {
|
|
element: "li",
|
|
onclick: function (event) {
|
|
handleIncludedTestToggle(test);
|
|
renderApiList(api);
|
|
},
|
|
children: [
|
|
{
|
|
element: "a",
|
|
children: [
|
|
{
|
|
element: "input",
|
|
type: "checkbox",
|
|
style:
|
|
"width: 1.3em; height: 1.3em;vertical-align: middle;",
|
|
checked: hasIncludedTest(test),
|
|
},
|
|
{
|
|
style:
|
|
"display: inline-block;vertical-align: middle;margin-left:0.3em;max-width: 90%",
|
|
text: test,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
})
|
|
);
|
|
return UI.createElement(testListView);
|
|
}
|
|
}
|
|
|
|
function renderExcludeField() {
|
|
var excludeField = UI.getElement("exclude-field");
|
|
excludeField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "exclude-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Excluded Tests" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
children: { id: "excluded-tests-view" },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
excludeField
|
|
);
|
|
renderExcludedTests();
|
|
}
|
|
|
|
function renderTypesField() {
|
|
if (!testTypeSelectionEnabled) return;
|
|
var typesField = UI.getElement("types-field");
|
|
typesField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "types-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Test Types" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
children: [
|
|
isTestTypeListValid()
|
|
? null
|
|
: createErrorMessage("Select at least one test type"),
|
|
].concat(createTestTypeList(types)),
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
typesField
|
|
);
|
|
}
|
|
|
|
function renderReferencesField() {
|
|
if (referenceSessions.length === 0) {
|
|
return;
|
|
}
|
|
var referencesField = UI.getElement("references-field");
|
|
referencesField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "references-field",
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: [
|
|
{ className: "label", text: "Reference Browsers" },
|
|
createSelectDeselectButtons(
|
|
function () {
|
|
selectAllRefSessions();
|
|
renderReferencesField();
|
|
},
|
|
function () {
|
|
deselectAllRefSessions();
|
|
renderReferencesField();
|
|
}
|
|
),
|
|
],
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field",
|
|
children: {
|
|
className: "control",
|
|
children: createRefSessionsList(referenceSessions),
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
referencesField
|
|
);
|
|
}
|
|
|
|
function renderButtonsField() {
|
|
var buttonsField = UI.getElement("buttons-field");
|
|
buttonsField.parentNode.replaceChild(
|
|
UI.createElement({
|
|
id: "buttons-field",
|
|
className: "level level-right",
|
|
children: [
|
|
{
|
|
element: "button",
|
|
className: "button is-success",
|
|
style: "margin-right: 0.3em",
|
|
disabled: !isConfigurationValid(),
|
|
onClick: function () {
|
|
handleStart();
|
|
renderButtonsField();
|
|
},
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "icon",
|
|
children: [
|
|
{
|
|
element: "i",
|
|
className: isSessionStarting()
|
|
? "fas fa-spinner fa-pulse"
|
|
: "fas fa-play",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
element: "span",
|
|
text: isSessionStarting()
|
|
? "Starting Session ..."
|
|
: "Start Session",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
element: "button",
|
|
className: "button",
|
|
onClick: function () {
|
|
handleDiscardChanges();
|
|
renderSessionConfiguration();
|
|
},
|
|
disabled: isSessionStarting(),
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "icon",
|
|
children: [
|
|
{
|
|
element: "i",
|
|
className: "fas fa-times",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
element: "span",
|
|
text: "Discard Changes",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}),
|
|
buttonsField
|
|
);
|
|
}
|
|
|
|
function renderExcludedTests() {
|
|
var excludedTestsView = UI.getElement("excluded-tests-view");
|
|
if (!excludedTestsView) return;
|
|
excludedTestsView.innerHTML = "";
|
|
|
|
var errorMessage = state.excludedTestError;
|
|
if (errorMessage) {
|
|
var error = createErrorMessage(errorMessage);
|
|
excludedTestsView.appendChild(UI.createElement(error));
|
|
}
|
|
|
|
var excludeInputs = [
|
|
{ title: "Add Raw", type: "raw" },
|
|
{ title: "Add Malfunctioning", type: "malfunc" },
|
|
{ title: "Add Previous Excluded", type: "excluded" },
|
|
];
|
|
|
|
var activeExcludeInput = state.activeExcludeInput;
|
|
|
|
var excludedTestInputSwitch = UI.createElement({
|
|
className: "tabs is-centered is-toggle is-small",
|
|
children: {
|
|
element: "ul",
|
|
children: excludeInputs.map(function (input) {
|
|
return {
|
|
element: "li",
|
|
onClick: function () {
|
|
handleExcludeInputChange(input.type);
|
|
},
|
|
className: (function () {
|
|
if (activeExcludeInput === input.type) return "is-active";
|
|
return "";
|
|
})(),
|
|
children: { element: "a", text: input.title },
|
|
};
|
|
}),
|
|
},
|
|
});
|
|
excludedTestsView.appendChild(excludedTestInputSwitch);
|
|
|
|
if (activeExcludeInput === "raw") {
|
|
var rawInput = UI.createElement({
|
|
children: [
|
|
{
|
|
className: "is-size-7",
|
|
style: "margin-bottom: 20px",
|
|
text:
|
|
"Provide paths to test files or directories to exclude them from the session. One path per line, lines starting with # are omitted.",
|
|
},
|
|
{
|
|
element: "textarea",
|
|
className: "textarea",
|
|
id: "excluded-tests-text",
|
|
},
|
|
{
|
|
style: "margin-top: 10px",
|
|
onClick: function () {
|
|
handleAddExludedTestsRaw();
|
|
},
|
|
children: [
|
|
{
|
|
element: "button",
|
|
className: "button",
|
|
style: "margin-bottom: 20px",
|
|
text: "Add",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
excludedTestsView.appendChild(rawInput);
|
|
} else if (
|
|
activeExcludeInput === "malfunc" ||
|
|
activeExcludeInput === "excluded"
|
|
) {
|
|
var malfuncInput = UI.createElement({
|
|
style: "margin-bottom: 1em",
|
|
children: [
|
|
{
|
|
className: "is-size-7",
|
|
style: "margin-bottom: 1em",
|
|
text:
|
|
activeExcludeInput === "malfunc"
|
|
? "Add malfunctioning tests from past sessions by providing at least the first eight characters of the session's token."
|
|
: "Add excluded tests from past sessions by providing at least the first eight characters of the session's token.",
|
|
},
|
|
{
|
|
className: "field is-horizontal",
|
|
children: [
|
|
{
|
|
className: "field-label",
|
|
children: { className: "label", text: "Session Token" },
|
|
},
|
|
{
|
|
className: "field-body",
|
|
children: {
|
|
className: "field is-grouped is-multiline",
|
|
children: [
|
|
{
|
|
id: "excluded-tests-text",
|
|
className: "input",
|
|
element: "input",
|
|
type: "text",
|
|
},
|
|
{
|
|
className: "button",
|
|
style: "margin-left: 1em",
|
|
text: "Add",
|
|
onClick: function () {
|
|
if (activeExcludeInput === "malfunc") {
|
|
handleAddExludedTestsMalfunctioning();
|
|
} else {
|
|
handleAddExludedTestsExcluded();
|
|
}
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
excludedTestsView.appendChild(malfuncInput);
|
|
}
|
|
|
|
var excludedTestsTable = createExcludedTestsTable();
|
|
var tableWrapper = UI.createElement({
|
|
style: "max-height: 250px; overflow: auto; margin-bottom: 10px",
|
|
});
|
|
tableWrapper.appendChild(excludedTestsTable);
|
|
excludedTestsView.appendChild(tableWrapper);
|
|
}
|
|
|
|
function renderResumeView() {
|
|
var query = utils.parseQuery(location.search);
|
|
var resumeToken = query.resume;
|
|
if (!resumeToken) resumeToken = "";
|
|
|
|
var renderResumeElement = UI.getElement("resume-view");
|
|
renderResumeElement.innerHTML = "";
|
|
if (state.expired) return;
|
|
|
|
var heading = UI.createElement({
|
|
element: "h2",
|
|
className: "title is-5",
|
|
text: "Resume session",
|
|
});
|
|
renderResumeElement.appendChild(heading);
|
|
|
|
var resumeControls = UI.createElement({
|
|
className: "columns",
|
|
children: [
|
|
{
|
|
className: "column",
|
|
children: {
|
|
className: "field",
|
|
children: [
|
|
{
|
|
element: "label",
|
|
className: "label",
|
|
text: "Token (first 8 characters or more)",
|
|
},
|
|
{
|
|
className: "control",
|
|
children: {
|
|
element: "input",
|
|
id: "resume-token",
|
|
className: "input is-family-monospace tabbable",
|
|
type: "text",
|
|
style: "max-width: 30em",
|
|
value: resumeToken,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
className: "column",
|
|
style:
|
|
"display: flex; align-items: flex-end; justify-content: flex-end",
|
|
children: {
|
|
className: "button",
|
|
onClick: function () {
|
|
resumeSession();
|
|
},
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "icon",
|
|
children: { element: "i", className: "fas fa-redo-alt" },
|
|
},
|
|
{
|
|
element: "span",
|
|
text: "Resume",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
renderResumeElement.appendChild(resumeControls);
|
|
}
|
|
|
|
function createExcludedTestsTable() {
|
|
var excludedTests = state.configuration.tests.exclude;
|
|
if (excludedTests.length === 0) {
|
|
return UI.createElement({
|
|
style: "text-align: center",
|
|
text: "- No Excluded Tests -",
|
|
});
|
|
}
|
|
var table = UI.createElement({
|
|
element: "table",
|
|
className: "table",
|
|
style: "width: 100%",
|
|
children: excludedTests.map(function (test) {
|
|
return {
|
|
element: "tr",
|
|
children: [
|
|
{ element: "td", style: "width: 100%;", text: test },
|
|
{
|
|
element: "td",
|
|
children: {
|
|
element: "button",
|
|
className: "button is-small",
|
|
onClick: function () {
|
|
handleRemoveExcludedTest(test);
|
|
},
|
|
children: {
|
|
element: "span",
|
|
className: "icon",
|
|
children: {
|
|
element: "i",
|
|
className: "fas fa-trash-alt",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
};
|
|
}),
|
|
});
|
|
return table;
|
|
}
|
|
|
|
function createTestTypeList(types) {
|
|
return types.map((type) => ({
|
|
element: "button",
|
|
style: "margin-right: 0.3em; margin-bottom: 0.3em",
|
|
className: "button" + (hasTestType(type.value) ? " is-info" : ""),
|
|
text: type.title,
|
|
onClick: function (event) {
|
|
handleTestTypeToggle(type.value);
|
|
renderTypesField();
|
|
},
|
|
}));
|
|
}
|
|
|
|
function createRefSessionsList(referenceSessions) {
|
|
return referenceSessions.map((session) => ({
|
|
element: "button",
|
|
className: "button" + (hasRefSession(session) ? " is-info" : ""),
|
|
style: "margin-right: 0.3em; margin-bottom: 0.3em; height: auto",
|
|
onClick: function () {
|
|
handleRefSessionToggle(session);
|
|
renderReferencesField();
|
|
},
|
|
children: [
|
|
{
|
|
element: "span",
|
|
className: "icon",
|
|
children: [{ element: "i", className: session.icon }],
|
|
},
|
|
{
|
|
element: "span",
|
|
children: [
|
|
{ text: session.title },
|
|
{
|
|
text: session.engine,
|
|
style: "font-size: 0.8em",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}));
|
|
}
|
|
|
|
function createSelectDeselectButtons(onSelect, onDeselect) {
|
|
return {
|
|
style: "margin-top: 0.3em",
|
|
children: [
|
|
{
|
|
element: "button",
|
|
style: "margin-right: 0.3em",
|
|
className: "button is-rounded is-small",
|
|
text: "All",
|
|
onClick: onSelect,
|
|
},
|
|
{
|
|
element: "button",
|
|
className: "button is-rounded is-small",
|
|
text: "None",
|
|
onClick: onDeselect,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function createErrorMessage(message) {
|
|
return {
|
|
element: "article",
|
|
className: "message is-danger",
|
|
children: [
|
|
{
|
|
className: "message-body",
|
|
text: message,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function createLoadingIndicator(text) {
|
|
return UI.createElement({
|
|
className: "level",
|
|
children: {
|
|
element: "span",
|
|
className: "level-item",
|
|
children: [
|
|
{
|
|
element: "i",
|
|
className: "fas fa-spinner fa-pulse",
|
|
},
|
|
{
|
|
style: "margin-left: 0.4em;",
|
|
text: text,
|
|
},
|
|
],
|
|
},
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|