Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
feat(debugger): allow multiple browser.pause()
Browse files Browse the repository at this point in the history
After this change, you can put multiple browser.pause() in the code. Each
browser.pause() is like a traditional breakpoint.

To detach from the debugger, press ^D (CTRL+D). This will continue code
execution until it hits the next browser.pause() or code finishes running.

This PR also fixes a number of small formatting issues.
  • Loading branch information
hankduan committed Dec 18, 2015
1 parent 148f020 commit b110ed9
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 71 deletions.
20 changes: 5 additions & 15 deletions lib/debugger/clients/explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ var WdRepl = function() {
this.client;
};

/**
* Initiate debugger client.
* @private
*/
WdRepl.prototype.initClient_ = function() {
this.client =
debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
};

/**
* Instantiate a server to handle IO.
* @param {number} port The port to start the server.
Expand Down Expand Up @@ -143,12 +134,11 @@ WdRepl.prototype.initReplOrServer_ = function() {
* @public
*/
WdRepl.prototype.init = function() {
console.log('Type <tab> to see a list of locator strategies.');
console.log('Use the `list` helper function to find elements by strategy:');
console.log(' e.g., list(by.binding(\'\')) gets all bindings.');

this.initClient_();
this.initReplOrServer_();
var self = this;
this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
this.client.once('ready', function() {
self.initReplOrServer_();
});
};

var wdRepl = new WdRepl();
Expand Down
28 changes: 6 additions & 22 deletions lib/debugger/clients/wddebugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@ var WdDebugger = function() {
this.currentRepl;
};

/**
* Initiate debugger client.
* @private
*/
WdDebugger.prototype.initClient_ = function() {
this.client =
debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
this.client.once('ready', function() {
console.log(' ready\n');
console.log('press c to continue to the next webdriver command');
console.log('press d to continue to the next debugger statement');
console.log('type "repl" to enter interactive mode');
console.log('type "exit" to break out of interactive mode');
console.log('press ^C to exit');
console.log();
});
};

/**
* Eval function for processing a single step in repl.
* @private
Expand Down Expand Up @@ -116,7 +98,7 @@ WdDebugger.prototype.initRepl_ = function() {
self.replServer.complete = self.currentRepl.complete.bind(self.currentRepl);

self.replServer.on('exit', function() {
console.log('Exiting debugger.');
console.log('Resuming code execution');
self.client.req({command: 'disconnect'}, function() {
// Intentionally blank.
});
Expand All @@ -129,9 +111,11 @@ WdDebugger.prototype.initRepl_ = function() {
* @public
*/
WdDebugger.prototype.init = function() {
console.log('------- WebDriver Debugger -------');
this.initClient_();
this.initRepl_();
var self = this;
this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
this.client.once('ready', function() {
self.initRepl_();
});
};

var wdDebugger = new WdDebugger();
Expand Down
7 changes: 1 addition & 6 deletions lib/debugger/modes/debuggerRepl.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var util = require('util');

var DBG_INITIAL_SUGGESTIONS =
['repl', 'c', 'frame', 'scopes', 'scripts', 'source', 'backtrace', 'd'];
['repl', 'c', 'frame', 'scopes', 'scripts', 'source', 'backtrace'];

/**
* Repl to step through code.
Expand Down Expand Up @@ -60,11 +60,6 @@ DebuggerRepl.prototype.stepEval = function(cmd, callback) {
callback();
});
break;
case 'd':
this.client.req({command: 'disconnect'}, function() {
// Intentionally blank.
});
break;
default:
console.log('Unrecognized command.');
callback();
Expand Down
109 changes: 81 additions & 28 deletions lib/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -824,17 +824,62 @@ Protractor.prototype.debugger = function() {
'add breakpoint to control flow');
};

/**
* 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.
*
* @return {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.
*/
Protractor.prototype.validatePortAvailability_ = function(port) {
if (this.debuggerValidated_) {
return webdriver.promise.fulfilled(false);
}
this.debuggerValidated_ = true;

var doneDeferred = webdriver.promise.defer();

// Resolve doneDeferred if port is available.
var net = require('net');
var tester = net.connect({port: port}, function() {
doneDeferred.reject('Port ' + port +
' is already in use. Please specify ' + 'another port to debug.');
});
tester.once('error', function (err) {
if (err.code === 'ECONNREFUSED') {
tester.once('close', function() {
doneDeferred.fulfill(true);
}).end();
} else {
doneDeferred.reject(
'Unexpected failure testing for port ' + port + ': ' +
JSON.stringify(err));
}
});

return doneDeferred.then(null, function(err) {
console.error(err);
process.exit(1);
});
},

