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

Commit b110ed9

Browse files
committed
feat(debugger): allow multiple browser.pause()
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.
1 parent 148f020 commit b110ed9

File tree

4 files changed

+93
-71
lines changed

4 files changed

+93
-71
lines changed

lib/debugger/clients/explorer.js

+5-15
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,6 @@ var WdRepl = function() {
1212
this.client;
1313
};
1414

15-
/**
16-
* Initiate debugger client.
17-
* @private
18-
*/
19-
WdRepl.prototype.initClient_ = function() {
20-
this.client =
21-
debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
22-
};
23-
2415
/**
2516
* Instantiate a server to handle IO.
2617
* @param {number} port The port to start the server.
@@ -143,12 +134,11 @@ WdRepl.prototype.initReplOrServer_ = function() {
143134
* @public
144135
*/
145136
WdRepl.prototype.init = function() {
146-
console.log('Type <tab> to see a list of locator strategies.');
147-
console.log('Use the `list` helper function to find elements by strategy:');
148-
console.log(' e.g., list(by.binding(\'\')) gets all bindings.');
149-
150-
this.initClient_();
151-
this.initReplOrServer_();
137+
var self = this;
138+
this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
139+
this.client.once('ready', function() {
140+
self.initReplOrServer_();
141+
});
152142
};
153143

154144
var wdRepl = new WdRepl();

lib/debugger/clients/wddebugger.js

+6-22
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,6 @@ var WdDebugger = function() {
2121
this.currentRepl;
2222
};
2323

24-
/**
25-
* Initiate debugger client.
26-
* @private
27-
*/
28-
WdDebugger.prototype.initClient_ = function() {
29-
this.client =
30-
debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
31-
this.client.once('ready', function() {
32-
console.log(' ready\n');
33-
console.log('press c to continue to the next webdriver command');
34-
console.log('press d to continue to the next debugger statement');
35-
console.log('type "repl" to enter interactive mode');
36-
console.log('type "exit" to break out of interactive mode');
37-
console.log('press ^C to exit');
38-
console.log();
39-
});
40-
};
41-
4224
/**
4325
* Eval function for processing a single step in repl.
4426
* @private
@@ -116,7 +98,7 @@ WdDebugger.prototype.initRepl_ = function() {
11698
self.replServer.complete = self.currentRepl.complete.bind(self.currentRepl);
11799

118100
self.replServer.on('exit', function() {
119-
console.log('Exiting debugger.');
101+
console.log('Resuming code execution');
120102
self.client.req({command: 'disconnect'}, function() {
121103
// Intentionally blank.
122104
});
@@ -129,9 +111,11 @@ WdDebugger.prototype.initRepl_ = function() {
129111
* @public
130112
*/
131113
WdDebugger.prototype.init = function() {
132-
console.log('------- WebDriver Debugger -------');
133-
this.initClient_();
134-
this.initRepl_();
114+
var self = this;
115+
this.client = debuggerCommons.attachDebugger(process.argv[2], process.argv[3]);
116+
this.client.once('ready', function() {
117+
self.initRepl_();
118+
});
135119
};
136120

137121
var wdDebugger = new WdDebugger();

lib/debugger/modes/debuggerRepl.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var util = require('util');
22

33
var DBG_INITIAL_SUGGESTIONS =
4-
['repl', 'c', 'frame', 'scopes', 'scripts', 'source', 'backtrace', 'd'];
4+
['repl', 'c', 'frame', 'scopes', 'scripts', 'source', 'backtrace'];
55

