//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- (function (window) { /////////////////////////////////////////////////////////////////////////////// // Utils object for helper functions /////////////////////////////////////////////////////////////////////////////// var Utils = {}; //Doc Mode Utils.IE7STANDARDSMODE = 7; Utils.IE8STANDARDSMODE = 8; Utils.IE9STANDARDSMODE = 9; Utils.IE10STANDARDSMODE = 10; Utils.IE11STANDARDSMODE = 11; Utils.IE12STANDARDSMODE = 12; //IE Architectures Utils.X86MODE = "x86"; Utils.X64MODE = "x64"; Utils.ARMMODE = "arm"; Utils.getHOSTMode = function () { var documentMode = this.IE11STANDARDSMODE; // Making default to be IE11 standards mode //See if document.documentMode is defined try { if ((document) && (document.documentMode)) { documentMode = document.documentMode; } } catch (ex) { //We are not running in IE, need to put logic of versioning for other hosts later } if (documentMode < 8) return this.IE7STANDARDSMODE; else if (documentMode == 8) return this.IE8STANDARDSMODE; else if (documentMode == 9) return this.IE9STANDARDSMODE; else if (documentMode == 10) return this.IE10STANDARDSMODE; else if (documentMode == 11 && typeof Math.hypot === "undefined") //hack for now as we run version:6 switch to emulate Doc mode 12 return this.IE11STANDARDSMODE; else return this.IE12STANDARDSMODE; } var activeXInfo = { "WScript.Shell": { "clsid": "72C24DD5-D70A-438b-8A42-98424B88AFB8", "type": "application/x-shellscript" }, "Loader42.Logger": { "clsid": "7CDFA963-4058-45AE-9061-D34614D83472", "type": "application/vns.ms-loader42loggerax" }, "Scripting.FileSystemObject": { "clsid": "0D43FE01-F093-11cf-8940-00A0C9054228", "type": "application/x-filesystemobject" }, "MutatorsHost.MutatorsActiveX": { "clsid": "ffafec7c-214c-4aab-b929-caa646777649", "type": "application/x-mutatorsactivex" }, "apgloballib.apglobal": { "clsid": "CBFCDDC2-CDB1-3270-B9C5-FBE31729B041", "type": "application/x-apgloballib" }, "Logger.Status": { "clsid": "712B2344-7A71-4242-B21C-AA728108EDD8", "type": "application/x-loggerstatus" }, "WTTLogger.Logger": { "clsid": "FFC4E997-D97B-45DF-AD22-44C4B2DEDD2B", "type": "application/x-wttlogger" } }; function createActiveXObject(progId) { var activeX; try { activeX = new ActiveXObject(progId); } catch (ex) { // Can't create ActiveXObject element in Edge mode, append Object element instead activeX = document.createElement("object"); activeX.setAttribute('id', progId); activeX.setAttribute('type', activeXInfo[progId]["type"]); document.body.appendChild(activeX); } return activeX; } Utils.IEMatchesArch = function () { if (this.getProcessorArchitecture() != this.X64MODE) { return true; } if (this.runWow64()) { return this.getIEArchitecture() == this.X86MODE; } else { return this.getIEArchitecture() == this.X64MODE; } } Utils.getProcessorArchitecture = function () { try { var GetEnvVariable = function (variable) { var wscriptShell = createActiveXObject("WScript.Shell"); var wshEnvironment = wscriptShell.Environment("System"); return wshEnvironment(variable); } var result = GetEnvVariable("PROCESSOR_ARCHITECTURE"); if (result != null) { result = result.toUpperCase(); } if (result === "AMD64") { return this.X64MODE; } else if (result === "X86") { return this.X86MODE; } else if (result === "ARM") { return this.ARMMODE; } } catch (e) { throw new Error("[getProcessorArchitecture] Failed to retrieve environment variable. " + e.Message); } } Utils.getIEArchitecture = function () { if (typeof navigator === 'undefined') { return this.X64MODE; } //get the user agent and split the strings and identify t he IE mode that we are running var mode = navigator.userAgent; var arr_mode = mode.toLowerCase().split(";"); for (var i = 0; i < arr_mode.length; i++) { if (arr_mode[i] === " wow64") { return this.X86MODE; } else if (arr_mode[i] === " x64") { return this.X64MODE; } else if (arr_mode[i] === " arm") { for (var j = i + 1; j < arr_mode.length; j++) { if (arr_mode[j] === " virtual)") return X86MODE; } return this.ARMMODE; } } //default to x86 return this.X86MODE; } Utils.runWow64 = function () { try { var GetEnvVariable = function (variable) { var wscriptShell = createActiveXObject("WScript.Shell"); var wshEnvironment = wscriptShell.Environment("Process"); return wshEnvironment(variable); } var result = GetEnvVariable("NightlyWow64"); if (result === "1") { return true; } else { return false; } } catch (e) { throw new Error("[runWow64] Failed to retrieve environment variable. " + e.Message); } } //Get the host of script engine Utils.WWAHOST = "WWA"; Utils.IEHOST = "Internet Explorer"; Utils.getHOSTType = function () { //navigator.appName will return the host name, e.g. "WWAHost/1.0" or "Microsoft Internet Explorer" if (typeof navigator != 'undefined' && navigator.appName.indexOf(this.WWAHOST) >= 0) { return this.WWAHOST; } return this.IEHOST; } //Get localized error message Utils.getLocalizedError = function (ID, _default, substitution) { if (_default) { return _default; } if (substitution == undefined) { var localizedString = apGlobalObj.apGetLocalizedString(ID); } else { var localizedString = apGlobalObj.apGetLocalizedStringWithSubstitution(ID, substitution); } return localizedString; } // read "MaxInterpretCount" environment variable to know how many time to run test suite Utils.getMaxInterpretCount = function () { try { var GetEnvVariable = function (variable) { var wscriptShell = createActiveXObject("WScript.Shell"); var wshEnvironment = wscriptShell.Environment("Process"); return wshEnvironment(variable); } var result = GetEnvVariable("MaxInterpretCount"); if (result === "") return 0; else return Math.floor(result); } catch (e) { return 0; } } /////////////////////////////////////////////////////////////////////////////// // WTTLogger: // Logs directly to WTT. /////////////////////////////////////////////////////////////////////////////// var loggers = []; function WTTLogger() { var WTT_TRACE_LVL_MSG = 0x10000000; var WTT_TRACE_LVL_USER = 0x20000000; var WTT_TRACE_LVL_ERR = 0x01000000; var WTT_TRACE_LVL_ASSERT = 0x02000000; var WTT_TRACE_LVL_INVALID_PARAM = 0x04000000; var WTT_TRACE_LVL_BUG = 0x08000000; var WTT_TRACE_LVL_BREAK = 0x00100000; var WTT_TRACE_LVL_WARN = 0x00200000; var WTT_TRACE_LVL_FUNCTION_ENTRY = 0x00010000; var WTT_TRACE_LVL_FUNCTION_EXIT = 0x00020000; var WTT_TRACE_LVL_CONTEXT_ENTRY = 0x00040000; var WTT_TRACE_LVL_CONTEXT_EXIT = 0x00080000; var WTT_TRACE_LVL_START_TEST = 0x0100; var WTT_TRACE_LVL_END_TEST = 0x0200; var WTT_TRACE_LVL_TCMINFO = 0x0400; var WTT_TRACE_LVL_MACHINEINFO = 0x0800; var WTT_TRACE_LVL_ROLLUP = 0x0010; var WTT_ERROR_TYPE_HRESULT = 0x1; var WTT_ERROR_TYPE_NTSTATUS = 0x2; var WTT_ERROR_TYPE_WIN32 = 0x4; var WTT_ERROR_TYPE_BOOL = 0x8; var WTT_ERROR_LIST_EXPECTED = 0x1; var WTT_ERROR_LIST_BREAKON = 0x2; var WTT_TC_RESULT_PASS = 0x1; var WTT_TC_RESULT_FAIL = 0x2; var WTT_TC_RESULT_BLOCKED = 0x3; var WTT_TC_RESULT_WARN = 0x4; var WTT_TC_RESULT_SKIPPED = 0x5; var WTT_BUG_TYPE_BLOCKING = 0x1; var WTT_BUG_TYPE_NONBLOCKING = 0x2; var loggerObj = createActiveXObject("WTTLogger.Logger"); var logHandle; var wwahostTimeout = 1000; var maxMsgSize = 3000; //actually 3901, but do we really even need 3000 in wtt this.start = function (filename, priority) { logHandle = loggerObj.CreateLogDevice(null); loggerObj.Trace(WTT_TRACE_LVL_MSG, logHandle, "Running " + filename); } this.testStart = function (test) { loggerObj.StartTest(test.desc, logHandle); } this.verify = function (test, passed, act, exp, msg) { if (!passed) loggerObj.Trace(WTT_TRACE_LVL_MSG, logHandle, "Failed - Act: " + act + ", Exp: " + exp + ":" + msg); } this.pass = function (test) { loggerObj.EndTest(test.desc, WTT_TC_RESULT_PASS, "", logHandle); } this.fail = function (test) { loggerObj.EndTest(test.desc, WTT_TC_RESULT_FAIL, "", logHandle); } this.error = function (test, error) { loggerObj.Trace(WTT_TRACE_LVL_MSG, logHandle, "Error: " + error.name + " - " + error.description); loggerObj.EndTest(test.desc, WTT_TC_RESULT_FAIL, "", logHandle); } this.end = function () { loggerObj.CloseLogDevice("", logHandle); //If we're running WTTLogger in a IE environment, we are probably running tests in wwahost, //so we should close the window after we finish in order to write logs and rollup. The timeout //is to ensure the rest of glue shutdown happens //if(typeof document !== "undefined" && Utils.getHOSTType() == Utils.WWAHOST) // setTimeout(function() { window.close(); }, wwahostTimeout); } this.comment = function (str) { var msg = str.toString().substring(0, maxMsgSize); loggerObj.Trace(WTT_TRACE_LVL_MSG, logHandle, msg); } this.bug = function (number, msg, db) { db = db || "Windows 8 Bugs"; loggerObj.Trace(WTT_TRACE_LVL_BUG, logHandle, db, number, WTT_BUG_TYPE_BLOCKING); this.comment(msg); } } WTTLogger.shouldEnable = function () { try { var test = createActiveXObject("WTTLogger.Logger"); return true; } catch (e) { return false; } } loggers.push(WTTLogger); /////////////////////////////////////////////////////////////////////////////// // HTMLLogger: // Provides HTML output. Must be run in a browser to function. /////////////////////////////////////////////////////////////////////////////// function HTMLLogger() { var resultsContainer; var testProgress = []; var results = []; var domReady = false; var summaryTable; var passes = 0; var failures = 0; var failureList = []; var executingTest = false; var done = false; var loggerInstance = this; var filename; var priority; var verifies = 0; if (document.addEventListener) { document.addEventListener("DOMContentLoaded", whenDomReady, false); } else { setTimeout(waitForDomReady, 1); } function waitForDomReady() { // Otherwise use the oft-used doScroll hack which, according to MSDN, will always thrown an // error unless the document is ready. try { document.documentElement.doScroll("left"); } catch (e) { // We're not ready yet. Check again in a bit. setTimeout(waitForDomReady, 1); return; } whenDomReady(); } function whenDomReady() { domReady = true; insertStyles(); setupMutators(); createSummaryTable(); createCheckBox(); setupTable(); printResults(); } // Inserts a stylesheet to the DOM and fills it with our styles. function insertStyles() { var css = [ "body{font-family:'Segoe UI';font-size:1em}", "table{border-collapse:collapse;padding:0;margin:0}", "table td,table th{padding:3px}", "table tr.test_result td.result{border:1px solid #000;border-right:none}", "table tr.test_result td.details{border:1px solid #000;border-left:none}", "table td.result{font-size:2em;font-family:'Segoe UI Semibold';text-align:center;padding:3px 20px}", "table tr.pass td.result{background-color:#c3d69b}", "table tr.fail td.result{background-color:#d99694}", "table tr.error td.result{background-color:#d99694}", "table tr.bug td.result{background-color:#ffefbb}", "table tr.comment td.result, table tr.comment td.details {font-size:1em;text-align:left;}", "table td.details{padding:0px}", "table td.details table{width:100%;height:100%;}", "table tr.pass td.details th{background-color:#c3d69b}", "table tr.fail td.details th{background-color:#d99694}", "table tr.error td.details th{background-color:#d99694}", "table tr.hidden {display:none;}", "table td.details tr.detail_pass{background-color:#ebf1dd}", "table td.details tr.detail_fail{background-color:#f2dcdb}", "table td.details tr.detail_comment{background-color:#f2ffe9;}", "table td.details tr.detail_comment td{padding: 10px 20px;}", "table td.details tr.detail_comment td.detail_result{font-family:'Segoe UI Semibold';}", "table td.details tr.detail_bug{background-color:#ffefbb;}", "table td.details tr.detail_bug td{padding: 10px 20px;}", "table td.details tr.detail_bug td.detail_result{font-family:'Segoe UI Semibold';}", "table td.details th{font-size:1.1em;font-weight:normal;font-family:'Segoe UI Semibold';text-align:left}", "table td.details table td{padding:3px 20px}", "table td.details table td.error_message{background-color:#d99694;padding:3px}", "table td.details td.detail_message{width:95%}", "table td.details td.detail_result{width:5%}", "#results_summary{margin-bottom:1em;font-size:1.5em;}", "div.mutators { float: right }", "#results_summary td{padding:0 20px;}"]; // Create a style dom element var style = document.createElement('style'); style.type = 'text/css'; style.rel = 'stylesheet'; style.media = 'screen'; // Append the element to the page's header document.getElementsByTagName("head")[0].appendChild(style); // Get a stylesheet object for the element we just inserted var stylesheet = document.styleSheets[document.styleSheets.length - 1]; // Add the styles to the sheet. If there is a cssText property, we can just join together // all of the rules and add them at once. Otherwise, add them one at a time. if (typeof stylesheet.cssText == "string") document.styleSheets[document.styleSheets.length - 1].cssText = css.join(''); else for (var i = 0; i < css.length; i++) stylesheet.insertRule(css[i], i); } //Replace angle brackets for the text function replaceAngleBrackets(text) { if (text == undefined) return ""; if (text.replace == undefined) return text; return text.replace(/&/g, '&') .replace(//g, '>') .replace(/\n/g, '
') .replace(/\s/g, ' '); } // Sets up the master table that will hold the results. function setupTable() { var masterTable = document.createElement("table"); var body = document.createElement("tbody"); masterTable.appendChild(body); document.body.appendChild(masterTable); resultsContainer = body; } // Add a dropdown box to select mutators to apply if we have the mutators ActiveX. function setupMutators() { function applyMutator(e) { // Undefined in IE < 8. if (typeof e === "undefined") e = window.event; var target = (typeof e.target === "undefined") ? e.srcElement : e.target; if (target.value === "") runner.mutators = []; else runner.mutators = [target.value]; // Clear logger and reset. loggerInstance.clear(); Run(); } try { if (typeof mutator === "undefined") { // assigns to global mutator mutator = createActiveXObject("MutatorsHost.MutatorsActiveX"); } } catch (e) { return } var mutators = mutator.GetMutatorsList().split(","); // Create a select box and fill it with options for each mutator. var select = document.createElement("select"); select.appendChild(document.createElement("option")); for (var i = 0; i < mutators.length; i++) { option = document.createElement("option"); option.innerHTML = mutators[i]; option.value = mutators[i]; select.appendChild(option); } select.onchange = applyMutator; // Put the box in a label, put the label in a div, and put the div in the document body. var container = document.createElement("div"); var label = document.createElement("label"); label.innerHTML = "Mutate With: "; label.appendChild(select); container.appendChild(label); container.setAttribute("class", "mutators"); document.body.appendChild(container); } // Adds a result to the results array.. function addResult(type, result, test, testProgress, error) { results.push({ type: type, result: result, test: test, testProgress: testProgress, error: error }); if (domReady) printResults(); } // Goes through all the results and prints them to the table. This results array // fills up when the DOM is not ready yet. // function printResults() { while (results.length > 0) { var result = results.shift(); if (result.type === "comment" || result.type === "bug") addRow(result); else addResultRow(result.result, result.test, result.testProgress, result.error); } } //Add a comment or bug to the status table function addRow(result) { var comment = result.result; var row = resultsContainer.insertRow(-1); var res = row.insertCell(-1); var details = row.insertCell(-1); res.innerHTML = replaceAngleBrackets(result.type); details.innerHTML = replaceAngleBrackets(comment); row.className = result.type.toLowerCase(); res.className = "result"; } // Adds a result to the status table. function addResultRow(result, test, testProgress, error) { var row = resultsContainer.insertRow(-1); var res = row.insertCell(-1); var details = row.insertCell(-1); res.innerHTML = result.toUpperCase(); row.className = "test_result " + result; res.className = "result"; details.className = "details"; var detailsTable = constructDetailsTable(test, testProgress, error); details.appendChild(detailsTable); } // Constructs and returns the table that holds all the test progress that were // run. function constructDetailsTable(test, testProgress, error) { var table = document.createElement('table'); var body, row, cell, text, msgCell; // Create the row with the test description body = document.createElement("tbody"); row = document.createElement("tr"); cell = document.createElement("th"); cell.innerHTML = replaceAngleBrackets(test.id + ": " + test.desc); cell.setAttribute("colSpan", "2"); row.appendChild(cell); body.appendChild(row); // Create an error row if there's an error. if (typeof error != "undefined") { row = document.createElement("tr"); cell = document.createElement("td"); cell.innerHTML = error.name + ": " + error.message; cell.setAttribute("colSpan", "2"); cell.className = "error_message"; row.appendChild(cell); body.appendChild(row); } // Loop through all the test progress adding rows for each. var assertionResult; for (var i = 0; i < testProgress.length; i++) { if (testProgress[i].type === 'comment') assertionResult = "comment"; else if (testProgress[i].type === 'bug') assertionResult = "bug"; else if (testProgress[i].passed) assertionResult = "pass"; else assertionResult = "fail"; row = document.createElement("tr"); row.className = "detail_" + assertionResult; if (testProgress[i].passed) row.className += " hidden"; cell = document.createElement("td"); cell.className = "detail_result"; msgCell = document.createElement("td"); msgCell.className = "detail_message"; cell.innerHTML = replaceAngleBrackets(assertionResult); msgCell.innerHTML = replaceAngleBrackets(testProgress[i].message); if (testProgress[i].type !== 'comment' && testProgress[i].type !== 'bug') { if (!testProgress[i].passed) { msgCell.innerHTML += "
Expected: " + testProgress[i].expected; msgCell.innerHTML += "
Actual: " + testProgress[i].actual; } } row.appendChild(cell); row.appendChild(msgCell); body.appendChild(row); } table.appendChild(body); return table; } function createSummaryTable() { var table = document.createElement("table"); table.id = "results_summary"; var row = table.insertRow(-1); var cell = row.insertCell(-1); cell.id = "testName"; cell = row.insertCell(-1); cell.id = "priority"; cell = row.insertCell(-1); cell.id = "passes"; cell = row.insertCell(-1); cell.id = "failures"; cell = row.insertCell(-1); cell.id = "total"; var rowForFailure = table.insertRow(-1); var cellForFailure = rowForFailure.insertCell(-1); cellForFailure.id = "FailedCases"; document.body.appendChild(table); if (done) populateSummaryTable(); } function populateSummaryTable() { if (!domReady) return; // we'll come back later if we need at the end of createSummaryTable. document.getElementById("testName").innerHTML = "TEST: " + filename; document.getElementById("priority").innerHTML = "PRIORITY: " + priority; document.getElementById("passes").innerHTML = "PASS: " + passes; document.getElementById("failures").innerHTML = "FAIL: " + failures; document.getElementById("total").innerHTML = "TOTAL: " + (passes + failures); if (failures > 0) document.getElementById("FailedCases").innerHTML = "Failed Cases: " + failureList.toString(); if ((passes + failures) === 0) { document.write("

