##// END OF EJS Templates
wait for new-notebook button to arrive...
Min RK -
Show More
@@ -1,831 +1,832
1 1 //
2 2 // Utility functions for the HTML notebook's CasperJS tests.
3 3 //
4 4 casper.get_notebook_server = function () {
5 5 // Get the URL of a notebook server on which to run tests.
6 6 var port = casper.cli.get("port");
7 7 port = (typeof port === 'undefined') ? '8888' : port;
8 8 return casper.cli.get("url") || ('http://127.0.0.1:' + port);
9 9 };
10 10
11 11 casper.open_new_notebook = function () {
12 12 // Create and open a new notebook.
13 13 var baseUrl = this.get_notebook_server();
14 14 this.start(baseUrl);
15 15 this.waitFor(this.page_loaded);
16 this.waitForSelector('#kernel-python2 a, #kernel-python3 a');
16 17 this.thenClick('#kernel-python2 a, #kernel-python3 a');
17 18
18 19 this.waitForPopup('');
19 20
20 21 this.withPopup('', function () {this.waitForSelector('.CodeMirror-code');});
21 22 this.then(function () {
22 23 this.open(this.popups[0].url);
23 24 });
24 25 this.waitFor(this.page_loaded);
25 26
26 27 // Hook the log and error methods of the console, forcing them to
27 28 // serialize their arguments before printing. This allows the
28 29 // Objects to cross into the phantom/slimer regime for display.
29 30 this.thenEvaluate(function(){
30 31 var serialize_arguments = function(f, context) {
31 32 return function() {
32 33 var pretty_arguments = [];
33 34 for (var i = 0; i < arguments.length; i++) {
34 35 var value = arguments[i];
35 36 if (value instanceof Object) {
36 37 var name = value.name || 'Object';
37 38 // Print a JSON string representation of the object.
38 39 // If we don't do this, [Object object] gets printed
39 40 // by casper, which is useless. The long regular
40 41 // expression reduces the verbosity of the JSON.
41 42 pretty_arguments.push(name + ' {' + JSON.stringify(value, null, ' ')
42 43 .replace(/(\s+)?({)?(\s+)?(}(\s+)?,?)?(\s+)?(\s+)?\n/g, '\n')
43 44 .replace(/\n(\s+)?\n/g, '\n'));
44 45 } else {
45 46 pretty_arguments.push(value);
46 47 }
47 48 }
48 49 f.apply(context, pretty_arguments);
49 50 };
50 51 };
51 52 console.log = serialize_arguments(console.log, console);
52 53 console.error = serialize_arguments(console.error, console);
53 54 });
54 55
55 56 // Make sure the kernel has started
56 57 this.waitFor(this.kernel_running);
57 58 // track the IPython busy/idle state
58 59 this.thenEvaluate(function () {
59 60 require(['base/js/namespace', 'base/js/events'], function (IPython, events) {
60 61
61 62 events.on('kernel_idle.Kernel',function () {
62 63 IPython._status = 'idle';
63 64 });
64 65 events.on('kernel_busy.Kernel',function () {
65 66 IPython._status = 'busy';
66 67 });
67 68 });
68 69 });
69 70
70 71 // Because of the asynchronous nature of SlimerJS (Gecko), we need to make
71 72 // sure the notebook has actually been loaded into the IPython namespace
72 73 // before running any tests.
73 74 this.waitFor(function() {
74 75 return this.evaluate(function () {
75 76 return IPython.notebook;
76 77 });
77 78 });
78 79 };
79 80
80 81 casper.page_loaded = function() {
81 82 // Return whether or not the kernel is running.
82 83 return this.evaluate(function() {
83 84 return typeof IPython !== "undefined" &&
84 85 IPython.page !== undefined;
85 86 });
86 87 };
87 88
88 89 casper.kernel_running = function() {
89 90 // Return whether or not the kernel is running.
90 91 return this.evaluate(function() {
91 92 return IPython &&
92 93 IPython.notebook &&
93 94 IPython.notebook.kernel &&
94 95 IPython.notebook.kernel.is_connected();
95 96 });
96 97 };
97 98
98 99 casper.kernel_disconnected = function() {
99 100 return this.evaluate(function() {
100 101 return IPython.notebook.kernel.is_fully_disconnected();
101 102 });
102 103 };
103 104
104 105 casper.wait_for_kernel_ready = function () {
105 106 this.waitFor(this.kernel_running);
106 107 this.thenEvaluate(function () {
107 108 IPython._kernel_ready = false;
108 109 IPython.notebook.kernel.kernel_info(
109 110 function () {
110 111 IPython._kernel_ready = true;
111 112 });
112 113 });
113 114 this.waitFor(function () {
114 115 return this.evaluate(function () {
115 116 return IPython._kernel_ready;
116 117 });
117 118 });
118 119 };
119 120
120 121 casper.shutdown_current_kernel = function () {
121 122 // Shut down the current notebook's kernel.
122 123 this.thenEvaluate(function() {
123 124 IPython.notebook.session.delete();
124 125 });
125 126 // We close the page right after this so we need to give it time to complete.
126 127 this.wait(1000);
127 128 };
128 129
129 130 casper.delete_current_notebook = function () {
130 131 // Delete created notebook.
131 132
132 133 // For some unknown reason, this doesn't work?!?
133 134 this.thenEvaluate(function() {
134 135 IPython.notebook.delete();
135 136 });
136 137 };
137 138
138 139 casper.wait_for_busy = function () {
139 140 // Waits for the notebook to enter a busy state.
140 141 this.waitFor(function () {
141 142 return this.evaluate(function () {
142 143 return IPython._status == 'busy';
143 144 });
144 145 });
145 146 };
146 147
147 148 casper.wait_for_idle = function () {
148 149 // Waits for the notebook to idle.
149 150 this.waitFor(function () {
150 151 return this.evaluate(function () {
151 152 return IPython._status == 'idle';
152 153 });
153 154 });
154 155 };
155 156
156 157 casper.wait_for_output = function (cell_num, out_num) {
157 158 // wait for the nth output in a given cell
158 159 this.wait_for_idle();
159 160 out_num = out_num || 0;
160 161 this.then(function() {
161 162 this.waitFor(function (c, o) {
162 163 return this.evaluate(function get_output(c, o) {
163 164 var cell = IPython.notebook.get_cell(c);
164 165 return cell.output_area.outputs.length > o;
165 166 },
166 167 // pass parameter from the test suite js to the browser code js
167 168 {c : cell_num, o : out_num});
168 169 });
169 170 },
170 171 function then() { },
171 172 function timeout() {
172 173 this.echo("wait_for_output timed out!");
173 174 });
174 175 };
175 176
176 177 casper.wait_for_widget = function (widget_info) {
177 178 // wait for a widget msg que to reach 0
178 179 //
179 180 // Parameters
180 181 // ----------
181 182 // widget_info : object
182 183 // Object which contains info related to the widget. The model_id property
183 184 // is used to identify the widget.
184 185
185 186 // Clear the results of a previous query, if they exist. Make sure a
186 187 // dictionary exists to store the async results in.
187 188 this.thenEvaluate(function(model_id) {
188 189 if (window.pending_msgs === undefined) {
189 190 window.pending_msgs = {};
190 191 } else {
191 192 window.pending_msgs[model_id] = -1;
192 193 }
193 194 }, {model_id: widget_info.model_id});
194 195
195 196 // Wait for the pending messages to be 0.
196 197 this.waitFor(function () {
197 198 var pending = this.evaluate(function (model_id) {
198 199
199 200 // Get the model. Once the model is had, store it's pending_msgs
200 201 // count in the window's dictionary.
201 202 IPython.notebook.kernel.widget_manager.get_model(model_id)
202 203 .then(function(model) {
203 204 window.pending_msgs[model_id] = model.pending_msgs;
204 205 });
205 206
206 207 // Return the pending_msgs result.
207 208 return window.pending_msgs[model_id];
208 209 }, {model_id: widget_info.model_id});
209 210
210 211 if (pending === 0) {
211 212 return true;
212 213 } else {
213 214 return false;
214 215 }
215 216 });
216 217 };
217 218
218 219 casper.get_output_cell = function (cell_num, out_num) {
219 220 // return an output of a given cell
220 221 out_num = out_num || 0;
221 222 var result = casper.evaluate(function (c, o) {
222 223 var cell = IPython.notebook.get_cell(c);
223 224 return cell.output_area.outputs[o];
224 225 },
225 226 {c : cell_num, o : out_num});
226 227 if (!result) {
227 228 var num_outputs = casper.evaluate(function (c) {
228 229 var cell = IPython.notebook.get_cell(c);
229 230 return cell.output_area.outputs.length;
230 231 },
231 232 {c : cell_num});
232 233 this.test.assertTrue(false,
233 234 "Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
234 235 );
235 236 } else {
236 237 return result;
237 238 }
238 239 };
239 240
240 241 casper.get_cells_length = function () {
241 242 // return the number of cells in the notebook
242 243 var result = casper.evaluate(function () {
243 244 return IPython.notebook.get_cells().length;
244 245 });
245 246 return result;
246 247 };
247 248
248 249 casper.set_cell_text = function(index, text){
249 250 // Set the text content of a cell.
250 251 this.evaluate(function (index, text) {
251 252 var cell = IPython.notebook.get_cell(index);
252 253 cell.set_text(text);
253 254 }, index, text);
254 255 };
255 256
256 257 casper.get_cell_text = function(index){
257 258 // Get the text content of a cell.
258 259 return this.evaluate(function (index) {
259 260 var cell = IPython.notebook.get_cell(index);
260 261 return cell.get_text();
261 262 }, index);
262 263 };
263 264
264 265 casper.insert_cell_at_bottom = function(cell_type){
265 266 // Inserts a cell at the bottom of the notebook
266 267 // Returns the new cell's index.
267 268 return this.evaluate(function (cell_type) {
268 269 var cell = IPython.notebook.insert_cell_at_bottom(cell_type);
269 270 return IPython.notebook.find_cell_index(cell);
270 271 }, cell_type);
271 272 };
272 273
273 274 casper.append_cell = function(text, cell_type) {
274 275 // Insert a cell at the bottom of the notebook and set the cells text.
275 276 // Returns the new cell's index.
276 277 var index = this.insert_cell_at_bottom(cell_type);
277 278 if (text !== undefined) {
278 279 this.set_cell_text(index, text);
279 280 }
280 281 return index;
281 282 };
282 283
283 284 casper.execute_cell = function(index, expect_failure){
284 285 // Asynchronously executes a cell by index.
285 286 // Returns the cell's index.
286 287
287 288 if (expect_failure === undefined) expect_failure = false;
288 289 var that = this;
289 290 this.then(function(){
290 291 that.evaluate(function (index) {
291 292 var cell = IPython.notebook.get_cell(index);
292 293 cell.execute();
293 294 }, index);
294 295 });
295 296 this.wait_for_idle();
296 297
297 298 this.then(function () {
298 299 var error = that.evaluate(function (index) {
299 300 var cell = IPython.notebook.get_cell(index);
300 301 var outputs = cell.output_area.outputs;
301 302 for (var i = 0; i < outputs.length; i++) {
302 303 if (outputs[i].output_type == 'error') {
303 304 return outputs[i];
304 305 }
305 306 }
306 307 return false;
307 308 }, index);
308 309 if (error === null) {
309 310 this.test.fail("Failed to check for error output");
310 311 }
311 312 if (expect_failure && error === false) {
312 313 this.test.fail("Expected error while running cell");
313 314 } else if (!expect_failure && error !== false) {
314 315 this.test.fail("Error running cell:\n" + error.traceback.join('\n'));
315 316 }
316 317 });
317 318 return index;
318 319 };
319 320
320 321 casper.execute_cell_then = function(index, then_callback, expect_failure) {
321 322 // Synchronously executes a cell by index.
322 323 // Optionally accepts a then_callback parameter. then_callback will get called
323 324 // when the cell has finished executing.
324 325 // Returns the cell's index.
325 326 var return_val = this.execute_cell(index, expect_failure);
326 327
327 328 this.wait_for_idle();
328 329
329 330 var that = this;
330 331 this.then(function(){
331 332 if (then_callback!==undefined) {
332 333 then_callback.apply(that, [index]);
333 334 }
334 335 });
335 336
336 337 return return_val;
337 338 };
338 339
339 340 casper.wait_for_element = function(index, selector){
340 341 // Utility function that allows us to easily wait for an element
341 342 // within a cell. Uses JQuery selector to look for the element.
342 343 var that = this;
343 344 this.waitFor(function() {
344 345 return that.cell_element_exists(index, selector);
345 346 });
346 347 };
347 348
348 349 casper.cell_element_exists = function(index, selector){
349 350 // Utility function that allows us to easily check if an element exists
350 351 // within a cell. Uses JQuery selector to look for the element.
351 352 return casper.evaluate(function (index, selector) {
352 353 var $cell = IPython.notebook.get_cell(index).element;
353 354 return $cell.find(selector).length > 0;
354 355 }, index, selector);
355 356 };
356 357
357 358 casper.cell_element_function = function(index, selector, function_name, function_args){
358 359 // Utility function that allows us to execute a jQuery function on an
359 360 // element within a cell.
360 361 return casper.evaluate(function (index, selector, function_name, function_args) {
361 362 var $cell = IPython.notebook.get_cell(index).element;
362 363 var $el = $cell.find(selector);
363 364 return $el[function_name].apply($el, function_args);
364 365 }, index, selector, function_name, function_args);
365 366 };
366 367
367 368 casper.validate_notebook_state = function(message, mode, cell_index) {
368 369 // Validate the entire dual mode state of the notebook. Make sure no more than
369 370 // one cell is selected, focused, in edit mode, etc...
370 371
371 372 // General tests.
372 373 this.test.assertEquals(this.get_keyboard_mode(), this.get_notebook_mode(),
373 374 message + '; keyboard and notebook modes match');
374 375 // Is the selected cell the only cell that is selected?
375 376 if (cell_index!==undefined) {
376 377 this.test.assert(this.is_only_cell_selected(cell_index),
377 378 message + '; cell ' + cell_index + ' is the only cell selected');
378 379 }
379 380
380 381 // Mode specific tests.
381 382 if (mode==='command') {
382 383 // Are the notebook and keyboard manager in command mode?
383 384 this.test.assertEquals(this.get_keyboard_mode(), 'command',
384 385 message + '; in command mode');
385 386 // Make sure there isn't a single cell in edit mode.
386 387 this.test.assert(this.is_only_cell_edit(null),
387 388 message + '; all cells in command mode');
388 389 this.test.assert(this.is_cell_editor_focused(null),
389 390 message + '; no cell editors are focused while in command mode');
390 391
391 392 } else if (mode==='edit') {
392 393 // Are the notebook and keyboard manager in edit mode?
393 394 this.test.assertEquals(this.get_keyboard_mode(), 'edit',
394 395 message + '; in edit mode');
395 396 if (cell_index!==undefined) {
396 397 // Is the specified cell the only cell in edit mode?
397 398 this.test.assert(this.is_only_cell_edit(cell_index),
398 399 message + '; cell ' + cell_index + ' is the only cell in edit mode '+ this.cells_modes());
399 400 // Is the specified cell the only cell with a focused code mirror?
400 401 this.test.assert(this.is_cell_editor_focused(cell_index),
401 402 message + '; cell ' + cell_index + '\'s editor is appropriately focused');
402 403 }
403 404
404 405 } else {
405 406 this.test.assert(false, message + '; ' + mode + ' is an unknown mode');
406 407 }
407 408 };
408 409
409 410 casper.select_cell = function(index) {
410 411 // Select a cell in the notebook.
411 412 this.evaluate(function (i) {
412 413 IPython.notebook.select(i);
413 414 }, {i: index});
414 415 };
415 416
416 417 casper.click_cell_editor = function(index) {
417 418 // Emulate a click on a cell's editor.
418 419
419 420 // Code Mirror does not play nicely with emulated brower events.
420 421 // Instead of trying to emulate a click, here we run code similar to
421 422 // the code used in Code Mirror that handles the mousedown event on a
422 423 // region of codemirror that the user can focus.
423 424 this.evaluate(function (i) {
424 425 var cm = IPython.notebook.get_cell(i).code_mirror;
425 426 if (cm.options.readOnly != "nocursor" && (document.activeElement != cm.display.input)){
426 427 cm.display.input.focus();
427 428 }
428 429 }, {i: index});
429 430 };
430 431
431 432 casper.set_cell_editor_cursor = function(index, line_index, char_index) {
432 433 // Set the Code Mirror instance cursor's location.
433 434 this.evaluate(function (i, l, c) {
434 435 IPython.notebook.get_cell(i).code_mirror.setCursor(l, c);
435 436 }, {i: index, l: line_index, c: char_index});
436 437 };
437 438
438 439 casper.focus_notebook = function() {
439 440 // Focus the notebook div.
440 441 this.evaluate(function (){
441 442 $('#notebook').focus();
442 443 }, {});
443 444 };
444 445
445 446 casper.trigger_keydown = function() {
446 447 // Emulate a keydown in the notebook.
447 448 for (var i = 0; i < arguments.length; i++) {
448 449 this.evaluate(function (k) {
449 450 var element = $(document);
450 451 var event = IPython.keyboard.shortcut_to_event(k, 'keydown');
451 452 element.trigger(event);
452 453 }, {k: arguments[i]});
453 454 }
454 455 };
455 456
456 457 casper.get_keyboard_mode = function() {
457 458 // Get the mode of the keyboard manager.
458 459 return this.evaluate(function() {
459 460 return IPython.keyboard_manager.mode;
460 461 }, {});
461 462 };
462 463
463 464 casper.get_notebook_mode = function() {
464 465 // Get the mode of the notebook.
465 466 return this.evaluate(function() {
466 467 return IPython.notebook.mode;
467 468 }, {});
468 469 };
469 470
470 471 casper.get_cell = function(index) {
471 472 // Get a single cell.
472 473 //
473 474 // Note: Handles to DOM elements stored in the cell will be useless once in
474 475 // CasperJS context.
475 476 return this.evaluate(function(i) {
476 477 var cell = IPython.notebook.get_cell(i);
477 478 if (cell) {
478 479 return cell;
479 480 }
480 481 return null;
481 482 }, {i : index});
482 483 };
483 484
484 485 casper.is_cell_editor_focused = function(index) {
485 486 // Make sure a cell's editor is the only editor focused on the page.
486 487 return this.evaluate(function(i) {
487 488 var focused_textarea = $('#notebook .CodeMirror-focused textarea');
488 489 if (focused_textarea.length > 1) { throw 'More than one Code Mirror editor is focused at once!'; }
489 490 if (i === null) {
490 491 return focused_textarea.length === 0;
491 492 } else {
492 493 var cell = IPython.notebook.get_cell(i);
493 494 if (cell) {
494 495 return cell.code_mirror.getInputField() == focused_textarea[0];
495 496 }
496 497 }
497 498 return false;
498 499 }, {i : index});
499 500 };
500 501
501 502 casper.is_only_cell_selected = function(index) {
502 503 // Check if a cell is the only cell selected.
503 504 // Pass null as the index to check if no cells are selected.
504 505 return this.is_only_cell_on(index, 'selected', 'unselected');
505 506 };
506 507
507 508 casper.is_only_cell_edit = function(index) {
508 509 // Check if a cell is the only cell in edit mode.
509 510 // Pass null as the index to check if all of the cells are in command mode.
510 511 var cells_length = this.get_cells_length();
511 512 for (var j = 0; j < cells_length; j++) {
512 513 if (j === index) {
513 514 if (!this.cell_mode_is(j, 'edit')) {
514 515 return false;
515 516 }
516 517 } else {
517 518 if (this.cell_mode_is(j, 'edit')) {
518 519 return false;
519 520 }
520 521 }
521 522 }
522 523 return true;
523 524 };
524 525
525 526 casper.is_only_cell_on = function(i, on_class, off_class) {
526 527 // Check if a cell is the only cell with the `on_class` DOM class applied to it.
527 528 // All of the other cells are checked for the `off_class` DOM class.
528 529 // Pass null as the index to check if all of the cells have the `off_class`.
529 530 var cells_length = this.get_cells_length();
530 531 for (var j = 0; j < cells_length; j++) {
531 532 if (j === i) {
532 533 if (this.cell_has_class(j, off_class) || !this.cell_has_class(j, on_class)) {
533 534 return false;
534 535 }
535 536 } else {
536 537 if (!this.cell_has_class(j, off_class) || this.cell_has_class(j, on_class)) {
537 538 return false;
538 539 }
539 540 }
540 541 }
541 542 return true;
542 543 };
543 544
544 545 casper.cells_modes = function(){
545 546 return this.evaluate(function(){
546 547 return IPython.notebook.get_cells().map(function(x,c){return x.mode})
547 548 }, {});
548 549 };
549 550
550 551 casper.cell_mode_is = function(index, mode) {
551 552 // Check if a cell is in a specific mode
552 553 return this.evaluate(function(i, m) {
553 554 var cell = IPython.notebook.get_cell(i);
554 555 if (cell) {
555 556 return cell.mode === m;
556 557 }
557 558 return false;
558 559 }, {i : index, m: mode});
559 560 };
560 561
561 562
562 563 casper.cell_has_class = function(index, classes) {
563 564 // Check if a cell has a class.
564 565 return this.evaluate(function(i, c) {
565 566 var cell = IPython.notebook.get_cell(i);
566 567 if (cell) {
567 568 return cell.element.hasClass(c);
568 569 }
569 570 return false;
570 571 }, {i : index, c: classes});
571 572 };
572 573
573 574 casper.is_cell_rendered = function (index) {
574 575 return this.evaluate(function(i) {
575 576 return !!IPython.notebook.get_cell(i).rendered;
576 577 }, {i:index});
577 578 };
578 579
579 580 casper.assert_colors_equal = function (hex_color, local_color, msg) {
580 581 // Tests to see if two colors are equal.
581 582 //
582 583 // Parameters
583 584 // hex_color: string
584 585 // Hexadecimal color code, with or without preceeding hash character.
585 586 // local_color: string
586 587 // Local color representation. Can either be hexadecimal (default for
587 588 // phantom) or rgb (default for slimer).
588 589
589 590 // Remove parentheses, hashes, semi-colons, and space characters.
590 591 hex_color = hex_color.replace(/[\(\); #]/, '');
591 592 local_color = local_color.replace(/[\(\); #]/, '');
592 593
593 594 // If the local color is rgb, clean it up and replace
594 595 if (local_color.substr(0,3).toLowerCase() == 'rgb') {
595 596 var components = local_color.substr(3).split(',');
596 597 local_color = '';
597 598 for (var i = 0; i < components.length; i++) {
598 599 var part = parseInt(components[i]).toString(16);
599 600 while (part.length < 2) part = '0' + part;
600 601 local_color += part;
601 602 }
602 603 }
603 604
604 605 this.test.assertEquals(hex_color.toUpperCase(), local_color.toUpperCase(), msg);
605 606 };
606 607
607 608 casper.notebook_test = function(test) {
608 609 // Wrap a notebook test to reduce boilerplate.
609 610 this.open_new_notebook();
610 611
611 612 // Echo whether or not we are running this test using SlimerJS
612 613 if (this.evaluate(function(){
613 614 return typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
614 615 })) {
615 616 console.log('This test is running in SlimerJS.');
616 617 this.slimerjs = true;
617 618 }
618 619
619 620 // Make sure to remove the onbeforeunload callback. This callback is
620 621 // responsible for the "Are you sure you want to quit?" type messages.
621 622 // PhantomJS ignores these prompts, SlimerJS does not which causes hangs.
622 623 this.then(function(){
623 624 this.evaluate(function(){
624 625 window.onbeforeunload = function(){};
625 626 });
626 627 });
627 628
628 629 this.then(test);
629 630
630 631 // Kill the kernel and delete the notebook.
631 632 this.shutdown_current_kernel();
632 633 // This is still broken but shouldn't be a problem for now.
633 634 // this.delete_current_notebook();
634 635
635 636 // This is required to clean up the page we just finished with. If we don't call this
636 637 // casperjs will leak file descriptors of all the open WebSockets in that page. We
637 638 // have to set this.page=null so that next time casper.start runs, it will create a
638 639 // new page from scratch.
639 640 this.then(function () {
640 641 this.page.close();
641 642 this.page = null;
642 643 });
643 644
644 645 // Run the browser automation.
645 646 this.run(function() {
646 647 this.test.done();
647 648 });
648 649 };
649 650
650 651 casper.wait_for_dashboard = function () {
651 652 // Wait for the dashboard list to load.
652 653 casper.waitForSelector('.list_item');
653 654 };
654 655
655 656 casper.open_dashboard = function () {
656 657 // Start casper by opening the dashboard page.
657 658 var baseUrl = this.get_notebook_server();
658 659 this.start(baseUrl);
659 660 this.waitFor(this.page_loaded);
660 661 this.wait_for_dashboard();
661 662 };
662 663
663 664 casper.dashboard_test = function (test) {
664 665 // Open the dashboard page and run a test.
665 666 this.open_dashboard();
666 667 this.then(test);
667 668
668 669 this.then(function () {
669 670 this.page.close();
670 671 this.page = null;
671 672 });
672 673
673 674 // Run the browser automation.
674 675 this.run(function() {
675 676 this.test.done();
676 677 });
677 678 };
678 679
679 680 // note that this will only work for UNIQUE events -- if you want to
680 681 // listen for the same event twice, this will not work!
681 682 casper.event_test = function (name, events, action, timeout) {
682 683
683 684 // set up handlers to listen for each of the events
684 685 this.thenEvaluate(function (events) {
685 686 var make_handler = function (event) {
686 687 return function () {
687 688 IPython._events_triggered.push(event);
688 689 IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
689 690 delete IPython._event_handlers[event];
690 691 };
691 692 };
692 693 IPython._event_handlers = {};
693 694 IPython._events_triggered = [];
694 695 for (var i=0; i < events.length; i++) {
695 696 IPython._event_handlers[events[i]] = make_handler(events[i]);
696 697 IPython.notebook.events.on(events[i], IPython._event_handlers[events[i]]);
697 698 }
698 699 }, [events]);
699 700
700 701 // execute the requested action
701 702 this.then(action);
702 703
703 704 // wait for all the events to be triggered
704 705 this.waitFor(function () {
705 706 return this.evaluate(function (events) {
706 707 return IPython._events_triggered.length >= events.length;
707 708 }, [events]);
708 709 }, undefined, undefined, timeout);
709 710
710 711 // test that the events were triggered in the proper order
711 712 this.then(function () {
712 713 var triggered = this.evaluate(function () {
713 714 return IPython._events_triggered;
714 715 });
715 716 var handlers = this.evaluate(function () {
716 717 return Object.keys(IPython._event_handlers);
717 718 });
718 719 this.test.assertEquals(triggered.length, events.length, name + ': ' + events.length + ' events were triggered');
719 720 this.test.assertEquals(handlers.length, 0, name + ': all handlers triggered');
720 721 for (var i=0; i < events.length; i++) {
721 722 this.test.assertEquals(triggered[i], events[i], name + ': ' + events[i] + ' was triggered');
722 723 }
723 724 });
724 725
725 726 // turn off any remaining event listeners
726 727 this.thenEvaluate(function () {
727 728 for (var event in IPython._event_handlers) {
728 729 IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
729 730 delete IPython._event_handlers[event];
730 731 }
731 732 });
732 733 };
733 734
734 735 casper.options.waitTimeout=10000;
735 736 casper.on('waitFor.timeout', function onWaitForTimeout(timeout) {
736 737 this.echo("Timeout for " + casper.get_notebook_server());
737 738 this.echo("Is the notebook server running?");
738 739 });
739 740
740 741 casper.print_log = function () {
741 742 // Pass `console.log` calls from page JS to casper.
742 743 this.on('remote.message', function(msg) {
743 744 this.echo('Remote message caught: ' + msg);
744 745 });
745 746 };
746 747
747 748 casper.on("page.error", function onError(msg, trace) {
748 749 // show errors in the browser
749 750 this.echo("Page Error");
750 751 this.echo(" Message: " + msg.split('\n').join('\n '));
751 752 this.echo(" Call stack:");
752 753 var local_path = this.get_notebook_server();
753 754 for (var i = 0; i < trace.length; i++) {
754 755 var frame = trace[i];
755 756 var file = frame.file;
756 757 // shorten common phantomjs evaluate url
757 758 // this will have a different value on slimerjs
758 759 if (file === "phantomjs://webpage.evaluate()") {
759 760 file = "evaluate";
760 761 }
761 762 // remove the version tag from the path
762 763 file = file.replace(/(\?v=[0-9abcdef]+)/, '');
763 764 // remove the local address from the beginning of the path
764 765 if (file.indexOf(local_path) === 0) {
765 766 file = file.substr(local_path.length);
766 767 }
767 768 var frame_text = (frame.function.length > 0) ? " in " + frame.function : "";
768 769 this.echo(" line " + frame.line + " of " + file + frame_text);
769 770 }
770 771 });
771 772
772 773
773 774 casper.capture_log = function () {
774 775 // show captured errors
775 776 var captured_log = [];
776 777 var seen_errors = 0;
777 778 this.on('remote.message', function(msg) {
778 779 captured_log.push(msg);
779 780 });
780 781
781 782 var that = this;
782 783 this.test.on("test.done", function (result) {
783 784 // test.done runs per-file,
784 785 // but suiteResults is per-suite (directory)
785 786 var current_errors;
786 787 if (this.suiteResults) {
787 788 // casper 1.1 has suiteResults
788 789 current_errors = this.suiteResults.countErrors() + this.suiteResults.countFailed();
789 790 } else {
790 791 // casper 1.0 has testResults instead
791 792 current_errors = this.testResults.failed;
792 793 }
793 794
794 795 if (current_errors > seen_errors && captured_log.length > 0) {
795 796 casper.echo("\nCaptured console.log:");
796 797 for (var i = 0; i < captured_log.length; i++) {
797 798 var output = String(captured_log[i]).split('\n');
798 799 for (var j = 0; j < output.length; j++) {
799 800 casper.echo(" " + output[j]);
800 801 }
801 802 }
802 803 }
803 804
804 805 seen_errors = current_errors;
805 806 captured_log = [];
806 807 });
807 808 };
808 809
809 810 casper.interact = function() {
810 811 // Start an interactive Javascript console.
811 812 var system = require('system');
812 813 system.stdout.writeLine('JS interactive console.');
813 814 system.stdout.writeLine('Type `exit` to quit.');
814 815
815 816 function read_line() {
816 817 system.stdout.writeLine('JS: ');
817 818 var line = system.stdin.readLine();
818 819 return line;
819 820 }
820 821
821 822 var input = read_line();
822 823 while (input.trim() != 'exit') {
823 824 var output = this.evaluate(function(code) {
824 825 return String(eval(code));
825 826 }, {code: input});
826 827 system.stdout.writeLine('\nOut: ' + output);
827 828 input = read_line();
828 829 }
829 830 };
830 831
831 832 casper.capture_log();
General Comments 0
You need to be logged in to leave comments. Login now