66
/**
77
* Repl to step through code.
@@ -60,11 +60,6 @@ DebuggerRepl.prototype.stepEval = function(cmd, callback) {
6060
callback();
6161
});
6262
break;
63-
case 'd':
64-
this.client.req({command: 'disconnect'}, function() {
65-
// Intentionally blank.
66-
});
67-
break;
6863
default:
6964
console.log('Unrecognized command.');
7065
callback();

lib/protractor.js

+81-28
Original file line numberDiff line numberDiff line change
@@ -824,17 +824,62 @@ Protractor.prototype.debugger = function() {
824824
'add breakpoint to control flow');
825825
};
826826

827+
/**
828+
* Validates that the port is free to use. This will only validate the first
829+
* time it is called. The reason is that on subsequent calls, the port will
830+
* already be bound to the debugger, so it will not be available, but that is
831+
* okay.
832+
*
833+
* @return {Promise<boolean>} A promise that becomes ready when the validation
834+
* is done. The promise will resolve to a boolean which represents whether
835+
* this is the first time that the debugger is called.
836+
*/
837+
Protractor.prototype.validatePortAvailability_ = function(port) {
838+
if (this.debuggerValidated_) {
839+
return webdriver.promise.fulfilled(false);
840+
}
841+
this.debuggerValidated_ = true;
842+
843+
var doneDeferred = webdriver.promise.defer();
844+
845+
// Resolve doneDeferred if port is available.
846+
var net = require('net');
847+
var tester = net.connect({port: port}, function() {
848+
doneDeferred.reject('Port ' + port +
849+
' is already in use. Please specify ' + 'another port to debug.');
850+
});
851+
tester.once('error', function (err) {
852+
if (err.code === 'ECONNREFUSED') {
853+
tester.once('close', function() {
854+
doneDeferred.fulfill(true);
855+
}).end();
856+
} else {
857+
doneDeferred.reject(
858+
'Unexpected failure testing for port ' + port + ': ' +
859+
JSON.stringify(err));
860+
}
861+
});
862+
863+
return doneDeferred.then(null, function(err) {
864+
console.error(err);
865+
process.exit(1);
866+
});
867+
},
868+
827869
/**
828870
* Helper function to:
829871
* 1) Set up helper functions for debugger clients to call on (e.g.
830872
* getControlFlowText, execute code, get autocompletion).
831873
* 2) Enter process into debugger mode. (i.e. process._debugProcess).
832874
* 3) Invoke the debugger client specified by debuggerClientPath.
833875
*
834-
* @param {string=} debuggerClientPath Absolute path of debugger client to use
876+
* @param {string} debuggerClientPath Absolute path of debugger client to use
877+
* @param {Function} onStartFn Function to call when the debugger starts. The
878+
* function takes a single parameter, which represents whether this is the
879+
* first time that the debugger is called.
835880
* @param {number=} opt_debugPort Optional port to use for the debugging process
836881
*/
837-
Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort) {
882+
Protractor.prototype.initDebugger_ = function(debuggerClientPath, onStartFn, opt_debugPort) {
838883
// Patch in a function to help us visualize what's going on in the control
839884
// flow.
840885
webdriver.promise.ControlFlow.prototype.getControlFlowText = function() {
@@ -847,26 +892,6 @@ Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort)
847892
// hackier, and involves messing with the control flow's internals / private
848893
// variables.
849894
return helper.filterStackTrace(controlFlowText);
850-
851-
};
852-
853-
// Invoke fn if port is available.
854-
var onPortAvailable = function(port, fn) {
855-
var net = require('net');
856-
var tester = net.connect({port: port}, function() {
857-
console.error('Port ' + port + ' is already in use. Please specify ' +
858-
'another port to debug.');
859-
process.exit(1);
860-
});
861-
tester.once('error', function (err) {
862-
if (err.code === 'ECONNREFUSED') {
863-
tester.once('close', fn).end();
864-
} else {
865-
console.error('Unexpected failure testing for port ' + port + ': ',
866-
err);
867-
process.exit(1);
868-
}
869-
});
870895
};
871896

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

893918
var browserUnderDebug = this;
894919
var debuggerReadyPromise = webdriver.promise.defer();
895-
flow.execute(function() {
896-
log.puts('Starting WebDriver debugger in a child process. Pause is ' +
897-
'still beta, please report issues at github.com/angular/protractor\n');
920+
flow.execute(function() {
898921
process.debugPort = opt_debugPort || process.debugPort;
899-
onPortAvailable(process.debugPort, function() {
922+
browserUnderDebug.validatePortAvailability_(process.debugPort).then(function(firstTime) {
923+
onStartFn(firstTime);
924+
900925
var args = [process.pid, process.debugPort];
901926
if (browserUnderDebug.debuggerServerPort_) {
902927
args.push(browserUnderDebug.debuggerServerPort_);
@@ -1038,7 +1063,19 @@ Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort)
10381063
*/
10391064
Protractor.prototype.enterRepl = function(opt_debugPort) {
10401065
var debuggerClientPath = __dirname + '/debugger/clients/explorer.js';
1041-
this.initDebugger_(debuggerClientPath, opt_debugPort);
1066+
var onStartFn = function() {
1067+
log.puts();
1068+
log.puts('------- Element Explorer -------');
1069+
log.puts('Starting WebDriver debugger in a child process. Element ' +
1070+
'Explorer is still beta, please report issues at ' +
1071+
'github.com/angular/protractor');
1072+
log.puts();
1073+
log.puts('Type <tab> to see a list of locator strategies.');
1074+
log.puts('Use the `list` helper function to find elements by strategy:');
1075+
log.puts(' e.g., list(by.binding(\'\')) gets all bindings.');
1076+
log.puts();
1077+
};
1078+
this.initDebugger_(debuggerClientPath, onStartFn, opt_debugPort);
10421079
};
10431080

10441081
/**
@@ -1059,7 +1096,23 @@ Protractor.prototype.enterRepl = function(opt_debugPort) {
10591096
*/
10601097
Protractor.prototype.pause = function(opt_debugPort) {
10611098
var debuggerClientPath = __dirname + '/debugger/clients/wddebugger.js';
1062-
this.initDebugger_(debuggerClientPath, opt_debugPort);
1099+
var onStartFn = function(firstTime) {
1100+
log.puts();
1101+
log.puts('Encountered browser.pause(). Attaching debugger...');
1102+
if (firstTime) {
1103+
log.puts();
1104+
log.puts('------- WebDriver Debugger -------');
1105+
log.puts('Starting WebDriver debugger in a child process. Pause is ' +
1106+
'still beta, please report issues at github.com/angular/protractor');
1107+
log.puts();
1108+
log.puts('press c to continue to the next webdriver command');
1109+
log.puts('press ^D to detach debugger and resume code execution');
1110+
log.puts('type "repl" to enter interactive mode');
1111+
log.puts('type "exit" to break out of interactive mode');
1112+
log.puts();
1113+
}
1114+
};
1115+
this.initDebugger_(debuggerClientPath, onStartFn, opt_debugPort);
10631116
};
10641117

10651118
/**

0 commit comments

Comments
 (0)