projecte_ionic/node_modules/protractor/built/debugger.js
2022-02-09 18:30:03 +01:00

233 lines
10 KiB
JavaScript
Executable file

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const net = require("net");
const selenium_webdriver_1 = require("selenium-webdriver");
const util = require("util");
const logger_1 = require("./logger");
let breakpointHook = require('./breakpointhook.js');
let logger = new logger_1.Logger('protractor');
class DebugHelper {
constructor(browserUnderDebug_) {
this.browserUnderDebug_ = browserUnderDebug_;
}
initBlocking(debuggerClientPath, onStartFn, opt_debugPort) {
this.init_(debuggerClientPath, true, onStartFn, opt_debugPort);
}
init(debuggerClientPath, onStartFn, opt_debugPort) {
this.init_(debuggerClientPath, false, onStartFn, opt_debugPort);
}
/**
* 1) Set up helper functions for debugger clients to call on (e.g.
* execute code, get autocompletion).
* 2) Enter process into debugger mode. (i.e. process._debugProcess).
* 3) Invoke the debugger client specified by debuggerClientPath.
*
* @param {string} debuggerClientPath Absolute path of debugger client to use.
* @param {boolean} blockUntilExit Whether to block the flow until process exit or resume
* immediately.
* @param {Function} onStartFn Function to call when the debugger starts. The
* function takes a single parameter, which represents whether this is the
* first time that the debugger is called.
* @param {number=} opt_debugPort Optional port to use for the debugging
* process.
*
* @return {Promise} If blockUntilExit, a promise resolved when the debugger process
* exits. Otherwise, resolved when the debugger process is ready to begin.
*/
init_(debuggerClientPath, blockUntilExit, onStartFn, opt_debugPort) {
const vm_ = require('vm');
let flow = selenium_webdriver_1.promise.controlFlow();
let context = { require: require };
global.list = (locator) => {
return global.protractor.browser.findElements(locator).then((arr) => {
let found = [];
for (let i = 0; i < arr.length; ++i) {
arr[i].getText().then((text) => {
found.push(text);
});
}
return found;
});
};
for (let key in global) {
context[key] = global[key];
}
let sandbox = vm_.createContext(context);
let debuggingDone = selenium_webdriver_1.promise.defer();
// We run one flow.execute block for the debugging session. All
// subcommands should be scheduled under this task.
let executePromise = flow.execute(() => {
process['debugPort'] = opt_debugPort || process['debugPort'];
this.validatePortAvailability_(process['debugPort']).then((firstTime) => {
onStartFn(firstTime);
let args = [process.pid, process['debugPort']];
if (this.browserUnderDebug_.debuggerServerPort) {
args.push(this.browserUnderDebug_.debuggerServerPort);
}
let nodedebug = require('child_process').fork(debuggerClientPath, args);
process.on('exit', function () {
nodedebug.kill('SIGTERM');
});
nodedebug
.on('message', (m) => {
if (m === 'ready') {
breakpointHook();
if (!blockUntilExit) {
debuggingDone.fulfill();
}
}
})
.on('exit', () => {
// Clear this so that we know it's ok to attach a debugger
// again.
this.dbgCodeExecutor = null;
debuggingDone.fulfill();
});
});
return debuggingDone.promise;
}, 'debugging tasks');
// Helper used only by debuggers at './debugger/modes/*.js' to insert code
// into the control flow, via debugger 'evaluate' protocol.
// In order to achieve this, we maintain a task at the top of the control
// flow, so that we can insert frames into it.
// To be able to simulate callback/asynchronous code, we poll this object
// whenever `breakpointHook` is called.
this.dbgCodeExecutor = {
execPromise_: undefined,
execPromiseResult_: undefined,
execPromiseError_: undefined,
// A dummy repl server to make use of its completion function.
replServer_: require('repl').start({
input: { on: function () { }, resume: function () { } },
// dummy readable stream
output: { write: function () { } },
useGlobal: true
}),
// Execute a function, which could yield a value or a promise,
// and allow its result to be accessed synchronously
execute_: function (execFn_) {
this.execPromiseResult_ = this.execPromiseError_ = undefined;
this.execPromise_ = execFn_();
// Note: This needs to be added after setting execPromise to execFn,
// or else we cause this.execPromise_ to get stuck in pending mode
// at our next breakpoint.
this.execPromise_.then((result) => {
this.execPromiseResult_ = result;
breakpointHook();
}, (err) => {
this.execPromiseError_ = err;
breakpointHook();
});
},
// Execute a piece of code.
// Result is a string representation of the evaluation.
execute: function (code) {
let execFn_ = () => {
// Run code through vm so that we can maintain a local scope which is
// isolated from the rest of the execution.
let res;
try {
res = vm_.runInContext(code, sandbox);
}
catch (e) {
res = selenium_webdriver_1.promise.when('Error while evaluating command: ' + e);
}
if (!selenium_webdriver_1.promise.isPromise(res)) {
res = selenium_webdriver_1.promise.when(res);
}
return res.then((res) => {
if (res === undefined) {
return undefined;
}
else {
// The '' forces res to be expanded into a string instead of just
// '[Object]'. Then we remove the extra space caused by the ''
// using substring.
return util.format.apply(this, ['', res]).substring(1);
}
});
};
this.execute_(execFn_);
},
// Autocomplete for a line.
// Result is a JSON representation of the autocomplete response.
complete: function (line) {
let execFn_ = () => {
let deferred = selenium_webdriver_1.promise.defer();
this.replServer_.complete(line, (err, res) => {
if (err) {
deferred.reject(err);
}
else {
deferred.fulfill(JSON.stringify(res));
}
});
return deferred.promise;
};
this.execute_(execFn_);
},
// Code finished executing.
resultReady: function () {
return !(this.execPromise_.state_ === 'pending');
},
// Get asynchronous results synchronously.
// This will throw if result is not ready.
getResult: function () {
if (!this.resultReady()) {
throw new Error('Result not ready');
}
if (this.execPromiseError_) {
throw this.execPromiseError_;
}
return this.execPromiseResult_;
}
};
return executePromise;
}
/**
* Validates that the port is free to use. This will only validate the first
* time it is called. The reason is that on subsequent calls, the port will
* already be bound to the debugger, so it will not be available, but that is
* okay.
*
* @returns {Promise<boolean>} A promise that becomes ready when the
* validation
* is done. The promise will resolve to a boolean which represents whether
* this is the first time that the debugger is called.
*/
validatePortAvailability_(port) {
if (this.debuggerValidated_) {
return selenium_webdriver_1.promise.when(false);
}
let doneDeferred = selenium_webdriver_1.promise.defer();
// Resolve doneDeferred if port is available.
let tester = net.connect({ port: port }, () => {
doneDeferred.reject('Port ' + port + ' is already in use. Please specify ' +
'another port to debug.');
});
tester.once('error', (err) => {
if (err.code === 'ECONNREFUSED') {
tester
.once('close', () => {
doneDeferred.fulfill(true);
})
.end();
}
else {
doneDeferred.reject('Unexpected failure testing for port ' + port + ': ' + JSON.stringify(err));
}
});
return doneDeferred.promise.then((firstTime) => {
this.debuggerValidated_ = true;
return firstTime;
}, (err) => {
console.error(err);
return process.exit(1);
});
}
isAttached() {
return !!this.dbgCodeExecutor;
}
}
exports.DebugHelper = DebugHelper;
//# sourceMappingURL=debugger.js.map