| <!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> |