/**
* Helper function to:
* 1) Set up helper functions for debugger clients to call on (e.g.
* getControlFlowText, 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 {string} debuggerClientPath Absolute path of debugger client to use
* @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
*/
Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort) {
Protractor.prototype.initDebugger_ = function(debuggerClientPath, onStartFn, opt_debugPort) {
// Patch in a function to help us visualize what's going on in the control
// flow.
webdriver.promise.ControlFlow.prototype.getControlFlowText = function() {
Expand All @@ -847,26 +892,6 @@ Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort)
// hackier, and involves messing with the control flow's internals / private
// variables.
return helper.filterStackTrace(controlFlowText);

};

// Invoke fn if port is available.
var onPortAvailable = function(port, fn) {
var net = require('net');
var tester = net.connect({port: port}, function() {
console.error('Port ' + port + ' is already in use. Please specify ' +
'another port to debug.');
process.exit(1);
});
tester.once('error', function (err) {
if (err.code === 'ECONNREFUSED') {
tester.once('close', fn).end();
} else {
console.error('Unexpected failure testing for port ' + port + ': ',
err);
process.exit(1);
}
});
};

var vm_ = require('vm');
Expand All @@ -892,11 +917,11 @@ Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort)

var browserUnderDebug = this;
var debuggerReadyPromise = webdriver.promise.defer();
flow.execute(function() {
log.puts('Starting WebDriver debugger in a child process. Pause is ' +
'still beta, please report issues at github.com/angular/protractor\n');
flow.execute(function() {
process.debugPort = opt_debugPort || process.debugPort;
onPortAvailable(process.debugPort, function() {
browserUnderDebug.validatePortAvailability_(process.debugPort).then(function(firstTime) {
onStartFn(firstTime);

var args = [process.pid, process.debugPort];
if (browserUnderDebug.debuggerServerPort_) {
args.push(browserUnderDebug.debuggerServerPort_);
Expand Down Expand Up @@ -1038,7 +1063,19 @@ Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort)
*/
Protractor.prototype.enterRepl = function(opt_debugPort) {
var debuggerClientPath = __dirname + '/debugger/clients/explorer.js';
this.initDebugger_(debuggerClientPath, opt_debugPort);
var onStartFn = function() {
log.puts();
log.puts('------- Element Explorer -------');
log.puts('Starting WebDriver debugger in a child process. Element ' +
'Explorer is still beta, please report issues at ' +
'github.com/angular/protractor');
log.puts();
log.puts('Type <tab> to see a list of locator strategies.');
log.puts('Use the `list` helper function to find elements by strategy:');
log.puts(' e.g., list(by.binding(\'\')) gets all bindings.');
log.puts();
};
this.initDebugger_(debuggerClientPath, onStartFn, opt_debugPort);
};

/**
Expand All @@ -1059,7 +1096,23 @@ Protractor.prototype.enterRepl = function(opt_debugPort) {
*/
Protractor.prototype.pause = function(opt_debugPort) {
var debuggerClientPath = __dirname + '/debugger/clients/wddebugger.js';
this.initDebugger_(debuggerClientPath, opt_debugPort);
var onStartFn = function(firstTime) {
log.puts();
log.puts('Encountered browser.pause(). Attaching debugger...');
if (firstTime) {
log.puts();
log.puts('------- WebDriver Debugger -------');
log.puts('Starting WebDriver debugger in a child process. Pause is ' +
'still beta, please report issues at github.com/angular/protractor');
log.puts();
log.puts('press c to continue to the next webdriver command');
log.puts('press ^D to detach debugger and resume code execution');
log.puts('type "repl" to enter interactive mode');
log.puts('type "exit" to break out of interactive mode');
log.puts();
}
};
this.initDebugger_(debuggerClientPath, onStartFn, opt_debugPort);
};

/**
Expand Down

0 comments on commit b110ed9

Please sign in to comment.