Comparing version 13.1.2 to 13.1.3
@@ -1,1 +0,1 @@ | ||
exports.TestCafe = require("./lib/server/test_cafe").TestCafe; | ||
exports.TestCafe = require('./lib/server/test_cafe').TestCafe; |
@@ -33,6 +33,6 @@ var fs = require("fs"), util = require("util"), javascriptParser = require("uglify-js").parser, astProcessor = require("uglify-js").uglify, url = require("url"), Const = require("./const"), Ast = require("./ast"), ErrCodes = require("./err_codes"); | ||
stepsInfo.forEach(function(info) { | ||
var stepFuncBodyAst = [], actionArgsAst = []; | ||
if (info.elemIdentifier && info.selector) { | ||
var stepFuncBodyAst = [], actionArgsAst = [], selector = info.selectors && info.selectors[info.currentSelectorIndex] && info.selectors[info.currentSelectorIndex].selector; | ||
if (info.elemIdentifier && selector) { | ||
try { | ||
var selectorAst = javascriptParser.parse(info.selector); | ||
var selectorAst = javascriptParser.parse(selector); | ||
} catch (parserErr) { | ||
@@ -39,0 +39,0 @@ throw { |
var fs = require("fs"), path = require("path"), javascriptParser = require("uglify-js").parser, astProcessor = require("uglify-js").uglify, async = require("async"), Const = require("./const"), Ast = require("./ast"), FuncCallValidator = require("./func_call_validator"), ErrCodes = require("./err_codes"); | ||
var Compiler = exports.Compiler = function(filename, modules, requiresCodeCache) { | ||
var Compiler = module.exports = function(filename, modules, requiresCodeCache) { | ||
requiresCodeCache = requiresCodeCache || {}; | ||
@@ -5,0 +5,0 @@ this.walker = astProcessor.ast_walker(); |
@@ -1,2 +0,2 @@ | ||
exports.Compiler = require("./compiler").Compiler; | ||
exports.Compiler = require("./compiler"); | ||
@@ -3,0 +3,0 @@ exports.CodeGenerator = require("./code_generator"); |
@@ -160,3 +160,3 @@ var util = require("util"), EventEmitter = require("events").EventEmitter, vfs = require("./vfs"), VirtualFS = vfs.VirtualFS, errMsgBuilder = require("./err_msg_builder"), ERR = require("./server_errs"); | ||
vfsPath = vfs.getPlatformDependentPath(vfsPath); | ||
vfsPath = vfsPath.split(VirtualFS.PLATFORM_PATH_SEPARATOR); | ||
vfsPath = vfsPath.split(path.sep); | ||
} | ||
@@ -169,3 +169,3 @@ this.vfs.getPathInfo(vfsPath, function(err, pathInfo) { | ||
Api.prototype.runTests = function(options, callback) { | ||
var source = options.source || null, sourceType = options.sourceType || "dir", workers = options.workers || [], browsers = options.browsers || [], emulateCursor = options.emulateCursor || false; | ||
var source = options.source || null, sourceType = options.sourceType || "dir", workers = options.workers || [], browsers = options.browsers || [], emulateCursor = options.emulateCursor || false, quarantineMode = options.quarantineMode || false; | ||
var api = this, testRunner = this.testRunner, callbackExists = typeof callback === "function"; | ||
@@ -188,3 +188,3 @@ if (!workers.length && !browsers.length) { | ||
if (actualWorkers.length) { | ||
taskUid = testRunner.addTask(taskName, suite, testUids, actualWorkers, emulateCursor); | ||
taskUid = testRunner.addTask(taskName, suite, testUids, actualWorkers, emulateCursor, quarantineMode); | ||
callbackExists ? callback(errs, taskUid, actualWorkers) : true; | ||
@@ -191,0 +191,0 @@ } else { |
@@ -100,2 +100,6 @@ var path = require("path"), util = require("util"), http = require("http"), EventEmitter = require("events").EventEmitter, express = require("express"), io = require("socket.io"), ejs = require("ejs"), WorkerPool = require("./worker_pool").WorkerPool, errMsgBuilder = require("./err_msg_builder"), ERR = require("./server_errs"); | ||
POST("/delete_test", this._deleteTest); | ||
POST("/dirs_by_path/", this._getDirsByPath); | ||
POST("/dirs_by_path", this._getDirsByPath); | ||
POST("/set_tests_dir/", this._setTestsDir); | ||
POST("/set_tests_dir", this._setTestsDir); | ||
POST("/recording/start/", this._record); | ||
@@ -185,5 +189,7 @@ POST("/recording/start", this._record); | ||
} | ||
var testsDir = vfs.getBasePath(); | ||
if (slide) { | ||
res.render("partial/tests_browser", { | ||
testsBrowser: testsBrowser | ||
testsBrowser: testsBrowser, | ||
testsDir: testsDir | ||
}); | ||
@@ -194,2 +200,3 @@ } else { | ||
testsBrowser: testsBrowser, | ||
testsDir: testsDir, | ||
errMsgs: errs.length ? errs : null | ||
@@ -200,2 +207,3 @@ }); | ||
testsBrowser: testsBrowser, | ||
testsDir: testsDir, | ||
expandRowFilename: expandRowFilename, | ||
@@ -486,2 +494,35 @@ errMsgs: errs.length ? errs : null, | ||
ControlPanel.prototype._getDirsByPath = function(req, res) { | ||
var fsPath = req.param("path"); | ||
if (fsPath) { | ||
this.vfs.getDirsByPath(decodeURIComponent(fsPath), function(err, dirs) { | ||
res.status(200); | ||
res.send(dirs); | ||
}); | ||
} else { | ||
this.vfs.getLocalDrives(function(err, devicesList) { | ||
if (!err) { | ||
res.status(200); | ||
res.send(devicesList); | ||
} else { | ||
res.status(304); | ||
res.send(errMsgBuilder.build(err)); | ||
} | ||
}); | ||
} | ||
}; | ||
ControlPanel.prototype._setTestsDir = function(req, res) { | ||
var fsPath = decodeURIComponent(req.param("testsDir")); | ||
this.vfs.setTestsDir(fsPath, function(err) { | ||
if (!err) { | ||
res.status(200); | ||
res.end(); | ||
} else { | ||
res.status(403); | ||
res.send(errMsgBuilder.build(err)); | ||
} | ||
}); | ||
}; | ||
ControlPanel.prototype._page404Handler = function(req, res) { | ||
@@ -488,0 +529,0 @@ res.status(404); |
@@ -187,3 +187,3 @@ var url = require("url"), util = require("util"), FixtureCode = require("../fixture_code"), ERR = require("./server_errs"), CLIENT_ERR = require("./../shared/client_errs"); | ||
case ERR.INJECTOR_DOCUMENT_PARSING_FAILED: | ||
m("Failed to parse test page HTML-markup. %s", err.msgs && err.msgs.join(" ")); | ||
m("Failed to parse test page HTML-markup. %s", err.msg && err.msg.toString()); | ||
break; | ||
@@ -319,2 +319,10 @@ | ||
case ERR.VIRTUAL_FILE_SYSTEM_DIR_DOESNT_READ: | ||
m('The "%s" directory can\'t be read. It might have been deleted.', err.fsPath); | ||
break; | ||
case ERR.VIRTUAL_FILE_SYSTEM_NODEJS_CANT_GET_DRIVE_LIST: | ||
m("It is impossible to get the list of local directories."); | ||
break; | ||
case ERR.WORKER_POOL_WORKER_NAME_IS_EMPTY: | ||
@@ -434,7 +442,7 @@ m("Worker name is empty. The name should contain at least one character which cannot be a blank space."); | ||
case CLIENT_ERR.API_INCORRECT_SELECT_ACTION_ARGUMENTS: | ||
m('Error on step "%s": select action parameters contains incorrect value.', err.stepName); | ||
m('Error on step "%s": select action\'s parameters contain an incorrect value.', err.stepName); | ||
break; | ||
case CLIENT_ERR.API_INCORRECT_WAIT_ACTION_FIRST_ARGUMENT: | ||
m('Error on step "%s": wait action first parameter contains incorrect value.', err.stepName); | ||
m('Error on step "%s": wait action\'s first parameter contains an incorrect value.', err.stepName); | ||
break; | ||
@@ -441,0 +449,0 @@ } |
@@ -1,2 +0,2 @@ | ||
var zlib = require("zlib"), whacko = require("whacko"), iconv = require("iconv-lite"), sharedConst = require("./../shared/const"), urlUtil = require("./../shared/url_util"), pageProc = require("./../shared/page_processor"), ERR = require("./server_errs"); | ||
var zlib = require("zlib"), util = require("util"), whacko = require("whacko"), iconv = require("iconv-lite"), sharedConst = require("./../shared/const"), urlUtil = require("./../shared/url_util"), pageProc = require("./../shared/page_processor"), ERR = require("./server_errs"); | ||
@@ -33,3 +33,3 @@ var ZLIB_INFLATE_INVALID_ENCODING_ERR_CODE = "Z_DATA_ERROR", CHARSET_LIST = [ "iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-9", "iso-8859-10", "iso-8859-11", "iso-8859-12", "iso-8859-13", "iso-8859-14", "iso-8859-15", "iso-8859-16", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256", "windows-1257", "windows-1258", "windows-874", "windows-866", "koi8-r", "koi8-u", "utf-8", "utf-16", "utf-32", "shift-jis", "x-euc", "big5", "euc-kr" ]; | ||
var encodedData = concatChunks(chunks), decodingCallback = function(err, decodingRes) { | ||
if (err || !(decodingRes && decodingRes.length)) { | ||
if (err && !decodingRes) { | ||
callback({ | ||
@@ -96,6 +96,2 @@ code: ERR.INJECTOR_RESOURCE_DECODING_FAILED, | ||
} | ||
if (!rawData) { | ||
callback(null, ""); | ||
return; | ||
} | ||
try { | ||
@@ -148,3 +144,3 @@ var processedData = processor(rawData, charset); | ||
code: ERR.INJECTOR_DOCUMENT_PARSING_FAILED, | ||
msgs: parseErrs | ||
msg: parseErrs | ||
}; | ||
@@ -163,18 +159,17 @@ } | ||
var injection = []; | ||
if (injectionOptions.startupScript) { | ||
if (injectionOptions.styleUrl) { | ||
injection.push('<link rel="stylesheet" type="text/css" class="'); | ||
injection.push(sharedConst.TEST_CAFE_UI_STYLESHEET_CLASSNAME); | ||
injection.push('"href = "'); | ||
injection.push(injectionOptions.styleUrl); | ||
injection.push('">'); | ||
} | ||
if (injectionOptions.scriptUrl) { | ||
injection.push('<script type="text/javascript" class="'); | ||
injection.push(sharedConst.TEST_CAFE_SCRIPT_CLASSNAME); | ||
injection.push('" charset="UTF-8" src="'); | ||
injection.push(injectionOptions.scriptUrl); | ||
injection.push('">'); | ||
injection.push("//<![CDATA[\n"); | ||
injection.push(injectionOptions.startupScript); | ||
injection.push("//]]>\n"); | ||
injection.push("</script>"); | ||
} | ||
if (injectionOptions.uiCss) { | ||
injection.push('<style type="text/css" class="'); | ||
injection.push(sharedConst.TEST_CAFE_UI_STYLESHEET_CLASSNAME); | ||
injection.push('">'); | ||
injection.push(injectionOptions.uiCss); | ||
injection.push("</style>"); | ||
} | ||
if (injection.length) { | ||
@@ -181,0 +176,0 @@ $("head").prepend(injection.join("")); |
@@ -1,19 +0,20 @@ | ||
var fs = require("fs"), path = require("path"), url = require("url"), util = require("util"), uuid = require("node-uuid"), assets = require("./assets"), cmd = require("../shared/service_msg_cmd"), ERR = require("./server_errs"), errMsgBuilder = require("./err_msg_builder"), FixtureCode = require("../fixture_code/"), injector = require("./injector"), urlUtil = require("../shared/url_util"); | ||
var fs = require("fs"), path = require("path"), url = require("url"), util = require("util"), uuid = require("node-uuid"), cmd = require("../shared/service_msg_cmd"), ERR = require("./server_errs"), errMsgBuilder = require("./err_msg_builder"), FixtureCode = require("../fixture_code/"), injector = require("./injector"), urlUtil = require("../shared/url_util"); | ||
var RECORDER_PAGE_HTML_TEMPLATE = "<!DOCTYPE html>" + "<html>" + "<head>" + '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' + "<title></title>" + '<style type="text/css">%s</style> ' + '<script type="text/javascript">%s</script>' + "</head>" + "<body></body>" + "</html>"; | ||
var RECORDER_PAGE_HTML_TEMPLATE = "<!DOCTYPE html>" + "<html>" + "<head>" + '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' + "<title></title>" + '<link href="%s" rel="stylesheet" type="text/css">' + '<script type="text/javascript">%s</script> ' + "</head>" + "<body></body>" + "</html>"; | ||
var Recorder = exports.Recorder = function(proxy, vfs, cookieShelf) { | ||
var Recorder = exports.Recorder = function(proxy, vfs, cookieShelf, assetsManager) { | ||
this.proxy = proxy; | ||
this.vfs = vfs; | ||
this.cookieShelf = cookieShelf; | ||
this.assetsManager = assetsManager; | ||
this.silentMode = false; | ||
this.disableTooltip = false; | ||
this.recordings = {}; | ||
this.recordingJsTmpl = new assets.JsTemplate(path.join(__dirname, "../_compiled_/client_runtime/recording.jstmpl")); | ||
this.serverErrorPageJsTmpl = new assets.JsTemplate(path.join(__dirname, "../_compiled_/client_runtime/recorder_server_err_page.jstmpl")); | ||
this.authenticationPageJsTmpl = new assets.JsTemplate(path.join(__dirname, "../_compiled_/client_runtime/authentication_page.jstmpl")); | ||
this.proxy.addEventListener(Recorder.JOB_OWNER_TOKEN, this); | ||
this.proxy.addOwnerDependentEventListener("beforeProxyResponse", Recorder.JOB_OWNER_TOKEN, this._onBeforeProxyResponse.bind(this)); | ||
this.proxy.addOwnerDependentEventListener("error", Recorder.JOB_OWNER_TOKEN, this._onProxyError.bind(this)); | ||
this.proxy.addOwnerDependentEventListener("serviceMsg", Recorder.JOB_OWNER_TOKEN, this._onServiceMsg.bind(this)); | ||
var proxyEvents = this.proxy.events.for(Recorder.JOB_OWNER_TOKEN); | ||
proxyEvents.listen("authCredentialsRequested", this._onAuthCredentialsRequested.bind(this)); | ||
proxyEvents.listen("beforeProxyResponse", this._onBeforeProxyResponse.bind(this)); | ||
proxyEvents.listen("error", this._onProxyError.bind(this)); | ||
var serviceChannelEvents = this.proxy.serviceChannel.events.for(Recorder.JOB_OWNER_TOKEN); | ||
serviceChannelEvents.listen("serviceMsg", this._onServiceMsg.bind(this)); | ||
serviceChannelEvents.listen("testCafeScriptRequested", this._onTestCafeScriptRequested.bind(this)); | ||
}; | ||
@@ -23,29 +24,7 @@ | ||
Recorder.prototype._redirectToServerErrorPage = function(proxyCtx, returnUrl) { | ||
var parsedProxyUrl = urlUtil.parseProxyUrl(proxyCtx.req.url), serverErrorPageJs = this.serverErrorPageJsTmpl.getJs({ | ||
returnUrl: returnUrl, | ||
errorCode: proxyCtx.originRes ? proxyCtx.originRes.statusCode : 500, | ||
originUrl: parsedProxyUrl ? url.format(parsedProxyUrl.originResourceInfo) : "" | ||
}); | ||
proxyCtx.res.statusCode = 500; | ||
proxyCtx.res.setHeader("content-type", "text/html"); | ||
proxyCtx.res.end(util.format(RECORDER_PAGE_HTML_TEMPLATE, assets.getClientUICss(), serverErrorPageJs)); | ||
Recorder.prototype._onAuthCredentialsRequested = function(proxyCtx, callback) { | ||
var recording = this.recordings[proxyCtx.jobInfo.uid]; | ||
callback(recording ? recording.authCredentials : null); | ||
}; | ||
Recorder.prototype._redirectToAuthenticationPage = function(proxyCtx, returnUrl, currentCredentials) { | ||
var parsedProxyUrl = urlUtil.parseProxyUrl(proxyCtx.req.url), authenticationPageJs = this.authenticationPageJsTmpl.getJs({ | ||
credentials: currentCredentials ? currentCredentials.username + ":" + currentCredentials.password : "", | ||
returnUrl: returnUrl, | ||
originUrl: parsedProxyUrl ? url.format(parsedProxyUrl.originResourceInfo) : "" | ||
}); | ||
proxyCtx.res.statusCode = 401; | ||
proxyCtx.res.setHeader("content-type", "text/html"); | ||
proxyCtx.res.end(util.format(RECORDER_PAGE_HTML_TEMPLATE, assets.getClientUICss(), authenticationPageJs)); | ||
}; | ||
Recorder.prototype.getAuthCredentials = function(jobUID) { | ||
var recording = this.recordings[jobUID]; | ||
return recording ? recording.authCredentials : null; | ||
}; | ||
Recorder.prototype._onBeforeProxyResponse = function(ctx, callback) { | ||
@@ -59,3 +38,3 @@ var recording = this.recordings[ctx.jobInfo.uid]; | ||
if (ctx.originRes.statusCode === 401 && ctx.originResContentInfo.isPage) { | ||
this._redirectToAuthenticationPage(ctx, recording.returnUrl, recording.authCredentials); | ||
this._redirectToAuthenticationPage(ctx, recording); | ||
return; | ||
@@ -81,4 +60,4 @@ } | ||
Recorder.prototype._onServiceMsg = function(msg, proxyCtx, callback) { | ||
var recording = this.recordings[proxyCtx.jobInfo.uid], res = null; | ||
Recorder.prototype._onServiceMsg = function(msg, callback) { | ||
var recording = this.recordings[msg.jobUid], res = null; | ||
if (recording) { | ||
@@ -94,6 +73,2 @@ switch (msg.cmd) { | ||
case cmd.SILENT_MODE_GET: | ||
res = this.silentMode; | ||
break; | ||
case cmd.SILENT_MODE_SET: | ||
@@ -103,6 +78,2 @@ this.silentMode = msg.silentMode; | ||
case cmd.TOOLBAR_POSITION_GET: | ||
res = recording.toolbarPosition; | ||
break; | ||
case cmd.TOOLBAR_POSITION_SET: | ||
@@ -112,6 +83,2 @@ recording.toolbarPosition = msg.toolbarPosition; | ||
case cmd.DISABLE_TOOLTIP_GET: | ||
res = this.disableTooltip; | ||
break; | ||
case cmd.DISABLE_TOOLTIP_SET: | ||
@@ -134,3 +101,3 @@ this.disableTooltip = msg.disableTooltip; | ||
case cmd.ADD_TEST_CODE: | ||
this.completeRecording(proxyCtx.jobInfo.uid, msg.testName, function(res) { | ||
this.completeRecording(msg.jobUid, msg.testName, function(res) { | ||
var err = res.err, fixtureFilename = res.fixtureFileName; | ||
@@ -147,4 +114,5 @@ callback({ | ||
vfs.getRealPath(recording.fixturePath, recording.fixtureFileName, function(fileName) { | ||
var username = msg.username, password = msg.password; | ||
FixtureCode.CodeGenerator.editDirectives(fileName, { | ||
auth: msg.credentials | ||
auth: username + ":" + password | ||
}, function(err) { | ||
@@ -155,2 +123,6 @@ if (err) { | ||
} | ||
recording.authCredentials = { | ||
username: username, | ||
password: password | ||
}; | ||
vfs.forceBuild(callback); | ||
@@ -168,31 +140,68 @@ }); | ||
Recorder.prototype._injectRecording = function(proxyCtx, recording, callback) { | ||
var recorder = this; | ||
this.cookieShelf.getClientCookieString(proxyCtx.jobInfo, proxyCtx.originResourceInfo.url, function(cookie) { | ||
var opt = { | ||
startupScript: recorder.recordingJsTmpl.getJs({ | ||
returnUrl: recording.returnUrl, | ||
jobUid: recording.uid, | ||
jobOwnerToken: Recorder.JOB_OWNER_TOKEN, | ||
cookie: cookie.replace(/'/g, "\\'"), | ||
originHost: proxyCtx.originResourceInfo.host, | ||
originProtocol: proxyCtx.originResourceInfo.protocol, | ||
originHostname: proxyCtx.originResourceInfo.hostname, | ||
originPort: proxyCtx.originResourceInfo.port || "" | ||
}), | ||
urlReplacer: recorder.proxy.getResourceUrlReplacer(proxyCtx), | ||
uiCss: assets.getClientUICss() | ||
}; | ||
injector.injectInPage(proxyCtx.originResBodyChunks, proxyCtx.originResContentInfo.encoding, proxyCtx.originResContentInfo.charset, opt, function(err, html) { | ||
if (err) { | ||
proxyCtx.originRes.statusCode = 500; | ||
recorder._redirectToServerErrorPage(proxyCtx, recording.returnUrl); | ||
} else { | ||
proxyCtx.originResBodyChunks = [ html ]; | ||
callback(); | ||
} | ||
Recorder.prototype._onTestCafeScriptRequested = function(refererInfo, callback) { | ||
var recorder = this, recording = this.recordings[refererInfo.jobInfo.uid]; | ||
this.cookieShelf.getClientCookieString(refererInfo.jobInfo, refererInfo.originResourceInfo.url, function(cookie) { | ||
var recordingScript = recorder.assetsManager.compileTemplate(recorder.assetsManager.TEMPLATES.RECORDING, { | ||
returnUrl: recording.returnUrl, | ||
jobUid: recording.uid, | ||
jobOwnerToken: Recorder.JOB_OWNER_TOKEN, | ||
serviceMsgUrl: recorder.proxy.serviceChannel.serviceMsgUrl, | ||
cookie: cookie.replace(/'/g, "\\'"), | ||
originHost: refererInfo.originResourceInfo.host, | ||
originProtocol: refererInfo.originResourceInfo.protocol, | ||
originHostname: refererInfo.originResourceInfo.hostname, | ||
originPort: refererInfo.originResourceInfo.port || "", | ||
silentMode: recorder.silentMode, | ||
disableTooltip: recorder.disableTooltip, | ||
toolbarPosLeft: recording.toolbarPosition.left, | ||
toolbarPosTop: recording.toolbarPosition.top | ||
}); | ||
callback(recordingScript); | ||
}); | ||
}; | ||
Recorder.prototype._redirectToServerErrorPage = function(proxyCtx, returnUrl) { | ||
var parsedProxyUrl = urlUtil.parseProxyUrl(proxyCtx.req.url), errorPageScript = this.assetsManager.compileTemplate(this.assetsManager.TEMPLATES.RECORDING_ERR_PAGE, { | ||
returnUrl: returnUrl, | ||
errorCode: proxyCtx.originRes ? proxyCtx.originRes.statusCode : 500, | ||
originUrl: parsedProxyUrl ? url.format(parsedProxyUrl.originResourceInfo) : "" | ||
}); | ||
proxyCtx.res.statusCode = 500; | ||
proxyCtx.res.setHeader("content-type", "text/html"); | ||
proxyCtx.res.end(util.format(RECORDER_PAGE_HTML_TEMPLATE, this.assetsManager.uiStyleUrl, errorPageScript)); | ||
}; | ||
Recorder.prototype._redirectToAuthenticationPage = function(proxyCtx, recording) { | ||
var parsedProxyUrl = urlUtil.parseProxyUrl(proxyCtx.req.url), authenticationPageScript = this.assetsManager.compileTemplate(this.assetsManager.TEMPLATES.RECORDING_AUTHENTICATION_PAGE, { | ||
jobUid: recording.uid, | ||
jobOwnerToken: Recorder.JOB_OWNER_TOKEN, | ||
serviceMsgUrl: this.proxy.serviceChannel.serviceMsgUrl, | ||
credentials: recording.authCredentials ? recording.authCredentials.username + ":" + recording.authCredentials.password : "", | ||
returnUrl: recording.returnUrl, | ||
targetUrl: proxyCtx.req.url, | ||
originUrl: parsedProxyUrl ? url.format(parsedProxyUrl.originResourceInfo) : "" | ||
}); | ||
var html = util.format(RECORDER_PAGE_HTML_TEMPLATE, this.assetsManager.uiStyleUrl, authenticationPageScript); | ||
proxyCtx.res.statusCode = 401; | ||
proxyCtx.res.setHeader("content-type", "text/html"); | ||
proxyCtx.res.end(html); | ||
}; | ||
Recorder.prototype._injectRecording = function(proxyCtx, recording, callback) { | ||
var recorder = this, opt = { | ||
urlReplacer: this.proxy.getResourceUrlReplacer(proxyCtx), | ||
styleUrl: this.assetsManager.uiStyleUrl, | ||
scriptUrl: this.proxy.serviceChannel.testCafeScriptUrl | ||
}; | ||
injector.injectInPage(proxyCtx.originResBodyChunks, proxyCtx.originResContentInfo.encoding, proxyCtx.originResContentInfo.charset, opt, function(err, html) { | ||
if (err) { | ||
proxyCtx.originRes.statusCode = 500; | ||
recorder._redirectToServerErrorPage(proxyCtx, recording.returnUrl); | ||
} else { | ||
proxyCtx.originResBodyChunks = [ html ]; | ||
callback(); | ||
} | ||
}); | ||
}; | ||
Recorder.prototype.startRecording = function(fixtureUid, returnUrl, callback) { | ||
@@ -215,3 +224,7 @@ var recorder = this; | ||
stepsInfo: [], | ||
uid: recordingUid | ||
uid: recordingUid, | ||
toolbarPosition: { | ||
left: null, | ||
top: null | ||
} | ||
}; | ||
@@ -218,0 +231,0 @@ try { |
@@ -26,3 +26,3 @@ var url = require("url"), util = require("util"), errMsgBuilder = require("./err_msg_builder"), moment = require("moment"), EventEmitter = require("events").EventEmitter; | ||
Reporter.prototype.taskAdded = function(taskUid, name, testCount, workerNames) { | ||
Reporter.prototype.taskAdded = function(taskUid, name, testCount, workerNames, reportUnstable) { | ||
var taskReport = { | ||
@@ -38,5 +38,8 @@ uid: taskUid, | ||
passed: 0, | ||
testErrReports: {}, | ||
completeTestRunCounters: {} | ||
testErrReports: {} | ||
}; | ||
if (reportUnstable) { | ||
taskReport.unstableTests = {}; | ||
} | ||
this.completedRunsPerTest = {}; | ||
this.taskReports.push(taskReport); | ||
@@ -80,9 +83,23 @@ this.emit("taskUpdated", { | ||
} | ||
if (taskReport.unstableTests && testRun.quarantine.failed && testRun.quarantine.succeeded) { | ||
if (!taskReport.unstableTests[testUid]) { | ||
taskReport.unstableTests[testUid] = { | ||
name: testRun.test.name, | ||
fixtureName: testRun.fixture.name, | ||
fixturePath: testRun.fixture.path.join("/"), | ||
workerNames: [] | ||
}; | ||
} | ||
taskReport.unstableTests[testUid].workerNames.push(testRun.workerName); | ||
} | ||
if (taskReport.testErrReports[testUid]) { | ||
return; | ||
} | ||
taskReport.completeTestRunCounters[testUid] = taskReport.completeTestRunCounters[testUid] || 0; | ||
if (++taskReport.completeTestRunCounters[testUid] === taskReport.workerNames.length) { | ||
if (!this.completedRunsPerTest[testRun.taskUid]) { | ||
this.completedRunsPerTest[testRun.taskUid] = {}; | ||
} | ||
this.completedRunsPerTest[testRun.taskUid][testUid] = this.completedRunsPerTest[testRun.taskUid][testUid] || 0; | ||
if (++this.completedRunsPerTest[testRun.taskUid][testUid] === taskReport.workerNames.length) { | ||
taskReport.passed++; | ||
if (Object.keys(taskReport.completeTestRunCounters).length < taskReport.testCount) { | ||
if (taskReport.passed + taskReport.failed < taskReport.testCount) { | ||
this.emit("taskUpdated", { | ||
@@ -89,0 +106,0 @@ id: testRun.taskUid |
@@ -101,2 +101,6 @@ exports.API_TESTS_TARGET_NOT_EXIST = "API_TESTS_TARGET_NOT_EXIST"; | ||
exports.VIRTUAL_FILE_SYSTEM_DIR_DOESNT_READ = "VIRTUAL_FILE_SYSTEM_DIR_DOESNT_READ"; | ||
exports.VIRTUAL_FILE_SYSTEM_NODEJS_CANT_GET_DRIVE_LIST = "VIRTUAL_FILE_SYSTEM_NODEJS_CANT_GET_DRIVE_LIST"; | ||
exports.WORKER_POOL_FAILED_TO_START_BROWSER = "WORKER_POOL_FAILED_TO_START_BROWSER"; | ||
@@ -103,0 +107,0 @@ |
@@ -1,2 +0,2 @@ | ||
var fs = require("fs"), util = require("util"), os = require("os"), path = require("path"), EventEmitter = require("events").EventEmitter, moment = require("moment"), errMsgBuilder = require("./err_msg_builder"), Reporter = require("./reporter").Reporter, Proxy = require("./proxy").Proxy, CookieShelf = require("./cookie_shelf").CookieShelf, TestRunner = require("./test_runner").TestRunner, Recorder = require("./recorder").Recorder, VirtualFS = require("./vfs").VirtualFS, WorkerPool = require("./worker_pool").WorkerPool, Api = require("./api").Api, Config = require("./config").Config, ControlPanel = require("./control_panel").ControlPanel; | ||
var EventEmitter = require("events").EventEmitter, exec = require("child_process").exec, fs = require("fs"), moment = require("moment"), os = require("os"), path = require("path"), util = require("util"), Api = require("./api").Api, AssetsManager = require("./assets_manager"), Config = require("./../config").Config, ControlPanel = require("./control_panel").ControlPanel, CookieShelf = require("./cookie_shelf").CookieShelf, errMsgBuilder = require("./err_msg_builder"), Proxy = require("../proxy/"), Recorder = require("./recorder").Recorder, Reporter = require("./reporter").Reporter, TestRunner = require("./test_runner").TestRunner, VirtualFS = require("./vfs").VirtualFS, WorkerPool = require("./worker_pool").WorkerPool; | ||
@@ -12,2 +12,3 @@ var GREETING = "DevExpress TestCafe - functional testing for the web.\n" + "Copyright (C) 2012 - %s Developer Express Inc.\n" + "-----------------------------------------------\n", CONTROL_PANEL_URL_INFO_PATTERN = "Control Panel URL - http://%s\n\n", SHUTDOWN_MSG_PATTERN = "%s - Initialization Error:\n%s\n"; | ||
this.workerPool = null; | ||
this.assetsManager = null; | ||
this._init(startupCfg, standalone); | ||
@@ -49,36 +50,57 @@ }; | ||
TestCafe.prototype._init = function(startupCfg, standalone) { | ||
var testCafe = this; | ||
if (standalone) { | ||
util.print(util.format(GREETING, new Date().getFullYear())); | ||
} | ||
try { | ||
var config = new Config(startupCfg); | ||
} catch (err) { | ||
var messages = errMsgBuilder.build(util.isArray(err) ? err : [ err ]); | ||
this._fatalError(messages.join("\n"), standalone); | ||
} | ||
var reporter = new Reporter(), cookieShelf = new CookieShelf(); | ||
this.proxy = new Proxy(config.servicePort, config.hostname, cookieShelf); | ||
var testCafe = this; | ||
this.proxy.on("fatalError", function(err) { | ||
testCafe._fatalError(errMsgBuilder.build(err), standalone); | ||
testCafe.config = new Config(); | ||
testCafe._initConfig(startupCfg, function(config, err) { | ||
if (err) { | ||
var messages = errMsgBuilder.build(util.isArray(err) ? err : [ err ]); | ||
testCafe._fatalError(messages.join("\n"), standalone); | ||
} | ||
var reporter = new Reporter(), cookieShelf = new CookieShelf(); | ||
testCafe.proxy = new Proxy(config.servicePort, config.hostname, cookieShelf); | ||
testCafe.proxy.events.broadcast.listen("fatalError", function(err) { | ||
testCafe._fatalError(errMsgBuilder.build(err), standalone); | ||
}); | ||
testCafe.assetsManager = new AssetsManager(testCafe.proxy.serviceChannel); | ||
try { | ||
testCafe.vfs = new VirtualFS(config.testsDir, testCafe.config); | ||
} catch (err) { | ||
testCafe._fatalError(errMsgBuilder.build(err), standalone); | ||
} | ||
testCafe.workerPool = new WorkerPool(config); | ||
testCafe.testRunner = new TestRunner(testCafe.proxy, reporter, testCafe.workerPool, cookieShelf, testCafe.assetsManager, config.controlPanelHost); | ||
var recorder = new Recorder(testCafe.proxy, testCafe.vfs, cookieShelf, testCafe.assetsManager); | ||
Api.call(testCafe, testCafe.testRunner, testCafe.workerPool, reporter, testCafe.vfs, config, standalone); | ||
testCafe.controlPanel = new ControlPanel(config.controlPanelPort, testCafe.testRunner, testCafe.vfs, testCafe.workerPool, reporter, testCafe, recorder); | ||
testCafe.controlPanel.on("fatalError", function(err) { | ||
testCafe._fatalError(errMsgBuilder.build(err), standalone); | ||
}); | ||
testCafe.proxy.start(); | ||
testCafe.controlPanel.start(); | ||
if (standalone) { | ||
var controlPanelHost = config.hostname === "127.0.0.1" ? "localhost:" + config.controlPanelPort : config.controlPanelHost; | ||
util.print(util.format(CONTROL_PANEL_URL_INFO_PATTERN, controlPanelHost)); | ||
var controlPanelUrl = "http://" + controlPanelHost.replace(/"/, '\\"'), platform = os.platform(); | ||
if (platform.match(/^win/)) { | ||
exec('start "" "' + controlPanelUrl + '"'); | ||
} else { | ||
if (platform === "darwin") { | ||
exec("open " + controlPanelUrl); | ||
} else { | ||
exec("xdg-open " + controlPanelUrl); | ||
} | ||
} | ||
} | ||
}); | ||
try { | ||
this.vfs = new VirtualFS(config.testsDir); | ||
} catch (err) { | ||
this._fatalError(errMsgBuilder.build(err), standalone); | ||
}; | ||
TestCafe.prototype._initConfig = function(startupCfg, callback) { | ||
if (startupCfg) { | ||
var result = this.config.initFromObj(startupCfg); | ||
callback(result.cfg, result.errs); | ||
} else { | ||
this.config.initFromFile(callback); | ||
} | ||
this.workerPool = new WorkerPool(config); | ||
this.testRunner = new TestRunner(this.proxy, reporter, this.workerPool, cookieShelf, config.controlPanelHost); | ||
var recorder = new Recorder(this.proxy, this.vfs, cookieShelf); | ||
Api.call(this, this.testRunner, this.workerPool, reporter, this.vfs, config, standalone); | ||
this.controlPanel = new ControlPanel(config.controlPanelPort, this.testRunner, this.vfs, this.workerPool, reporter, this, recorder); | ||
this.controlPanel.on("fatalError", function(err) { | ||
testCafe._fatalError(errMsgBuilder.build(err), standalone); | ||
}); | ||
this.proxy.start(); | ||
this.controlPanel.start(); | ||
if (standalone) { | ||
var controlPanelHost = config.hostname === "127.0.0.1" ? "localhost:" + config.controlPanelPort : config.controlPanelHost; | ||
util.print(util.format(CONTROL_PANEL_URL_INFO_PATTERN, controlPanelHost)); | ||
} | ||
}; |
@@ -1,4 +0,4 @@ | ||
var EventEmitter = require("events").EventEmitter, path = require("path"), url = require("url"), util = require("util"), uuid = require("node-uuid"), assets = require("./assets"), CLIENT_ERR = require("../shared/client_errs"), cmd = require("../shared/service_msg_cmd"), ERR = require("./server_errs"), injector = require("./injector"); | ||
var EventEmitter = require("events").EventEmitter, path = require("path"), url = require("url"), util = require("util"), uuid = require("node-uuid"), CLIENT_ERR = require("../shared/client_errs"), cmd = require("../shared/service_msg_cmd"), ERR = require("./server_errs"), injector = require("./injector"); | ||
var PAGE_ERROR_VERIFICATION_HTML_TEMPLATE = "/*" + "<!DOCTYPE html>" + "<html>" + "<head>" + "<title></title>" + '<script type="text/javascript">%s</script>' + "</head>" + "<body></body>" + "</html>" + "*/"; | ||
var PAGE_ERROR_VERIFICATION_HTML_TEMPLATE = "/*" + "<!DOCTYPE html>" + "<html>" + "<head>" + "<title></title>" + '<script type="text/javascript">%s</script>' + "</head>" + "<body></body>" + "</html>" + "*/", QUARANTINE_TEST_RUN_COUNT = 3; | ||
@@ -15,3 +15,3 @@ function proxyErrRes(proxyCtx, resBody) { | ||
var TestRunner = exports.TestRunner = function(proxy, reporter, workerPool, cookieShelf, controlPanelHost) { | ||
var TestRunner = exports.TestRunner = function(proxy, reporter, workerPool, cookieShelf, assetsManager, controlPanelHost) { | ||
EventEmitter.call(this); | ||
@@ -23,11 +23,13 @@ this.controlPanelHost = controlPanelHost; | ||
this.cookieShelf = cookieShelf; | ||
this.testRunJsTmpl = new assets.JsTemplate(path.join(__dirname, "../_compiled_/client_runtime/test_run.jstmpl")); | ||
this.errHandlerJsTmpl = new assets.JsTemplate(path.join(__dirname, "../_compiled_/client_runtime/test_run_err_handler.jstmpl")); | ||
this.assetsManager = assetsManager; | ||
this.tasks = {}; | ||
this.testRuns = {}; | ||
this.workerPool.on("workerDisconnected", this._onWorkerDisconnected.bind(this)); | ||
this.proxy.addEventListener(TestRunner.JOB_OWNER_TOKEN, this); | ||
this.proxy.addOwnerDependentEventListener("beforeProxyResponse", TestRunner.JOB_OWNER_TOKEN, this._onBeforeProxyResponse.bind(this)); | ||
this.proxy.addOwnerDependentEventListener("error", TestRunner.JOB_OWNER_TOKEN, this._onProxyError.bind(this)); | ||
this.proxy.addOwnerDependentEventListener("serviceMsg", TestRunner.JOB_OWNER_TOKEN, this._onServiceMsg.bind(this)); | ||
var proxyEvents = this.proxy.events.for(TestRunner.JOB_OWNER_TOKEN); | ||
proxyEvents.listen("authCredentialsRequested", this._onAuthCredentialsRequested.bind(this)); | ||
proxyEvents.listen("beforeProxyResponse", this._onBeforeProxyResponse.bind(this)); | ||
proxyEvents.listen("error", this._onProxyError.bind(this)); | ||
var serviceChannelEvents = this.proxy.serviceChannel.events.for(TestRunner.JOB_OWNER_TOKEN); | ||
serviceChannelEvents.listen("serviceMsg", this._onServiceMsg.bind(this)); | ||
serviceChannelEvents.listen("testCafeScriptRequested", this._onTestCafeScriptRequested.bind(this)); | ||
}; | ||
@@ -46,5 +48,5 @@ | ||
TestRunner.prototype.getAuthCredentials = function(jobUID) { | ||
var testRun = this.testRuns[jobUID]; | ||
return testRun ? testRun.authCredentials : null; | ||
TestRunner.prototype._onAuthCredentialsRequested = function(proxyCtx, callback) { | ||
var testRun = this.testRuns[proxyCtx.jobInfo.uid]; | ||
callback(testRun ? testRun.authCredentials : null); | ||
}; | ||
@@ -79,5 +81,5 @@ | ||
} | ||
return; | ||
} else { | ||
callback(); | ||
} | ||
callback(); | ||
}; | ||
@@ -95,10 +97,11 @@ | ||
var causeUid = this._addPossibleTestFailCause(testRun, err); | ||
var verificationJs = this.errHandlerJsTmpl.getJs({ | ||
var errHandlerScript = this.assetsManager.compileTemplate(this.assetsManager.TEMPLATES.TEST_RUN_ERR_HANDLER, { | ||
workerIdleUrl: this.workerPool.getWorkerIdleUrl(testRun.workerName), | ||
jobUid: testRun.uid, | ||
jobOwnerToken: TestRunner.JOB_OWNER_TOKEN, | ||
serviceMsgUrl: this.proxy.serviceChannel.serviceMsgUrl, | ||
error: JSON.stringify(err), | ||
removeFailCauseUid: causeUid | ||
}); | ||
proxyErrRes(proxyCtx, util.format(PAGE_ERROR_VERIFICATION_HTML_TEMPLATE, verificationJs)); | ||
proxyErrRes(proxyCtx, util.format(PAGE_ERROR_VERIFICATION_HTML_TEMPLATE, errHandlerScript)); | ||
} else { | ||
@@ -112,4 +115,29 @@ this._addPossibleTestFailCause(testRun, err); | ||
TestRunner.prototype._onServiceMsg = function(msg, proxyCtx, callback) { | ||
var res = null, expectedInactivityDuration = null, testRun = this.testRuns[proxyCtx.jobInfo.uid]; | ||
TestRunner.prototype._onTestCafeScriptRequested = function(refererInfo, callback) { | ||
var runner = this, testRun = this.testRuns[refererInfo.jobInfo.uid]; | ||
this.cookieShelf.getClientCookieString(refererInfo.jobInfo, refererInfo.originResourceInfo.url, function(cookie) { | ||
var testRunScript = runner.assetsManager.compileTemplate(runner.assetsManager.TEMPLATES.TEST_RUN, { | ||
stepNames: refererInfo.isIFrame ? "null" : JSON.stringify(testRun.test.stepData.names), | ||
testSteps: refererInfo.isIFrame ? "null" : testRun.test.stepData.js, | ||
sharedJs: refererInfo.isIFrame ? "null" : testRun.fixture.sharedJs, | ||
nextStep: testRun.nextStep, | ||
workerIdleUrl: runner.workerPool.getWorkerIdleUrl(testRun.workerName), | ||
restartTestRunUrl: runner.workerPool.getRestartTestRunUrl(testRun.workerName), | ||
halted: testRun.halted, | ||
jobUid: testRun.uid, | ||
jobOwnerToken: TestRunner.JOB_OWNER_TOKEN, | ||
serviceMsgUrl: runner.proxy.serviceChannel.serviceMsgUrl, | ||
cookie: cookie.replace(/'/g, "\\'"), | ||
originHost: refererInfo.originResourceInfo.host, | ||
originProtocol: refererInfo.originResourceInfo.protocol, | ||
originHostname: refererInfo.originResourceInfo.hostname, | ||
originPort: refererInfo.originResourceInfo.port || "", | ||
emulateCursor: testRun.emulateCursor | ||
}); | ||
callback(testRunScript); | ||
}); | ||
}; | ||
TestRunner.prototype._onServiceMsg = function(msg, callback) { | ||
var res = null, expectedInactivityDuration = null, testRun = this.testRuns[msg.jobUid]; | ||
if (testRun && !testRun.halted) { | ||
@@ -134,6 +162,2 @@ switch (msg.cmd) { | ||
case cmd.RESET_ALL_POSSIBLE_FAIL_CAUSES: | ||
testRun.possibleFailCauses = []; | ||
break; | ||
case cmd.TEST_COMPLETE: | ||
@@ -151,14 +175,2 @@ testRun.halted = true; | ||
case cmd.GET_CURSOR_POS: | ||
res = testRun.cursorPos; | ||
break; | ||
case cmd.SET_CURSOR_POS: | ||
testRun.cursorPos = msg.cursorPos; | ||
break; | ||
case cmd.GET_NEXT_STEP: | ||
res = testRun.nextStep; | ||
break; | ||
case cmd.SET_NEXT_STEP: | ||
@@ -212,33 +224,19 @@ testRun.nextStep = msg.nextStep; | ||
var runner = this; | ||
this.cookieShelf.getClientCookieString(proxyCtx.jobInfo, proxyCtx.originResourceInfo.url, function(cookie) { | ||
var opt = { | ||
startupScript: runner.testRunJsTmpl.getJs({ | ||
testSteps: testRun.test.stepData.js, | ||
stepNames: JSON.stringify(testRun.test.stepData.names), | ||
workerIdleUrl: runner.workerPool.getWorkerIdleUrl(testRun.workerName), | ||
restartTestRunUrl: runner.workerPool.getRestartTestRunUrl(testRun.workerName), | ||
sharedJs: testRun.fixture.sharedJs, | ||
halted: testRun.halted, | ||
jobUid: testRun.uid, | ||
jobOwnerToken: TestRunner.JOB_OWNER_TOKEN, | ||
cookie: cookie.replace(/'/g, "\\'"), | ||
originHost: proxyCtx.originResourceInfo.host, | ||
originProtocol: proxyCtx.originResourceInfo.protocol, | ||
originHostname: proxyCtx.originResourceInfo.hostname, | ||
originPort: proxyCtx.originResourceInfo.port || "", | ||
emulateCursor: testRun.emulateCursor | ||
}), | ||
urlReplacer: runner.proxy.getResourceUrlReplacer(proxyCtx), | ||
uiCss: assets.getClientUICss() | ||
}; | ||
injector.injectInPage(proxyCtx.originResBodyChunks, proxyCtx.originResContentInfo.encoding, proxyCtx.originResContentInfo.charset, opt, function(err, html) { | ||
if (err) { | ||
testRun.halted = true; | ||
runner._addTestError(testRun, err); | ||
runner.workerPool.redirectToWorkerIdle(proxyCtx, testRun.workerName); | ||
} else { | ||
proxyCtx.originResBodyChunks = [ html ]; | ||
callback(); | ||
} | ||
}); | ||
if (!proxyCtx.isIFrame) { | ||
testRun.possibleFailCauses = []; | ||
} | ||
var opt = { | ||
urlReplacer: this.proxy.getResourceUrlReplacer(proxyCtx), | ||
styleUrl: this.assetsManager.uiStyleUrl, | ||
scriptUrl: this.proxy.serviceChannel.testCafeScriptUrl | ||
}; | ||
injector.injectInPage(proxyCtx.originResBodyChunks, proxyCtx.originResContentInfo.encoding, proxyCtx.originResContentInfo.charset, opt, function(err, html) { | ||
if (err) { | ||
testRun.halted = true; | ||
runner._addTestError(testRun, err); | ||
runner.workerPool.redirectToWorkerIdle(proxyCtx, testRun.workerName); | ||
} else { | ||
proxyCtx.originResBodyChunks = [ html ]; | ||
callback(); | ||
} | ||
}); | ||
@@ -280,6 +278,7 @@ }; | ||
TestRunner.prototype.addTask = function(name, suite, testUids, workerNames, emulateCursor) { | ||
TestRunner.prototype.addTask = function(name, suite, testUids, workerNames, emulateCursor, quarantineMode) { | ||
var runner = this, taskUid = uuid.v4(), task = { | ||
pendingTestRunCount: 0, | ||
started: false | ||
started: false, | ||
quarantineMode: quarantineMode | ||
}; | ||
@@ -292,3 +291,2 @@ workerNames.forEach(function(workerName) { | ||
authCredentials: fixture.authCredentials, | ||
cursorPos: null, | ||
emulateCursor: emulateCursor, | ||
@@ -298,3 +296,3 @@ errs: [], | ||
halted: false, | ||
nextStep: null, | ||
nextStep: 0, | ||
possibleFailCauses: [], | ||
@@ -306,3 +304,8 @@ restartCount: 0, | ||
uid: testRunUid, | ||
workerName: workerName | ||
workerName: workerName, | ||
quarantine: { | ||
succeeded: 0, | ||
failed: 0, | ||
lastFailedRunErrs: [] | ||
} | ||
}; | ||
@@ -314,3 +317,3 @@ worker.testRuns.push(testRunUid); | ||
this.tasks[taskUid] = task; | ||
this.reporter.taskAdded(taskUid, name, testUids.length, workerNames); | ||
this.reporter.taskAdded(taskUid, name, testUids.length, workerNames, quarantineMode); | ||
var taskReport = runner.reporter.getTaskReportByUid(taskUid); | ||
@@ -335,4 +338,25 @@ workerNames.forEach(function(workerName) { | ||
TestRunner.prototype._quarantine = function(testRun) { | ||
if (testRun.errs.length) { | ||
testRun.quarantine.lastFailedRunErrs = testRun.errs; | ||
testRun.quarantine.failed++; | ||
} else { | ||
testRun.quarantine.succeeded++; | ||
} | ||
if (testRun.quarantine.failed > 0) { | ||
if (testRun.quarantine.succeeded + testRun.quarantine.failed < QUARANTINE_TEST_RUN_COUNT) { | ||
this._resetTestRun(testRun); | ||
return true; | ||
} else { | ||
testRun.errs = testRun.quarantine.failed > testRun.quarantine.succeeded ? testRun.quarantine.lastFailedRunErrs : []; | ||
} | ||
} | ||
return false; | ||
}; | ||
TestRunner.prototype._completeTestRun = function(testRun) { | ||
var task = this.tasks[testRun.taskUid], workerName = testRun.workerName, worker = this.workerPool.get(workerName); | ||
if (task.quarantineMode && this._quarantine(testRun)) { | ||
return; | ||
} | ||
this.cookieShelf.removeCookies({ | ||
@@ -366,2 +390,13 @@ ownerToken: TestRunner.JOB_OWNER_TOKEN, | ||
TestRunner.prototype._resetTestRun = function(testRun) { | ||
testRun.errs = []; | ||
testRun.halted = false; | ||
testRun.nextStep = 0; | ||
testRun.stepsSharedData = null; | ||
this.cookieShelf.removeCookies({ | ||
ownerToken: TestRunner.JOB_OWNER_TOKEN, | ||
uid: testRun.uid | ||
}); | ||
}; | ||
TestRunner.prototype.restartCurrentTestRun = function(workerName) { | ||
@@ -376,12 +411,5 @@ var worker = this.workerPool.get(workerName), testRunUid = worker.testRuns.length && worker.testRuns[0], testRun = this.testRuns[testRunUid]; | ||
} else { | ||
testRun.errs = []; | ||
testRun.halted = false; | ||
testRun.nextStep = 0; | ||
testRun.stepsSharedData = null; | ||
this.cookieShelf.removeCookies({ | ||
ownerToken: TestRunner.JOB_OWNER_TOKEN, | ||
uid: testRunUid | ||
}); | ||
this._resetTestRun(testRun); | ||
} | ||
} | ||
}; |
@@ -1,2 +0,2 @@ | ||
var fs = require("fs"), path = require("path"), util = require("util"), uuid = require("node-uuid"), EventEmitter = require("events").EventEmitter, async = require("async"), suite = require("./suite"), FixtureCode = require("../fixture_code/"), ERR = require("./server_errs"); | ||
var fs = require("fs"), path = require("path"), util = require("util"), uuid = require("node-uuid"), EventEmitter = require("events").EventEmitter, Process = require("child_process"), async = require("async"), suite = require("./suite"), FixtureCode = require("../fixture_code/"), ERR = require("./server_errs"); | ||
@@ -25,3 +25,3 @@ function validateBasePath(basePath) { | ||
var getPlatformDependentPath = function(universalPath) { | ||
return universalPath.replace(new RegExp(VirtualFS.PATH_UNIVERSAL_SEPARATOR, "g"), VirtualFS.PLATFORM_PATH_SEPARATOR); | ||
return universalPath.replace(new RegExp(VirtualFS.PATH_UNIVERSAL_SEPARATOR, "g"), path.sep); | ||
}; | ||
@@ -79,7 +79,6 @@ | ||
var VirtualFS = exports.VirtualFS = function(basePath) { | ||
var VirtualFS = exports.VirtualFS = function(basePath, config) { | ||
EventEmitter.call(this); | ||
basePath = preparePath(basePath); | ||
validateBasePath(basePath); | ||
this.basePath = basePath; | ||
this.basePath = null; | ||
this.config = config; | ||
this.requireRebuild = true; | ||
@@ -90,2 +89,3 @@ this.suiteCache = null; | ||
this.rebuildLock = false; | ||
this.setBasePath(basePath); | ||
}; | ||
@@ -107,4 +107,10 @@ | ||
VirtualFS.PLATFORM_PATH_SEPARATOR = VirtualFS.WIN_PLATFORM ? "\\" : VirtualFS.PATH_UNIVERSAL_SEPARATOR; | ||
VirtualFS.GET_LOCAL_DRIVES_CMD = "wmic logicaldisk get name, description"; | ||
VirtualFS.GET_LOCAL_DRIVES_DESC = "Local Fixed Disk"; | ||
VirtualFS.CMD_OUTPUT_NEWLINE_CHARACTER = "\r\r\n"; | ||
VirtualFS.CFG_FILE_PATH = path.join(__dirname, "../../config.json"); | ||
VirtualFS.getTestUidsByPath = function(suite, dirPath) { | ||
@@ -118,2 +124,8 @@ var testUids = [], fixtures = getFixturesByPath(suite, dirPath, false); | ||
VirtualFS.prototype.setBasePath = function(basePath) { | ||
var newBasePath = preparePath(basePath); | ||
validateBasePath(newBasePath); | ||
this.basePath = newBasePath; | ||
}; | ||
VirtualFS.prototype.watchRevisionChanges = function() { | ||
@@ -197,3 +209,3 @@ var vfs = this; | ||
VirtualFS.prototype.getPathInfo = function(dirPath, callback) { | ||
var vfs = this, fsPath = path.join(vfs.basePath, dirPath.join(VirtualFS.PLATFORM_PATH_SEPARATOR)); | ||
var vfs = this, fsPath = path.join(vfs.basePath, dirPath.join(path.sep)); | ||
var getInfoFromSuite = function() { | ||
@@ -637,2 +649,84 @@ vfs.getSuite(function(suite) { | ||
VirtualFS.prototype.getLocalDrives = function(callback) { | ||
if (VirtualFS.WIN_PLATFORM) { | ||
Process.exec(VirtualFS.GET_LOCAL_DRIVES_CMD, function(err, stdout) { | ||
var stdoutLines = stdout.split(VirtualFS.CMD_OUTPUT_NEWLINE_CHARACTER), drives = stdoutLines.splice(1, stdoutLines.length - 3), result = []; | ||
if (err) { | ||
err = ERR.VIRTUAL_FILE_SYSTEM_NODEJS_CANT_GET_DRIVE_LIST; | ||
} | ||
for (var i = 0, length = drives.length; i < length; i++) { | ||
if (drives[i].indexOf(VirtualFS.GET_LOCAL_DRIVES_DESC) === -1) { | ||
continue; | ||
} | ||
var driveName = drives[i].replace(VirtualFS.GET_LOCAL_DRIVES_DESC, "").replace(/\s*/g, "") + path.sep; | ||
result.push({ | ||
text: driveName, | ||
id: encodeURIComponent(driveName) | ||
}); | ||
} | ||
callback(err, result); | ||
}); | ||
} else { | ||
callback(null, [ { | ||
text: VirtualFS.PATH_UNIVERSAL_SEPARATOR, | ||
id: encodeURIComponent(VirtualFS.PATH_UNIVERSAL_SEPARATOR) | ||
} ]); | ||
} | ||
}; | ||
VirtualFS.prototype.getDirsByPath = function(fsPath, callback) { | ||
var dirs = [], fsPath = getPlatformDependentPath(fsPath); | ||
fs.readdir(fsPath, function(err, items) { | ||
if (err) { | ||
err = { | ||
code: ERR.VIRTUAL_FILE_SYSTEM_DIR_DOESNT_READ, | ||
fsPath: fsPath | ||
}; | ||
} | ||
if (items) { | ||
async.forEachSeries(items, function(elm, elmListCallback) { | ||
var elmPath = path.join(fsPath, elm); | ||
fs.stat(elmPath, function(err, stats) { | ||
if (!err && stats.isDirectory()) { | ||
dirs.push({ | ||
text: elm, | ||
id: encodeURIComponent(elmPath) | ||
}); | ||
} | ||
elmListCallback(); | ||
}); | ||
}, function() { | ||
callback(err, dirs); | ||
}); | ||
} else { | ||
callback(err, dirs); | ||
} | ||
}); | ||
}; | ||
VirtualFS.prototype.setTestsDir = function(fsPath, callback) { | ||
var vfs = this, basePath = vfs.basePath, dirNotExistsErr = { | ||
code: ERR.VIRTUAL_FILE_SYSTEM_DIR_DOESNT_READ, | ||
fsPath: fsPath | ||
}; | ||
vfs.removeFSWatchers(); | ||
try { | ||
vfs.setBasePath(fsPath); | ||
} catch (err) { | ||
vfs._setupFSWatchers(basePath); | ||
callback(err && dirNotExistsErr); | ||
return; | ||
} | ||
vfs.suiteCache = null; | ||
this.forceBuild(function() { | ||
vfs.config.setTestsDir(fsPath, function(err) { | ||
callback(err && dirNotExistsErr); | ||
}); | ||
}); | ||
}; | ||
VirtualFS.prototype.getBasePath = function() { | ||
return getPlatformDependentPath(this.basePath); | ||
}; | ||
exports.getPlatformDependentPath = getPlatformDependentPath; |
var async = require("async"), EventEmitter = require("events").EventEmitter, execFile = require("child_process").execFile, fs = require("fs"), path = require("path"), url = require("url"), util = require("util"), userAgentParser = require("useragent"), uuid = require("node-uuid"), ERR = require("./server_errs"); | ||
var ADD_WORKER_URL_PATHNAME_PATTERN = "/worker/add/%s/", WORKER_IDLE_URL_PATHNAME_PATTERN = "/worker/idle/%s/", CREATE_WORKER_ON_LOCAL_BROWSER_TIMEOUT = 1e4; | ||
var ADD_WORKER_URL_PATHNAME_PATTERN = "/worker/add/%s/", WORKER_IDLE_URL_PATHNAME_PATTERN = "/worker/idle/%s/", CREATE_WORKER_ON_LOCAL_BROWSER_TIMEOUT = 3e4; | ||
@@ -18,3 +18,3 @@ function sanitizeWorkerName(name) { | ||
WorkerPool.WORKER_HEARTBEAT_TIMEOUT = 6e4; | ||
WorkerPool.WORKER_HEARTBEAT_TIMEOUT = 24e4; | ||
@@ -21,0 +21,0 @@ var WORKER_TYPE = WorkerPool.WORKER_TYPE = { |
@@ -6,3 +6,2 @@ TestCafeClient = typeof TestCafeClient === "undefined" ? {} : TestCafeClient; | ||
root.PROXY_SERVICE_MSG_REQUEST_HEADER = "x-tc-sm-c8f5bd4f"; | ||
root.XHR_IS_TESTCAFE_REQUEST = "is_tc_req-c8f5bd4f"; | ||
root.XHR_REQUEST_MARKER_HEADER = "x-tc-xm-cd46977f"; | ||
@@ -24,4 +23,5 @@ root.XHR_CORS_SUPPORTED_FLAG = "cors"; | ||
root.TEST_CAFE_PROPERTY_PREFIX = "tc-1b082a6cec-51966-"; | ||
root.OLD_ATTR_VALUES = root.TEST_CAFE_PROPERTY_PREFIX + "oldAttrValues"; | ||
root.ACTION_FUNC_NAMES = [ "click", "rclick", "dblclick", "drag", "type", "wait", "hover", "press", "select" ]; | ||
root.PROPERTIES_FOR_WRAPPING = [ "innerHTML", "src", "href", "action", "location", "domain", "cookie", "referrer", "URL", "onbeforeunload" ]; | ||
})(); |
@@ -21,9 +21,12 @@ TestCafeClient = typeof TestCafeClient === "undefined" ? {} : TestCafeClient; | ||
var getAttr = function($el, attr) { | ||
return isNode ? $el[0].attribs[attr] : TCC.domSandbox.nativeMethods.getAttribute.call($el[0], attr); | ||
return isNode ? $el[0].attribs[attr] : TCC.sandboxes.dom.nativeMethods.getAttribute.call($el[0], attr); | ||
}; | ||
var getTagName = function($el) { | ||
return isNode ? $el[0].name : $el[0].tagName; | ||
}; | ||
var setAttr = function($el, attr, value) { | ||
return isNode ? $el[0].attribs[attr] = value : TCC.domSandbox.nativeMethods.setAttribute.call($el[0], attr, value); | ||
return isNode ? $el[0].attribs[attr] = value : TCC.sandboxes.dom.nativeMethods.setAttribute.call($el[0], attr, value); | ||
}; | ||
var removeAttr = function($el, attr) { | ||
return isNode ? $el.removeAttr(attr) : $el[0].removeAttribute(attr); | ||
return isNode ? $el.removeAttr(attr) : TCC.sandboxes.dom.nativeMethods.removeAttribute.call($el[0], attr); | ||
}; | ||
@@ -52,19 +55,22 @@ var syncWrapProps = function($el, urlAttr) { | ||
}; | ||
var enumAttrs = function($el, attrRegEx, action) { | ||
var attributes = $el[0].attributes || []; | ||
var attrNameWrappingRequired = function($el, attr) { | ||
var tagName = getTagName($el).toLowerCase(); | ||
return (attr !== "action" || tagName !== "form") && (attr !== "href" || tagName !== "a" && tagName !== "area" && tagName !== "link" && tagName !== "base") && (attr !== "hreflang" || tagName !== "a" && tagName !== "link") && (attr !== "nohref" || tagName !== "area") && (attr !== "src" || tagName !== "script" && tagName !== "input" && tagName !== "frame" && tagName !== "iframe" && tagName !== "img" && tagName !== "embed"); | ||
}; | ||
var enumAttrs = function($el, action) { | ||
if (isNode) { | ||
for (var attr in $el[0].attribs) { | ||
if (Object.prototype.hasOwnProperty.call($el[0].attribs, attr)) { | ||
attributes.push({ | ||
nodeName: attr | ||
}); | ||
Object.keys($el[0].attribs).forEach(function(attr) { | ||
if (attr.indexOf(consts.DOM_SANDBOX_STORED_ATTR_KEY_PREFIX) === -1) { | ||
action(attr); | ||
} | ||
}); | ||
} else { | ||
var attributes = $el[0].attributes || []; | ||
for (var i = 0; i < attributes.length; i++) { | ||
var attr = attributes[i].nodeName; | ||
if (attr.indexOf(consts.DOM_SANDBOX_STORED_ATTR_KEY_PREFIX) === -1) { | ||
action(attr); | ||
} | ||
} | ||
} | ||
for (var i = 0; i < attributes.length; i++) { | ||
var attr = attributes[i].nodeName; | ||
if (attrRegEx.test(attr) && attr.indexOf(consts.DOM_SANDBOX_STORED_ATTR_KEY_PREFIX) === -1) { | ||
action(attr); | ||
} | ||
} | ||
}; | ||
@@ -83,2 +89,7 @@ var processTargetBlank = function($el) { | ||
} | ||
}, processStyleAttr = function($el, urlReplacer) { | ||
var style = getAttr($el, "style"); | ||
if (style) { | ||
setAttr($el, "style", root.processStylesheet(style, urlReplacer)); | ||
} | ||
}, processScript = function($script) { | ||
@@ -95,12 +106,2 @@ if (isNode) { | ||
} | ||
}, processForm = function($form) { | ||
var method = getAttr($form, "METHOD"); | ||
if (!isNode && (!method || method.toLowerCase() === "get")) { | ||
var action = getAttr($form, "action"), desc = urlUtil.parseUrl(action).query[urlUtil.REQUEST_DESCRIPTOR_QUERY_KEY]; | ||
$("<input>", { | ||
type: "hidden", | ||
name: urlUtil.REQUEST_DESCRIPTOR_QUERY_KEY, | ||
value: desc | ||
}).appendTo($form); | ||
} | ||
}, processEvtHandlers = function($el) { | ||
@@ -154,12 +155,16 @@ if (isNode) { | ||
} | ||
}, processAttrValue = function($el, urlReplacer, pattern) { | ||
enumAttrs($el, pattern.attrRegEx, function(attr) { | ||
setAttr($el, attr, root.wrapProps(getAttr($el, attr))); | ||
}, processAttrValue = function($el) { | ||
enumAttrs($el, function(attr) { | ||
if (attr === "id" || attr === "class" || attr.indexOf("data-") === 0) { | ||
setAttr($el, attr, root.wrapProps(getAttr($el, attr))); | ||
} | ||
}); | ||
}, processCustomAttr = function($el, urlReplacer, pattern) { | ||
enumAttrs($el, pattern.attrRegEx, function(attr) { | ||
var replacedAttr = root.wrapProps(attr); | ||
if (replacedAttr !== attr) { | ||
setAttr($el, replacedAttr, getAttr($el, attr)); | ||
removeAttr($el, attr); | ||
}, processCustomAttr = function($el) { | ||
enumAttrs($el, function(attr) { | ||
if (attrNameWrappingRequired($el, attr)) { | ||
var replacedAttr = root.wrapProps(attr); | ||
if (replacedAttr !== attr) { | ||
setAttr($el, replacedAttr, getAttr($el, attr)); | ||
removeAttr($el, attr); | ||
} | ||
} | ||
@@ -179,3 +184,3 @@ }); | ||
urlAttr: "action", | ||
elementProcessors: [ processTargetBlank, processUrlAttrs, processForm ] | ||
elementProcessors: [ processTargetBlank, processUrlAttrs ] | ||
}, { | ||
@@ -196,9 +201,4 @@ selector: "iframe", | ||
selector: "*", | ||
attrRegEx: /^(data-|id$|class$)/i, | ||
elementProcessors: [ processAttrValue ] | ||
elementProcessors: [ processAttrValue, processCustomAttr, processStyleAttr ] | ||
}, { | ||
selector: "*", | ||
attrRegEx: /^data-/i, | ||
elementProcessors: [ processCustomAttr ] | ||
}, { | ||
selector: "script", | ||
@@ -218,3 +218,3 @@ elementProcessors: [ processScript ] | ||
if (!isNode && $.browser.msie && parseInt($.browser.version, 10) === 9 && $el[0].tagName.toLowerCase() === "script") { | ||
var $clone = $(TCC.domSandbox.nativeMethods.cloneNode.call($el[0], false)); | ||
var $clone = $(TCC.sandboxes.dom.nativeMethods.cloneNode.call($el[0], false)); | ||
$clone[0].src = $clone[0].innerHTML = ""; | ||
@@ -225,2 +225,6 @@ return $clone; | ||
}; | ||
var isTestCafeElement = function($el) { | ||
var el = $el[0]; | ||
return typeof el.className === "string" && el.className.indexOf(consts.TEST_CAFE_UI_CLASSNAME_POSTFIX) > -1; | ||
}; | ||
root.processPage = function($, urlReplacer) { | ||
@@ -242,3 +246,3 @@ for (var i = 0; i < ELEMENT_PROCESSOR_PATTERNS.length; i++) { | ||
var pattern = ELEMENT_PROCESSOR_PATTERNS[i]; | ||
if ($elementForSelectorCheck.is(pattern.selector)) { | ||
if ($elementForSelectorCheck.is(pattern.selector) && !isTestCafeElement($el)) { | ||
for (var j = 0; j < pattern.elementProcessors.length; j++) { | ||
@@ -261,4 +265,7 @@ pattern.elementProcessors[j]($el, urlReplacer, pattern); | ||
root.isJSON = function(text) { | ||
return /^\s*\{[\s\S]*\}\s*$/.test(text); | ||
return /^\s*\{.*\}\s*$/.test(text); | ||
}; | ||
root.isDataScript = function(text) { | ||
return root.isJSON(text) || /^\s*\[.*\]\s*$/.test(text); | ||
}; | ||
root.getWrapper = function(name) { | ||
@@ -268,7 +275,7 @@ return name.slice(0, -1) + consts.DOM_SANDBOX_WRAPPER_SYMBOL; | ||
root.processScript = function(text, wrappersOnly) { | ||
var bom = getBOM(text), isJson = root.isJSON(text); | ||
var bom = getBOM(text), isDataScript = root.isDataScript(text); | ||
if (bom) { | ||
text.replace(bom, ""); | ||
} | ||
if (!isJson && !wrappersOnly && text.indexOf(consts.DOM_SANDBOX_OVERRIDE_DOM_METHOD_NAME) === -1) { | ||
if (!isDataScript && !wrappersOnly && text.indexOf(consts.DOM_SANDBOX_OVERRIDE_DOM_METHOD_NAME) === -1) { | ||
var overrideDomMeth = "window['" + consts.DOM_SANDBOX_OVERRIDE_DOM_METHOD_NAME + "']"; | ||
@@ -275,0 +282,0 @@ text = "\r\ntypeof window !== 'undefined' && " + overrideDomMeth + " && " + overrideDomMeth + "();\r\n" + text; |
@@ -11,3 +11,2 @@ TestCafeClient = typeof TestCafeClient === "undefined" ? {} : TestCafeClient; | ||
root.REMOVE_POSSIBLE_FAIL_CAUSE = "CMD_REMOVE_POSSIBLE_FAIL_CAUSE"; | ||
root.RESET_ALL_POSSIBLE_FAIL_CAUSES = "CMD_RESET_ALL_POSSIBLE_FAIL_CAUSES"; | ||
root.TEST_COMPLETE = "CMD_TEST_COMPLETE"; | ||
@@ -20,12 +19,6 @@ root.INACTIVITY_EXPECTED = "CMD_INACTIVITY_EXPECTED"; | ||
root.SET_COOKIE = "CMD_SET_COOKIE"; | ||
root.GET_CURSOR_POS = "CMD_GET_CURSOR_POS"; | ||
root.SET_CURSOR_POS = "CMD_SET_CURSOR_POS"; | ||
root.GET_NEXT_STEP = "CMD_GET_NEXT_STEP"; | ||
root.SET_NEXT_STEP = "CMD_SET_NEXT_STEP"; | ||
root.SILENT_MODE_SET = "CMD_SILENT_MODE_SET"; | ||
root.SILENT_MODE_GET = "CMD_SILENT_MODE_GET"; | ||
root.TOOLBAR_POSITION_SET = "CMD_TOOLBAR_POSITION_SET"; | ||
root.TOOLBAR_POSITION_GET = "CMD_TOOLBAR_POSITION_GET"; | ||
root.DISABLE_TOOLTIP_SET = "CMD_DISABLE_TOOLTIP_SET"; | ||
root.DISABLE_TOOLTIP_GET = "CMD_DISABLE_TOOLTIP_GET"; | ||
root.ADD_TEST_CODE = "CMD_ADD_TEST_CODE"; | ||
@@ -32,0 +25,0 @@ root.RESTART_RECORD = "CMD_RESTART_RECORD"; |
@@ -64,3 +64,7 @@ TestCafeClient = typeof TestCafeClient === "undefined" ? {} : TestCafeClient; | ||
var urlResolver = (doc || document)[root.DOCUMENT_URL_RESOLVER]; | ||
urlResolver.href = url; | ||
if (url === null) { | ||
urlResolver.removeAttribute("href"); | ||
} else { | ||
urlResolver.href = url; | ||
} | ||
return urlResolver.href; | ||
@@ -81,3 +85,3 @@ }; | ||
for (var i = 0; i < queryKeyValueStrs.length; i++) { | ||
var keyValue = queryKeyValueStrs[i].split("="), key = keyValue[0], val = keyValue[1]; | ||
var keyValue = queryKeyValueStrs[i].split("="), key = keyValue[0], val = queryKeyValueStrs[i].substr(key.length + 1); | ||
if (key) { | ||
@@ -184,3 +188,3 @@ if (key === root.REQUEST_DESCRIPTOR_QUERY_KEY) { | ||
var parsedCheckedUrl = root.parseUrl(checkedUrl), parsedLocation = root.parseUrl(location), parsedProxyLocation = root.parseProxyUrl(location), parsedOriginUrl = parsedProxyLocation ? parsedProxyLocation.originResourceInfo : null; | ||
isSubDomain = function(domain, subDomain) { | ||
var isSubDomain = function(domain, subDomain) { | ||
var index = subDomain.lastIndexOf(domain); | ||
@@ -348,3 +352,3 @@ return subDomain[index - 1] === "." && subDomain.length === index + domain.length; | ||
hostname: originHostParts[0], | ||
port: parseInt(originHostParts[1], 10), | ||
port: originHostParts[1] || "", | ||
pathname: parsedProxyUrl.pathname, | ||
@@ -351,0 +355,0 @@ query: proxyQuery, |
{ | ||
"name": "testcafe", | ||
"description": "Functional testing for the web", | ||
"version": "13.1.2", | ||
"version": "13.1.3", | ||
"dependencies": { | ||
@@ -10,3 +10,3 @@ "async": "0.2.6", | ||
"iconv-lite": "0.2.7", | ||
"whacko": "0.12.7", | ||
"whacko": "0.12.9", | ||
"moment": "1.7.0", | ||
@@ -16,3 +16,3 @@ "node-uuid": "1.3.3", | ||
"uglify-js": "1.2.6", | ||
"tough-cookie" : "0.9.14", | ||
"tough-cookie": "0.9.14", | ||
"revalidator": "0.1.5", | ||
@@ -26,2 +26,2 @@ "useragent": "2.0.6", | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
1724762
68
6668
14
10
+ Addedparse5@0.6.1(transitive)
+ Addedwhacko@0.12.9(transitive)
- Removedparse5@0.5.4(transitive)
- Removedwhacko@0.12.7(transitive)
Updatedwhacko@0.12.9