No tests were run! There's probably a SyntaxError in the test file.

") } // Added for XBox team to parse out pass/fail from page title document.title = 'PASS:' + passes + ';FAIL:' + failures + ';TOTAL:' + (passes + failures); } function createCheckBox() { var box = document.createElement("input"); box.type = "checkbox"; box.checked = false; box.id = "displayCheckbox"; var label = document.createElement('label'); label.innerHTML = "Display passed test result" box.onclick = function () { var trList = document.getElementsByTagName("tr"); for (var x = 0; x < trList.length; x++) { if (this.checked) { //Remove hidden in class name trList[x].className = trList[x].className.replace("hidden", ""); } else { //Add hidden to detail_pass trList[x].className = trList[x].className.replace("detail_pass", "detail_pass hidden"); } } } document.body.appendChild(box); document.body.appendChild(label); } function internalComment(type, str) { if (executingTest) testProgress.push({ type: type, message: str }); else addResult(type, str); } this.start = function (file, pri) { filename = file; priority = pri; } this.testStart = function (test) { testProgress = []; // track assertions and comments from tests. executingTest = true; } this.verify = function (test, passed, act, exp, msg) { verifies++; if (verifies < 1000 || !passed) { testProgress.push({ type: 'assertion', passed: passed, actual: act, expected: exp, message: msg }); } } this.pass = function (test) { passes++; addResult('result', 'pass', test, testProgress); executingTest = false; } this.fail = function (test) { failures++; failureList.push(test.id); addResult('result', 'fail', test, testProgress); executingTest = false; } this.error = function (test, error) { failures++; // Throw the error so we can debug in browser. setTimeout(function () { throw error }, 0); addResult('result', 'error', test, testProgress, error); executingTest = false; } this.end = function () { done = true; populateSummaryTable(); } this.comment = function (str) { internalComment('comment', str); }; this.bug = function (number, msg) { msg = msg || ""; internalComment('bug', "Bug: " + number + ": " + msg); }; this.clear = function () { if (typeof resultsContainer == "undefined") return; passes = 0; failures = 0; failureList = []; var children = resultsContainer.childNodes; for (var i = children.length - 1; i >= 0; i--) { resultsContainer.removeChild(children[i]); } } } HTMLLogger.shouldEnable = function () { return typeof document !== "undefined" } loggers.push(HTMLLogger); /////////////////////////////////////////////////////////////////////////////// // WScriptLogger: // Log to the console using WScript. //////////////////////////////////////////////////////////////////////////////// function WScriptLogger() { var passCount = 0; var failCount = 0; var verifications = []; var verbose = false; // Initialize the projection related stuff in JsHost if (Utils.getHOSTType() == Utils.WWAHOST) { WScript.InitializeProjection(); } this.start = function (filename, priority) { if (!verbose) { return; } if (priority === "all") { WScript.Echo(filename); } else { WScript.Echo(filename + " Priority " + priority); } } this.testStart = function (test) { verifications = []; } this.verify = function (test, passed, act, exp, msg) { //print(`test:${test} passed:${passed} act:${act} exp:${exp} msg:${msg}`) if (passed) { if (verbose) { verifications.push("\tPass: " + msg + "\n\t\t Actual: " + act + "\n"); } } else { verifications.push("\n\tFail: " + msg + "\n\t\tExpected: " + exp + "\n\t\t Actual: " + act + "\n"); } } this.pass = function (test) { passCount++; if (verbose) { WScript.Echo("PASS\t" + test.id + ": " + test.desc); //Needed for baselines WScript.Echo(verifications.join("\n")); } } this.fail = function (test) { failCount++; WScript.Echo(""); WScript.Echo(""); WScript.Echo("FAIL\t" + test.id + ": " + test.desc); WScript.Echo(""); WScript.Echo(verifications.join("\n")); WScript.Echo(""); WScript.Echo(""); } this.error = function (test, error) { failCount++; WScript.Echo(""); WScript.Echo(""); WScript.Echo("FAIL\t" + test.id + ": " + test.desc); WScript.Echo("\n\tError: " + error.name + " - " + error.description); if (verifications.length > 0) { WScript.Echo(""); WScript.Echo(verifications.join("\n")); WScript.Echo(""); } WScript.Echo(""); WScript.Echo(""); } this.end = function () { if (verbose) { WScript.Echo(""); WScript.Echo("Passed: " + passCount); WScript.Echo("Failed: " + failCount); if ((passCount + failCount) === 0) { WScript.Echo(""); WScript.Echo("No tests were run! There's probably a SyntaxError in the test file.") } } else { if (failCount === 0) { WScript.Echo('pass'); } else { WScript.Echo('fail'); } } } this.comment = function (str) { if (verbose) { verifications.push("\tComment: " + str); } }; } WScriptLogger.shouldEnable = function () { return typeof WScript !== "undefined" && typeof WScript.Echo !== "undefined" && typeof window.location === "undefined"; } loggers.push(WScriptLogger); // A simple logger object exposed on the window, giving the user access to the comment method. // Simply publishes a comment event. var windowLogger = {}; windowLogger.comment = function (str) { runner.publish('comment', str); } /////////////////////////////////////////////////////////////////////////////// // NightlyLogger: // Provides Logging for automated nightly runs using apgloblib. Must be run in a browser to // function. /////////////////////////////////////////////////////////////////////////////// function NightlyLogger() { var apPlatform; var runCount; function VBS_apglobal_init() { apPlatform = apGlobalObj.apGetPlatform(); runCount = 0; } function apInitTest(stTestName) { if (window.runsExecuted == 0) { // In MaxInterprentCount case do it only on first iteration apGlobalObj.apInitTest(stTestName); } } function apInitScenario(stScenarioName) { runCount++; apGlobalObj.apInitScenario(stScenarioName); } function apLogFailInfo(stMessage, stExpected, stActual, stBugNum) { if (stBugNum == null) stBugNum = ""; // and may Fajen suffer for it apGlobalObj.apLogFailInfo("" + stMessage, "" + stExpected, "" + stActual, "" + stBugNum); } function apEndTest() { if (window.runsExecuted >= window.runsToExecute - 1) { // In MaxInterprentCount case do it only on last iteration if (runCount === 0) { apLogFailInfo("Zero tests were run! Must be a SyntaxError in the test file...", true, false, ''); } apGlobalObj.apEndTest(); } } function apErrorTrap() { apGlobalObj.apLogFailInfo("call to apErrorTrap found!", "", "", ""); } this.start = function (filename) { VBS_apglobal_init() apInitTest(filename) } this.testStart = function (test) { apInitScenario(test.id + " : " + test.desc) } this.verify = function (test, passed, act, exp, msg) { if (!passed) apLogFailInfo(msg, exp, act, ''); } this.pass = function (test) { } this.fail = function (test) { } this.error = function (test, error) { apLogFailInfo("Exception thrown : " + error.name + "Message: " + error.message, true, false, ''); } this.end = function () { apEndTest(); } this.comment = function (msg) { } } NightlyLogger.shouldEnable = function () { try { var GetEnvVariable = function (variable) { var wscriptShell = createActiveXObject("WScript.Shell"); var wshEnvironment = wscriptShell.Environment("Process"); return wshEnvironment(variable); } var result = GetEnvVariable("JScript_NightlyRun"); if (result === "") return false; else return true; } catch (e) { return false; } } loggers.push(NightlyLogger); /////////////////////////////////////////////////////////////////////////////// // Loader42Logger: // Provides Logging for automations runs with Loader42. Must be run in a browser to function. /////////////////////////////////////////////////////////////////////////////// function Loader42Logger() { // Provides logging back to Loader42 (and therefore WTT and Blackbeard) var filename = "File Name Not Specified"; var Loader42Log = { // define some constants for reporting success/failure of a test FAIL: 0, PASS: 1, BLOCKED: 2, Logger: null, TestsComplete: false, Debug: false, // Initialize the ActiveX logger object InitLogger: function () { if (Loader42Log.Logger === null) { try { Loader42Log.Logger = createActiveXObject("Loader42.Logger"); } catch (ex) { // Can't create ActiveXObject element in Edge mode, append Object element instead Loader42Log.Logger = document.createElement("object"); Loader42Log.Logger.setAttribute('id', 'logger'); Loader42Log.Logger.setAttribute('type', 'application/vns.ms-loader42loggerax'); document.body.appendChild(Loader42Log.Logger); } try { // Cleanup logger in case where HTMLLoader is not in use // but Loader42 is present. if (Loader42Log.Logger.loaderName !== "HTMLLoader") { Loader42Log.Logger = null; } } catch (ex) { Loader42Log.Logger = null; } } }, // Called at the start of every test case. Argument is the name of the test // case. TestStart: function (name) { Loader42Log.InitLogger(); if (Loader42Log.Logger) { Loader42Log.Logger.LogTestStart(name); } else if (Loader42Log.Debug) { alert("TestStart(): " + name); } }, // Called at the end of every test case. Name should match what is used in // TestStart(). Result should be one of the constants // Logger42Log.FAIL, // Logger42Log.PASS, // Logger42Log.BLOCKED // Message is a simple descriptive error, mostly useful in explaining why // a test failed or was blocked. TestFinish: function (name, result, message) { Loader42Log.InitLogger(); if (Loader42Log.Logger) { Loader42Log.Logger.LogTestEnd(name, result, message); } else if (Loader42Log.Debug) { var status; if (result === Loader42Log.PASS) { status = "PASS"; } else if (result === Loader42Log.FAIL) { status = "FAIL"; } else if (result === Loader42Log.BLOCKED) { status = "BLOCKED"; } else { status = "UNKNOWN"; } alert("TestFinish(): " + name + "\n\n" + status + ": " + message); } }, // Logs a message on behalf of the function. Used for tracing/debugging. TestComment: function (comment) { Loader42Log.InitLogger(); if (Loader42Log.Logger) { Loader42Log.Logger.LogComment(comment); } }, // Signaled at the end of the test, when the page wishes to close the // browser. Should only be called once. AllTestsComplete: function () { Loader42Log.InitLogger(); if (Loader42Log.TestsComplete) { Loader42Log.TestStart("LogException"); Loader42Log.TestComment("AllTestsComplete() called twice."); Loader42Log.TestFinish("LogException", Loader42Log.PASS, "AllTestsComplete() called twice."); } else { Loader42Log.TestsComplete = true; if (Loader42Log.Logger) { // Since success/failure is really determined by if there are // any failures in TestFinish, we don't have to really worry // about logging success or failure here. Loader42Log.Logger.LogResult(true, "AllTestsComplete"); } else if (Loader42Log.Debug) { alert("AllTestsComplete"); } } } }; //Added for Loader42- True for the first Scenario var firsttest = true; var prevScenario = ""; var loggermessage = ""; var loggerresult = 1; function apInitTest(stTestName) { if (window.runsExecuted == 0) { // In MaxInterprentCount case do it only on first iteration filename = stTestName; //Added for Loader42 - InitLogger will initialize the logger Loader42Log.InitLogger(); } } function apInitScenario(stScenarioName) { //Added for Loader42-If this is the first scenario do not call Test Fininsh if (firsttest === true) { Loader42Log.TestStart(stScenarioName); firsttest = false; //Done with the first test } else { //Added for Loader42- if the Scenario Succeeded log the result as passed if (loggerresult === 1) { Loader42Log.TestComment(""); Loader42Log.TestComment("File Name " + filename); Loader42Log.TestComment("Result PASSED"); Loader42Log.TestComment("Scenario " + prevScenario); Loader42Log.TestComment(""); } //TestFinish the previous scenario Loader42Log.TestFinish(prevScenario, loggerresult, loggermessage); //Test start a New Scenario Loader42Log.TestStart(stScenarioName); //Added for Loader42-Reset the Variable back to the initial value for the New Scenario loggermessage = ""; //Clear tge loggermessage loggerresult = 1; //Set Default to Success } prevScenario = stScenarioName; } function apLogFailInfo(stMessage, stExpected, stActual, stBugNum) { if (stBugNum == null) stBugNum = ""; // and may Fajen suffer for it //Added for Loader42 - Store the result in a global variable with all the values Loader42Log.TestComment(""); Loader42Log.TestComment("File Name " + filename); Loader42Log.TestComment("Result FAILED"); Loader42Log.TestComment("Scenario " + stMessage); Loader42Log.TestComment("EXPECTED Result " + stExpected); Loader42Log.TestComment("ACTUAL Result " + stActual); Loader42Log.TestComment(""); loggerresult = 0; //this indicates failiure } function apEndTest() { if (window.runsExecuted >= window.runsToExecute - 1) { // In MaxInterprentCount case do it only on last iteration //Added for Loader42- Finish the last test that was started if (loggerresult === 1) { Loader42Log.TestComment(""); Loader42Log.TestComment("File Name " + filename); Loader42Log.TestComment("Result PASSED"); Loader42Log.TestComment("Scenario " + prevScenario); Loader42Log.TestComment(""); } //Finishing the last Scenario Loader42Log.TestFinish(prevScenario, loggerresult, loggermessage); //Added for Loader42 - - called once to finish the test Loader42Log.TestComment("Ending Test" + filename); Loader42Log.AllTestsComplete(); } } this.start = function (filename) { apInitTest(filename) } this.testStart = function (test) { apInitScenario(test.desc) } this.verify = function (test, passed, act, exp, msg) { if (!passed) apLogFailInfo(msg, exp, act, ''); } this.pass = function (test) { } this.fail = function (test) { } this.error = function (test, error) { apLogFailInfo("Exception thrown : " + error.name + "Message: " + error.message, true, false, ''); } this.end = function () { apEndTest(); } this.comment = function (msg) { Loader42Log.TestComment(msg); } } Loader42Logger.shouldEnable = function () { try { var Logger = createActiveXObject("Loader42.Logger"); return true; } catch (e) { return false; } return false; } loggers.push(Loader42Logger); /////////////////////////////////////////////////////////////////////////////// // StressLogger: // Provides logging support via the DrummerLogger ActiveX object to log and report to the Drummer test harness. It also uses the HTML logger to output the results in IE. /////////////////////////////////////////////////////////////////////////////// function StressLogger() { var htmlLogger = new HTMLLogger(); var htmlLoggerRegistered = false; var logger = new ActiveXObject("DrummerLogger.Logger"); var that = this; var id; window.onerror = function () { this.end(); } //logger.Config.NumberIterations -> Contains the number of iterations asked to perform this.CurrentTestGroupIteration = 1; function getRunId() { id = window.location.href.substring(window.location.href.indexOf("stressrun=") + 10) } this.start = function (fileName) { getRunId(); logger.Initialize(id); that.Config = logger.Config; logger.Comment("Test File: " + fileName); logger.Comment("Runtime Iterations Requested: " + that.Config.RuntimeIterations); logger.Comment("Testcase Iterations Requested: " + that.Config.TestcaseIterations); logger.Comment("Collection Frequency Requested: " + that.Config.CollectionFrequency); if (that.Config.OutputHTML && !htmlLoggerRegistered) { runner.subscribe(htmlLogger); htmlLoggerRegistered = true; } } this.testStart = function (test) { logger.InitScenario(test.id + ": " + test.desc); } this.verify = function (test, passed, act, exp, msg) { logger.Comment("Passed: " + passed + "; Actual: " + act + "; Expected: " + exp + "; Message: " + msg); } this.pass = function (test) { logger.EndScenario("pass", test.desc); } this.fail = function (test) { logger.EndScenario("fail", test.desc); } this.error = function (test, error) { logger.Error(error.message); } this.end = function () { logger.Comment("TestGroupIteration Executed: " + (that.CurrentTestGroupIteration - 1)); logger.Finished(); } this.comment = function (msg) { logger.Comment(msg); } this.CollectMetrics = function () { logger.CollectMetrics(); } } StressLogger.shouldEnable = function () { try { var logger = new ActiveXObject("DrummerLogger.Logger"); var str = window.location.href.substring(window.location.href.indexOf("?") + 1); if (str.indexOf("stressrun") > -1) return true; else return false; } catch (e) { return false; } } loggers.push(StressLogger); /////////////////////////////////////////////////////////////////////////////// // End Loggers /////////////////////////////////////////////////////////////////////////////// //scheduler in charge of giving us tasks var scheduler = null; var runner = (function () { //holds onto objects that wish to be notified of pub/sub events var subscribers = []; //callbacks that get run before any tests execute var globalSetups = []; //callbacks that get run after all tests execute var globalTeardowns = []; //how many start calls we are waiting for var waitCount = 0; //current handle returned from setTimeout var waitHandle = null; //valid states //init - initial state // => running // => error //running - running test // => waiting // => ending // => error //waiting - waiting for async op // => waiting // => resuming // => timeout // => error //resuming - resuming from async op // => error // => running //timeout - timed out during async op // => resuming // => error //ending - final state //error - error in the test // => running // => ending //current state of the runner var currentState = "init"; //name of the currently executing file/suite. This is set via //setting Loader42_FileName on the global scope var fileName = null; //priority to run for filtering tests. Valid values are "all" or a number var priority = "all"; //default timeout for async tasks var DEFAULT_TIMEOUT = 10000; //flag for the main run loop var ended = false; //object holding valid states and the action to take each iteration while running in them var states = { init: function () { if (window.runsExecuted == 0) { // In MaxInterprentCount case do it only on first iteration runner.publish('start', fileName, priority); } scheduler.prepend(globalSetups); scheduler.push(globalTeardowns); transitionTo("running"); }, resuming: function () { transitionTo("running"); }, running: function () { var task = scheduler.next(); if (task) { try { task(); } catch (ex) { runner.publish('error', currentTest, ex); transitionTo("error"); } } else { transitionTo('ending'); } }, ending: function () { if (++window.runsExecuted < window.runsToExecute) { transitionTo("restarting"); } else { runner.publish("end", fileName, priority); ended = true; } }, restarting: function () { tasks = ranTasks; ranTasks = []; transitionTo("init"); }, waiting: function () { }, timeout: function () { if (waitHandle === null) return; waitHandle = null; currentTest.error = new Error("Timed out before runner.start() being called"); testPassed = false; waitCount = 0; transitionTo("resuming"); }, error: function () { ended = true; } }; // Subscribes to an event named prop for each property of obj, with the callback being the // value. function subscribeObject(obj) { for (var prop in obj) if (typeof obj[prop] === "function") subscribeToEvent(prop, obj[prop]); } // Subscribes to an event of a given name with the given callback. function subscribeToEvent(e, callback) { if (typeof subscribers[e] === "undefined") subscribers[e] = []; subscribers[e].push(callback); } //transitions the runner to a new state and validates that the transition is allowed //parameters: // * newState: string representing the new state to transition to function transitionTo(newState) { function transitionAssert() { var res = false; for (var i = 0; i < arguments.length; i++) if (arguments[i] === newState) res = true; runnerAssert(res, "invalid state transition: " + currentState + "->" + newState) } switch (currentState) { case "init": transitionAssert("running", "error"); break; case "running": transitionAssert("waiting", "ending", "error"); break; case "waiting": transitionAssert("resuming", "waiting", "error", "timeout"); break; case "timeout": transitionAssert("resuming", "error"); break; case "resuming": transitionAssert("running", "error"); break; case "error": transitionAssert("running", "ending"); break; case "ending": transitionAssert("restarting", "error"); break; case "restarting": transitionAssert("init", "error"); break; default: runnerAssert(false, "Invalid state"); break; } currentState = newState; } //internal check to validate assumptions at given points in the code //along the lines of the C ASSERT macro or C# asserts //parameters: // * bool - boolean value that is assumed to be true // * msg - message to publish if the bool is false //returns: // the bool passed in (for use in conditionals or assignment) function runnerAssert(bool, msg) { if (!bool) { runner.publish('comment', "Internal failure: " + msg); transitionTo("error"); currentTest.error = new Error("Internal failure: " + msg); } return bool; } //main runloop for the runner. Loops continuously unless state //is waiting or the test is ended. Each iteration, it runs the action //associated with the current state function runTasks() { while (!ended) { states[currentState](); if (currentState === "waiting") return; } } //wraps globalSetup or globalTeardown operations in a try catch block to allow logging function wrapGlobalOperation(type, task) { return function () { try { task(); } catch (e) { runner.publish('comment', "Error during " + type + " - " + e.name + ": " + e.description); runner.publish('end', fileName, priority); transitionTo("error"); } } } // Runner object is responsible for controlling the execution of tests. It is exposed on the global // object to external code can call any method on runner. return { // Subscribe to an event by passing an eventName and a callback function. Subscribe to // multiple events by passing an object with keys that correspond to the event names you // want to subscribe to. subscribe: function () { if (arguments.length == 1) subscribeObject(arguments[0]); else subscribeToEvent(arguments[0], arguments[1]); }, // Publish an event. Calls all subscribed callbacks with the provided arguments. publish: function (e) { var args = Array.prototype.slice.call(arguments, 1); if (typeof subscribers[e] !== "undefined") for (var i = 0; i < subscribers[e].length; i++) subscribers[e][i].apply(undefined, args); }, mutators: [], //Runs list of tests once. //parameters: // * testFileName - name of the test file/suite run: function (testFileName) { runnerAssert(scheduler !== null, "no scheduler set. This only happens if setScheduler is not called with a scheduler"); if (ended) { // We are running it again window.runsExecuted = 0; ended = false; tasks = ranTasks; ranTasks = []; currentState = "init"; } runnerAssert(currentState === "init", "run called while runner in bad state. Probably means multiple calls to run were made"); if (typeof testFileName === "undefined") { try { testFileName = Loader42_FileName; // Loader42_FileName may be undefined. } catch (e) { //noop } finally { testFileName = testFileName || "No filename given"; } } fileName = testFileName; runTasks(); }, //Decrements the wait counter and instructs the runner to resume running after a wait call // if the counter is 0. If the timeout has fired, this is a noop. //parameters: // * testWaitHandle - waithandle corresponding to the return value from start start: function (testWaitHandle) { if (typeof testWaitHandle !== "undefined" && testWaitHandle !== waitHandle) return; waitCount--; if (waitCount === 0) { clearTimeout && clearTimeout(testWaitHandle); transitionTo("resuming"); waitHandle = null; runTasks(); } }, //Increments the wait counter and instructs the runner to wait for an async op to finish //if the counter is > 0. If count is passed, the wait counter is incremented by that amount. //parameters: // * timeout - time (in milliseconds) to wait before timing out. Defaults to DEFAULT_TIMEOUT // * count - amount to increment counter by. Defaults to 1 //returns: // * the waitHandle produced by setTimeout wait: function (timeout, count) { count = count || 1; timeout = timeout || DEFAULT_TIMEOUT; waitCount += count; if (waitCount > 0) { transitionTo("waiting"); waitHandle = setTimeout && setTimeout(function () { transitionTo("timeout"); runTasks(); }, timeout); return waitHandle; } }, // Create a new test object with the properties specified in obj. addTest: function (obj) { var test = new TestCase(); for (var prop in obj) { test[prop] = obj[prop] } test.AddTest(); }, globalSetup: function (callback) { globalSetups.push(wrapGlobalOperation("global setup", callback)); }, globalTeardown: function (callback) { globalTeardowns.push(wrapGlobalOperation("global teardown", callback)); }, //sets the scheduler for the runner. Note that this doesn't (currently) unregister the old scheduler. //As part of registering the scheduler, the 'schedule' and 'prepend' events are registered for pub/sub //parameters: // * sched - new scheduler object setScheduler: function (sched) { scheduler = sched; runner.subscribe("schedule", sched.schedule); } }; })(); for (var i = 0; i < loggers.length; i++) if (loggers[i].shouldEnable()) runner.subscribe(new loggers[i]()); // holds the list of tasks that need to be scheduled. Running is currently destructive to this list var tasks = []; // holds the list of tasks that have been run already, so that tasks[] list can be restored after the run var ranTasks = []; window.runsExecuted = 0; window.runsToExecute = Utils.getMaxInterpretCount() + 1; // populate and return default scheduler object var defaultScheduler = (function () { return { //adds an item to the tail of the list. If the item has a tasks property that is a function //the return value of that function is used instead of this item //parameters: // * item - item to schedule schedule: function (item) { var items; if (item.tasks && typeof item.tasks === "function") items = item.tasks(); else items = [item]; for (var i = 0; i < items.length; i++) tasks.push(items[i]); }, //prepends an item or array to the list. If the item is an array, it is concatenated onto //the front of the list (i.e. single level flatten). Otherwise it is just put into the list //parameters: // * obj - item to put onto the front of the list prepend: function (obj) { var type = hoozit(obj); if (type === "array") { for (var i = obj.length - 1; i >= 0; i--) { tasks.unshift(obj[i]); } } else { tasks.unshift(obj); } }, //appends an item or array to the list. If the item is an array, it is concatenated onto //the end of the list (i.e. single level flatten). Otherwise it is just put into the list //parameters: // * obj - item to put onto the end of the list push: function (obj) { var type = hoozit(obj); if (type === "array") { for (var i = 0; i < obj.length; i++) { tasks.push(obj[i]); } } else { tasks.push(obj); } }, //returns the next task to run //returns: //the next task to run or null if there are no more tasks next: function () { var task = null; if (tasks.length > 0) { task = tasks.shift(); ranTasks.push(task); } return task; } }; })(); runner.setScheduler(defaultScheduler); /////////////////////////////////////////////////////////////////////////////// // Test Runner /////////////////////////////////////////////////////////////////////////////// var currentTest; var testPassed; // A test case object var TestCase = function () { //close over this for the tasks method var testCase = this; // Flag to execute the test in global scope this.useGlobalThis = false; this.baselineFile = false; this.baselineHandler = 0; this.baselineCounter = 0; this.pass = true; this.AddTest = function () { // Function to add use test case to testCases list if ((this.id === undefined) || (this.id === "")) { runner.publish('comment', "Test case id is not valid "); } else if (this.desc === undefined || this.desc === "") { runner.publish('comment', "Test case description not specified"); } else if (this.preReq && (!(typeof (this.preReq) === "function"))) { runner.publish('comment', "Invalid preReq function"); } else if ((this.test === undefined) || (!(typeof (this.test) === "function"))) { runner.publish('comment', "Invalid test function"); } else { runner.publish('schedule', this); } }; //splits the test work into setup, test and cleanup. This allows the runner to pause //between the test task and the cleanup task in order to wait for async operations //returns: // - an array of functions this.tasks = function () { function setup() { testPassed = true; currentTest = testCase; runner.publish('testStart', currentTest); } function test() { // if (!Utils.IEMatchesArch()) { // throw new Error("IE architecture setting does not match Processor architecture and run settings: " + Utils.getIEArchitecture()); // } if (!currentTest.preReq || currentTest.preReq()) { try { if (currentTest.useGlobalThis) { testFunc = currentTest.test; testFunc(); } else { currentTest.test(); } } catch (e) { currentTest.error = e; } } else { runner.publish('comment', "Test prereq returned false. Skipping"); } } function cleanup() { if (currentTest.error !== undefined) { runner.publish('error', currentTest, currentTest.error); } else if (testPassed) { runner.publish('pass', currentTest); } else { runner.publish('fail', currentTest); } runner.publish('testEnd', currentTest); } return [setup, test, cleanup]; } } /* * Baseline test cases */ function addEscapeCharacter(txt) { txt = txt.replace(/\\/g, "\\\\"); txt = txt.replace(/\"/g, "\\\""); txt = txt.replace(/\'/g, "\\\'"); txt = txt.replace(/\r/g, "\\r"); txt = txt.replace(/\n/g, "\\n"); return txt; } //Testcase that will use baseline support function BaselineTestCase() { //close over this for the tasks method var testCase = this; // Flag to execute the test in global scope this.useGlobalThis = false; this.baselineFile = false; this.baselineHandler = 0; this.baselineCounter = 0; this.pass = true; this.AddTest = function () { // Function to add use test case to testCases list if ((this.id === undefined) || (this.id === "")) { runner.publish('comment', "Test case id is not valid "); } else if (this.desc === undefined || this.desc === "") { runner.publish('comment', "Test case description not specified"); } else if (this.preReq && (!(typeof (this.preReq) === "function"))) { runner.publish('comment', "Invalid preReq function"); } else if ((this.test === undefined) || (!(typeof (this.test) === "function"))) { runner.publish('comment', "Invalid test function"); } else { runner.publish('schedule', this); } }; //splits the test work into setup, test and cleanup. This allows the runner to pause //between the test task and the cleanup task in order to wait for async operations //returns: // - an array of functions this.tasks = function () { function setup() { testPassed = true; currentTest = testCase; BaselineTestCase.startBaseline(); runner.publish('testStart', currentTest); } function test() { if (!currentTest.preReq || currentTest.preReq()) { try { if (currentTest.useGlobalThis) { testFunc = currentTest.test; testFunc(); } else { currentTest.test(); } } catch (e) { currentTest.error = e; } } else { runner.publish('comment', "Test prereq returned false. Skipping"); } } function cleanup() { if (currentTest.error !== undefined) { runner.publish('error', currentTest, currentTest.error); } else if (testPassed) { runner.publish('pass', currentTest); } else { runner.publish('fail', currentTest); } BaselineTestCase.stopBaseline(); runner.publish('testEnd', currentTest); } return [setup, test, cleanup]; } } //Static variables for BaselineTestCase BaselineTestCase.baselineFileExt = ".baseline.js"; BaselineTestCase.editMode = false; BaselineTestCase.useGlobalBaseline = false; BaselineTestCase.fileName = null; BaselineTestCase.fileHandler = null; BaselineTestCase.elementIndexInFile = 0; BaselineTestCase.currentTestcaseID = 0; //Static methods for BaselineTestCase BaselineTestCase.constructBLFileName = function (testCaseID) { var filePath = window.location.href; var fileName = filePath.substring(filePath.lastIndexOf("/") + 1); fileName = fileName.substring(0, fileName.lastIndexOf(".")); if (typeof (testCaseID) == "undefined") { fileName = fileName + "." + getHOSTMode() + BaselineTestCase.baselineFileExt; } else { testCaseID = String(testCaseID).replace(/[^a-zA-Z 0-9]+/g, "_"); fileName = fileName + testCaseID + "." + getHOSTMode() + BaselineTestCase.baselineFileExt; } var baselineFilePath = filePath.substring(0, filePath.lastIndexOf("/") + 1) + fileName; //redirecting the baseline file path to js directory when we find EzeHtmlTestFiles directory structure baselineFilePath = baselineFilePath.replace(/EzeHtmlTestFiles/, "EzeJSTestFiles"); // mutator local run will fail in baseline testcases if don't reset this counters BaselineTestCase.elementIndexInFile = 0; BaselineTestCase.currentTestcaseID = 0; //loadjsfile(baselineFilePath); return baselineFilePath; } BaselineTestCase.openBaseline = function (fileName) { var objFSO = createActiveXObject("Scripting.FileSystemObject"); if (fileName.indexOf("file:///") > -1) fileName = fileName.substring(8); BaselineTestCase.fileHandler = objFSO.CreateTextFile(fileName, true); BaselineTestCase.fileHandler.WriteLine("var baselineExpArr = ["); } BaselineTestCase.closeBaseline = function () { if (BaselineTestCase.editMode) { BaselineTestCase.fileHandler.WriteLine(); BaselineTestCase.fileHandler.WriteLine("];"); BaselineTestCase.fileHandler.Close(); } else { //If there is more elements in the baseline file has not been compared, fail the test. if (typeof (baselineExpArr) != "undefined") { if (BaselineTestCase.elementIndexInFile < baselineExpArr.length) { fail("There is more elements in baseline file than expected. Total element in baselinefile=" + baselineExpArr.length + ", expected=" + BaselineTestCase.elementIndexInFile); } } if (!BaselineTestCase.useGlobalBaseline) { BaselineTestCase.elementIndexInFile = 0; BaselineTestCase.fileHandler = 0; } } } BaselineTestCase.verify = function (act, msg) { if (BaselineTestCase.editMode) { if (BaselineTestCase.elementIndexInFile > 0) { BaselineTestCase.fileHandler.WriteLine(","); } var escapedAct = act; if (act instanceof String || typeof act == "string") { escapedAct = addEscapeCharacter(act); } if (escapedAct != null) //don't add quotes to null values { BaselineTestCase.fileHandler.Write("\"" + escapedAct + "\""); } else { BaselineTestCase.fileHandler.Write("null"); } BaselineTestCase.elementIndexInFile++; } else { if (BaselineTestCase.elementIndexInFile >= baselineExpArr.length) { fail("Test is expecting more elements in baseline file.Expecting " + (BaselineTestCase.elementIndexInFile + 1) + ", actual = " + baselineExpArr.length); } else { var exp = baselineExpArr[BaselineTestCase.elementIndexInFile++]; if (act != null && act.toString) verify(act.toString(), exp, msg); else verify(act, exp, msg); } } } BaselineTestCase.loadBaseline = function (fileName) { var loadSuccess = true; var oXmlHttp = new XMLHttpRequest(); oXmlHttp.OnReadyStateChange = function () { if (oXmlHttp.readyState == 4) { //XmlHttpRequest will return 0 if we request a file from local disk //Return value 200 on succees only if we request a file from web server if (oXmlHttp.status == 200 || window.location.href.indexOf("http") == -1) { if (fileName != null) { if (document.getElementById("blScriptTag") == null) { var header = document.getElementsByTagName('HEAD').item(0); var oScript = document.createElement("script"); oScript.language = "javascript"; oScript.type = "text/javascript"; oScript.defer = true; oScript.id = "blScriptTag"; oScript.text = oXmlHttp.responseText; header.appendChild(oScript); } else { //Get response text and strip the "var baselineExpArr =" then assign the new value to baselineExpArr var a = oXmlHttp.responseText; baselineExpArr = eval(a.substring(a.indexOf("=") + 1, a.length - 2)); } } } else { loadSuccess = false; } } } oXmlHttp.open('GET', fileName, false); oXmlHttp.send(null); if (!loadSuccess) throw new Error('XML request error for file ' + fileName + ': ' + oXmlHttp.statusText + ' (' + oXmlHttp.status + ')'); } BaselineTestCase.startBaseline = function () { if (BaselineTestCase.useGlobalBaseline) return; //Set baseline file name based on the fileName from Run method and test id //This block will be executed we are not using global baseline if (currentTest instanceof BaselineTestCase) { //Create baseline file if baselineEditMode is true, otherwise load the js file if (BaselineTestCase.editMode) { BaselineTestCase.openBaseline(BaselineTestCase.constructBLFileName(currentTest.id)); } else { BaselineTestCase.loadBaseline(BaselineTestCase.constructBLFileName(currentTest.id)); } } } BaselineTestCase.stopBaseline = function () { if (BaselineTestCase.useGlobalBaseline) return; if (currentTest instanceof BaselineTestCase) { //Close the baseline file BaselineTestCase.closeBaseline(); } BaselineTestCase.currentTestcaseID++; } BaselineTestCase.startGlobalBaseline = function () { if (!BaselineTestCase.useGlobalBaseline) return; try { //Open baseline file if we use global baseline (one baseline for all test) if (BaselineTestCase.editMode) { BaselineTestCase.openBaseline(BaselineTestCase.constructBLFileName()); } //load the global baseline file else { BaselineTestCase.loadBaseline(BaselineTestCase.constructBLFileName()); } } catch (e) { fail("Exception occured on opening baseline file with message: " + e.message); } } BaselineTestCase.stopGlobalBaseline = function () { if (!BaselineTestCase.useGlobalBaseline) return; try { //Close the baseline file (only if we use global baseline file) BaselineTestCase.closeBaseline(); } catch (e) { fail("Exception occured on closing baseline file with message: " + e.message); } } // Subscribe to various events to set up baseline. runner.subscribe('start', BaselineTestCase.startGlobalBaseline); runner.subscribe('end', BaselineTestCase.stopGlobalBaseline); /* * This object facilitates testing cross-context scenarios. Create a new instance first to getcd * started. See http://devdiv/sites/bpt/team/eze/Eze%20Wiki/Cross%20Context%20Test%20Framework.aspx * for documentation. */ var CrossContextTest = function () { var cct = this; cct.testIframe = true; cct.testWindow = (Utils.getHOSTType() == Utils.WWAHOST) ? false : true; var waitingForReady = false; var readyCallbacks = []; var childFunctions = []; var childProperties = {}; /* * Wait for the document to be ready for DOM updates. * This should be updated to use DOMContentReady when that is supported. */ function onReady(callback) { // Check if we're already ready, and if so, just call the callback. if (document.readyState == "complete") { callback.call(); return; } // Otherwise, push to our ready list and start waiting. readyCallbacks.push(callback); // If we're not already waiting, wait for document ready and call callbacks. if (!waitingForReady) { waitingForReady = true; waitForReady(); } } /* * Wait until doScroll doesn't throw an exception, and then run all the callbacks. */ function waitForReady() { // Otherwise use the oft-used doScroll hack which, according to MSDN, will always thrown an // error unless the document is ready. try { document.documentElement.doScroll("left"); } catch (e) { // We're not ready yet. Check again in a bit. setTimeout(waitForReady, 1); return; } // Assume we're ready, so call all the callbacks. for (var i = 0; i < readyCallbacks.length; i++) { readyCallbacks[i].call(); } } /* * Sets any properties on the newly opened window. * be careful -- this only reference obj from parent context */ function setChildWindowProperties(win) { for (var prop in childProperties) { win[prop] = childProperties[prop]; } } /* * Writes the HTML to the child window by appending HTML tag. Used for WWA * * parentWindow, that refers to either window.parent or window.opener, depending on if this is a * popup window or an iframe, * Append html, head, body and script content to iframe/popup window */ function AppendChildHtml(doc) { function createChildTag(tagName, attributes) { tag = doc.createElement(tagName); for (var k in attributes) { tag.setAttribute(k, attributes[k]); } return tag; } doc.open(); doc.write(""); doc.close(); var head = doc.getElementsByTagName("head")[0]; var meta1 = createChildTag('meta', { 'http-equiv': '"X-UA-Compatible"', 'content': '"IE=' + document.documentMode + '"' } ); head.appendChild(meta1); // add the body content if (typeof cct.childContent !== 'undefined') { var body = doc.getElementsByTagName("body")[0]; body.innerHTML = cct.childContent; } // this function will be sent to child, be called when the script load complete function __init__(sender) { parentWindow = window.parent == window ? window.opener : window.parent; if (sender.readyState == "complete") { sender.onreadystatechange = null; parentWindow.childReady.ready(); // notify parent to start test } } // init the script var script = createChildTag('script', { type: 'text/javascript', onreadystatechange: "iamready(this)" } ); script.text += __init__.toString() + "\n" + "var iamready = __init__;\n"; // write the childFunctions for (var i = 0; i < childFunctions.length; i++) { script.text += childFunctions[i].toString() + "\n"; } head.appendChild(script); } /* * Writes html content to doc. Used for IE * * parentWindow, that refers to either window.parent or window.opener, depending on if this is a * popup window or an iframe, */ function WriteChildHtml(doc) { doc.open(); doc.write("\n\n"); doc.write("\n"); doc.write("\n"); doc.write("var parentWindow = window.parent == window ? window.opener : window.parent;\n"); //// function waitForReady will sent to child, to determin if child is ready function waitForReady() { if (document.addEventListener) { document.addEventListener("DOMContentLoaded", whenDomReady, false); } else { setTimeout(waitForDomReady, 1); } function waitForDomReady() { // Otherwise use the oft-used doScroll hack which, according to MSDN, will always thrown an // error unless the document is ready. try { document.documentElement.doScroll("left"); } catch (e) { // We're not ready yet. Check again in a bit. setTimeout(waitForDomReady, 1); return; } whenDomReady(); } function whenDomReady() { parentWindow.childReady.ready(); } } ////// doc.write(waitForReady.toString() + "\n"); doc.write("waitForReady();\n"); for (var i = 0; i < childFunctions.length; i++) { doc.write(childFunctions[i].toString() + "\n"); } doc.write("\n\n\n"); if (cct.childContent !== undefined) doc.write(cct.childContent) doc.write(""); doc.close(); } var writeChildContent = (Utils.getHOSTType() == Utils.WWAHOST) ? AppendChildHtml : WriteChildHtml; // Set either of these to control the content of the child windows. this.childContent = undefined; /* * If we have Loader42Log, use that to log comments, otherwise use a nop. This is merely a * default and can be overridden by users should they need custom logging. */ if (typeof window.logger !== 'undefined') { this.comment = logger.comment; } else { this.comment = new Function(); } /* * Add a function to the list of functions to append to the child windows. * NOTE: While when you define these functions they look like closures, they are not used as such. * The toString representation is appended to the new windows, destroying any scope you might have * set up. */ this.addChildFunction = function (func) { childFunctions.push(func); } /* * Add a property to the list of properties to give to the child windows. */ this.addChildProperty = function (name, value) { childProperties[name] = value; } function ChildReady(waitHandle) { this.waitHandle = waitHandle; this.ready = function () { runner.start(this.waitHandle); } } var prepareIframe = function () { cct.comment("Starting iframe tests"); cct.frame = document.createElement('iframe'); document.body.appendChild(cct.frame); var waitHandle = runner.wait(); window.childReady = new ChildReady(waitHandle); writeChildContent(cct.frame.contentDocument ? cct.frame.contentDocument : cct.frame.contentWindow.document); } var runIframe = function () { if (typeof BaselineTestCase !== 'undefined' && typeof mutator !== 'undefined') { // save the check point for window.open cct.baselineTCElementIndexInFile = BaselineTestCase.elementIndexInFile; } setChildWindowProperties(cct.frame.contentWindow); try { cct.callback.call(undefined, cct.frame.contentWindow); } catch (e) { currentTest.error = e; } } var cleanupIframe = function () { window.childReady = undefined; // Remove the iframe when we're done so it doesn't interfere with any other tests. document.body.removeChild(cct.frame); } var preparePopWin = function () { cct.comment("Starting new window tests"); cct.win = window.open(); var waitHandle = runner.wait(); window.childReady = new ChildReady(waitHandle); writeChildContent(cct.win.document); } var runPopWin = function () { if (typeof BaselineTestCase !== 'undefined' && typeof mutator !== 'undefined') { // save the check point for window.open BaselineTestCase.elementIndexInFile = cct.baselineTCElementIndexInFile; } setChildWindowProperties(cct.win); try { cct.callback.call(undefined, cct.win); } catch (e) { currentTest.error = e; } } var cleanupPopWin = function () { window.childReady = undefined; cct.win.close(); window.parentWaitHandle = undefined; // make sure the popup window is closed and then move on var waitHandle = runner.wait(); setTimeout(function () { try { if (!cct.win.closed) { setTimeout(arguments.callee, 10); return; } } catch (e) { } runner.start(waitHandle); }, 10); } /* * Run a test. Do any verification in the callback. The callback will be called with the new * window as the first parameter. If you want to wait for the document to be ready, pass true * for the second parameter. You should note that this will currently break our test framework * as nothing in the test function body can be asyncrhonous. When a test is asynchronous, the * test will pass (with no verify statements executed) while the verifies wait in the * background for the document to become ready. */ this.test = function (callback, waitForReady) { var run = function () { cct.callback = callback; if (cct.testWindow) { // Test with window.open scheduler.prepend([preparePopWin, runPopWin, cleanupPopWin]); } if (cct.testIframe) { // Test with iframe. scheduler.prepend([prepareIframe, runIframe, cleanupIframe]); } }; if (waitForReady) onReady(run); else run(); } } //Mutator support var mutator; // env will be the empty string if the environment variable is not present. var env = ""; try { env = (createActiveXObject("WScript.Shell")).Environment("Process")("JScript_Mutators"); if (env.length > 0) runner.mutators = env.split(","); } catch (e) { } (function () { var originalTest; runner.subscribe('testStart', function (test) { if (runner.mutators.length === 0) return; try { if (typeof mutator === "undefined") mutator = createActiveXObject("MutatorsHost.MutatorsActiveX"); runner.publish('comment', "Mutating with " + runner.mutators.join(",")); originalTest = test.test; originalTestString = originalTest.toString(); // Parse out just the function body to mutate. testBody = originalTestString.substring(originalTestString.indexOf("{") + 1, originalTestString.lastIndexOf("}")); newTestString = mutator.Mutate(runner.mutators.join(","), testBody); // Create a new function with the mutated function body. Note that this destroys the // lexical environment of the original test function, so test code that relies on // identifiers declared outside of the function body but not in global scope will fail. test.test = new Function(newTestString); runner.publish('comment', "
" + test.test.toString() + "
"); } catch (e) { } }) /* Restore the original test object after the test has finished. */ runner.subscribe('testEnd', function (test) { if (runner.mutators.length === 0) return; test.test = originalTest; }) })() /**************************************************************************** //Function for stress testing that determines if the test group should loop again //Currently not working as scheduler does not call this function. //Need to change the stressLogger to StressLogger.shouldEnable() ****************************************************************************/ function ContinueTestGroupIteration() { if (typeof stressLogger !== "undefined") { stressLogger.CurrentTestGroupIteration++; if (stressLogger.Config.RuntimeIterations < stressLogger.CurrentTestGroupIteration) { return false; } return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // Verification functions. /////////////////////////////////////////////////////////////////////////////// //the wrapping function is really just to allow folding in editors var verify = (function () { function verify(act, exp, msg) { var result = equiv(act, exp); testPassed = testPassed && result; runner.publish('verify', currentTest, result, act, exp, msg); } verify.equal = verify; verify.notEqual = function (act, exp, msg) { var result = !equiv(act, exp); testPassed = testPassed && result; runner.publish('verify', currentTest, result, act, "not " + exp, msg); }; verify.strictEqual = function (act, exp, msg) { var result = act === exp; testPassed = testPassed && result; runner.publish('verify', currentTest, result, act, exp, msg); } verify.notStrictEqual = function (act, exp, msg) { var result = act !== exp; testPassed = testPassed && result; runner.publish('verify', currentTest, result, act, "not " + exp, msg); } verify.noException = function (callback, msg) { try { var res = callback(); verify.equal(res, res, msg); } catch (e) { verify.equal(res, "no exception", msg); } }; verify.exception = function (callback, exception, msg) { try { var res = callback(); verify.equal(res, "exception", msg); } catch (e) { verify.instanceOf(e, exception) } }; verify.defined = function (obj, msg) { return verify.notStrictEqual(obj, undefined, msg + " should be defined"); }; verify.notDefined = function (obj, msg) { return verify.strictEqual(obj, undefined, msg + " should not be defined"); }; verify.typeOf = function (obj, expected) { return verify.equal(typeof obj, expected, "typeof " + obj); }; verify.instanceOf = function (obj, expected) { return verify.equal(obj instanceof expected, true, "instanceof " + obj); }; return verify; })(); function fail(msg) { verify(true, false, msg); } function assert(condition, msg) { verify(condition, true, msg); } /////////////////////////////////////////////////////////////////////////////// // Below here is code to support the old test case format. These old test // cases call a series of ap* functions directly. The object below basically // proxies calls to ap* functions to the appropriate logger. Because the old // test case format doesn't instantiate a test case object, the code below // creates a stub to pass to the loggers. /////////////////////////////////////////////////////////////////////////////// function OldGlueProxy() { var test; var testStarted = false; var scenarioStarted = false; var scenarioPassed = true; function finishScenario() { if (scenarioPassed) runner.publish('pass', test); else runner.publish('fail', test); } this.apInitTest = function (name) { if (window.runsExecuted == 0) { // In MaxInterprentCount case do it only on first iteration if (testStarted) runner.publish('end'); testStarted = true; if (typeof Loader42_FileName == 'undefined') runner.publish('start', name); else runner.publish('start', Loader42_FileName); } } this.apInitScenario = function (name) { if (scenarioStarted) finishScenario(); var id = /^\d+/.exec(name); if (id === null) id = ""; test = { id: id, desc: name, test: Function() }; runner.publish('testStart', test); scenarioStarted = true; scenarioPassed = true; } this.apEndScenario = function () { finishScenario(); scenarioStarted = false; } this.apLogFailInfo = function (msg, exp, act) { runner.publish('verify', test, false, act, exp, msg); scenarioPassed = false; } this.apEndTest = function () { if (window.runsExecuted >= window.runsToExecute - 1) { // In MaxInterprentCount case do it only on last iteration if (scenarioStarted) finishScenario(); runner.publish('end'); testStarted = false; } } this.apWriteDebug = function (msg) { runner.publish('comment', msg); } this.VBS_apglobal_init = function () { try { window.apGlobalObj = createActiveXObject("apgloballib.apglobal"); } catch (e) { // Can't create ActiveX, so make a stub object with the same methods. window.apGlobalObj = { apInitTest: apInitTest, apInitScenario: apInitScenario, apLogFailInfo: apLogFailInfo, apEndScenario: apEndScenario, apEndTest: apEndTest, apGetLangExt: function (Lcid) { var LangExt = ""; switch (apGlobalObj.apPrimaryLang(Lcid)) { case 0x9: LangExt = "EN"; // LANG_ENGLISH break; case 0xC: LangExt = "FR"; // LANG_FRENCH break; case 0xA: LangExt = "ES"; // LANG_SPANISH break; case 0x7: LangExt = "DE"; // LANG_GERMAN break; case 0x10: LangExt = "IT"; // LANG_ITALIAN break; case 0x16: LangExt = "PT"; // LANG_PORTUGUESE break; case 0x1D: LangExt = "SV"; // LANG_SWEDISH break; case 0x14: LangExt = "NO"; // LANG_NORWEGIAN break; } if (LangExt == "") { //Hack for DBCS switch (Lcid) { case 0x411: LangExt = "JP"; // LANG_JAPANESE break; case 0x412: LangExt = "KO"; // LANG_KOREAN break; case 0x404: LangExt = "CHT"; // Chinese break; case 0x804: LangExt = "CHS"; // PRC break; } } return LangExt; }, apPrimaryLang: function (Lcid) { return Lcid & 0x3FF; }, apGetLocFormatDate: function (Lcid, Fmt) { var strToken = ""; switch (Fmt) // am not sure whenstr.toUpper will be supported.. { case "LONG DATE": case "long date": strToken = "datelong"; break; case "MEDIUM DATE": case "medium date": strToken = "datemed"; break; case "SHORT DATE": case "short date": strToken = "dateshort"; break; case "GENERAL DATE": case "general date": strToken = "gendate"; break; case "LONG TIME": case "long time": strToken = "timelong"; break; case "MEDIUM TIME": case "medium time": strToken = "timemed"; break; case "SHORT TIME": case "short time": strToken = "timeshort"; break; } return apGlobalObj.apGetToken(Lcid, "fmt_named_" + strToken, "OLB"); }, apGetToken: function (Lcid, strTokenName, strFileName) { // not needed right now.. var strToken = ""; return (strToken == "") ? "[[Token Not Found]]" : strToken; }, apGetLocInfo: function (Lcid, lctype, ProjectOverride, Flags) { }, apGetOSVer: function () { //not needed return "NT;"; }, apGetPathName: function () { //not needed apGlobalObj.apLogFailInfo("FAILURE: TC requested function apGetPathName", "", "", ""); apGlobalObj.apEndTest(); return ""; }, apGetPlatform: function () { //not needed return "NT;"; }, apGetVolumeName: function (OS) { //not needed return "NT;"; }, LangHost: function () { // return GetUserDefaultLCID(); return 1033; }, apGetLocalizedString: function (resID) { return "No localized string returned"; }, apGetLocalizedStringWithSubstitution: function (resID, stringSubstitution) { return "No localized string returned"; } } } window.apPlatform = apGlobalObj.apGetPlatform(); } this.apGetLocale = function () { return apGlobalObj.LangHost(); } } /////////////////////////////////////////////////////////////////////////////// // Object Comparison functions. /////////////////////////////////////////////////////////////////////////////// // Determine what is o. function hoozit(o) { if (typeof o === "undefined") { return "undefined"; } else if (o === null) { return "null"; } else if (o.constructor === String) { return "string"; } else if (o.constructor === Boolean) { return "boolean"; } else if (o.constructor === Number) { if (isNaN(o)) { return "nan"; } else { return "number"; } // consider: typeof [] === object } else if (o instanceof Array) { return "array"; // consider: typeof new Date() === object } else if (o instanceof Date) { return "date"; // consider: /./ instanceof Object; // /./ instanceof RegExp; // typeof /./ === "function"; // => false in IE and Opera, // true in FF and Safari } else if (o instanceof RegExp) { return "regexp"; } else if (typeof o === "object") { return "object"; } else if (o instanceof Function) { return "function"; } else { return undefined; } } // Call the o related callback with the given arguments. function bindCallbacks(o, callbacks, args) { var prop = hoozit(o); if (prop) { if (hoozit(callbacks[prop]) === "function") { return callbacks[prop].apply(callbacks, args); } else { return callbacks[prop]; // or undefined } } } // Test for equality any JavaScript type. // Discussions and reference: http://philrathe.com/articles/equiv // Test suites: http://philrathe.com/tests/equiv // Author: Philippe Rath? var equiv = function () { var innerEquiv; // the real equiv function var callers = []; // stack to decide between skip/abort functions var callbacks = function () { // for string, boolean, number and null function useStrictEquality(b, a) { if (b instanceof a.constructor || a instanceof b.constructor) { // to catch short annotaion VS 'new' annotation of a declaration // e.g. var i = 1; // var j = new Number(1); return a == b; } else { return a === b; } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function (b) { return isNaN(b); }, "date": function (b, a) { return hoozit(b) === "date" && a.valueOf() === b.valueOf(); }, "regexp": function (b, a) { return hoozit(b) === "regexp" && a.source === b.source && // the regex itself a.global === b.global && // and its modifers (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline; }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function () { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined"; }, "array": function (b, a) { var i; var len; // b could be an object literal here if (!(hoozit(b) === "array")) { return false; } len = a.length; if (len !== b.length) { // safe and faster return false; } for (i = 0; i < len; i++) { if (!innerEquiv(a[i], b[i])) { return false; } } return true; }, "object": function (b, a) { var i; var eq = true; // unless we can proove it var aProperties = [], bProperties = []; // collection of strings // comparing constructors is more strict than using instanceof if (a.constructor !== b.constructor) { return false; } // stack constructor before traversing properties callers.push(a.constructor); for (i in a) { // be strict: don't ensures hasOwnProperty and go deep aProperties.push(i); // collect a's properties if (!innerEquiv(a[i], b[i])) { eq = false; } } callers.pop(); // unstack, we are done for (i in b) { bProperties.push(i); // collect b's properties } // Ensures identical properties name return eq && innerEquiv(aProperties, bProperties); } }; } (); innerEquiv = function () { // can take multiple arguments var args = Array.prototype.slice.apply(arguments); if (args.length < 2) { return true; // end transition } return (function (a, b) { if (a === b) { return true; // catch the most you can } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) { return false; // don't lose time with error prone cases } else { return bindCallbacks(a, callbacks, [b, a]); } // apply transition with (1..n) arguments })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length - 1)); }; return innerEquiv; } (); /////////////////////////////////////////////////////////////////////////////// // // Assign globals to the global object. // These are underscore prefixed because they are called by functions of the same name outside the // framework closure. This is done because window.verify will not be overwritten by a // function verify() {} in global scope, which many old tests depend on. // /////////////////////////////////////////////////////////////////////////////// window._verify = verify; window._assert = assert; window._fail = fail; window.getLocalizedError = Utils.getLocalizedError; window.Run = runner.run; window.runner = runner; window.TestCase = TestCase; window.BaselineTestCase = BaselineTestCase; window.CrossContextTest = CrossContextTest; window.logger = windowLogger; // globals for versioning window.IE7STANDARDSMODE = Utils.IE7STANDARDSMODE; window.IE8STANDARDSMODE = Utils.IE8STANDARDSMODE; window.IE9STANDARDSMODE = Utils.IE9STANDARDSMODE; window.IE10STANDARDSMODE = Utils.IE10STANDARDSMODE; window.IE11STANDARDSMODE = Utils.IE11STANDARDSMODE; window.IE12STANDARDSMODE = Utils.IE12STANDARDSMODE; window.getHOSTMode = Utils.getHOSTMode; window.X86MODE = Utils.X86MODE; window.X64MODE = Utils.X64MODE; window.ARMMODE = Utils.ARMMODE; window.getIEArchitecture = Utils.getIEArchitecture; window.Utils = Utils; // Function returns true/false based on equality, same as verify, but excludes logging. Utils.equiv = equiv; // An object to hold various implementational details of our engine that our test cases rely upon window.Chakra = { // The amount of properties that have to be declared to transition object from path to // dictionary type. PathToDictionaryTransitionThreshold: 16 } // if this is called, set up globals required for old test cases. window.VBS_apglobal_init = function () { // Old test cases require conditional compilation in a few cases, so turn it on. //@cc_on var proxy = new OldGlueProxy(); window.apInitTest = proxy.apInitTest; window.apInitScenario = proxy.apInitScenario; window.apEndScenario = proxy.apEndScenario; window.apLogFailInfo = proxy.apLogFailInfo; window.apEndTest = proxy.apEndTest; window.apWriteDebug = proxy.apWriteDebug; window.VBS_apglobal_init = proxy.VBS_apglobal_init; window.apGetLocale = proxy.apGetLocale; proxy.VBS_apglobal_init(); } // It used to be defined via 'function VBS_apglobal_init()' // 'window.apGlobalObj' has to be defined if (typeof window.apGlobalObj == "undefined") { try { // Define the object window.apGlobalObj = createActiveXObject("apgloballib.apglobal"); } catch (e) { // Cannot instantiate ActiveXObject window.apGlobalObj = null; } } })(this); // Default version of these verification functions simply call the framework version of them. verify = _verify; assert = _assert; fail = _fail; // Disable simple print from doing anything VBS_apglobal_init(); function ScriptEngineMajorVersion() { return 5; } function ScriptEngineMinorVersion() { return 8; } apGlobalObj.osSetIntlValue = function() { return 0; } apGlobalObj.osGetIntlValue = function() { return 0; }