##// END OF EJS Templates
Some code cleanup in javascript and python...
Some code cleanup in javascript and python change patern that are prone to error, like function redifinition and other.

File last commit:

r19739:7c74a0d3
r19739:7c74a0d3
Show More
util.js
831 lines | 27.8 KiB | application/javascript | JavascriptLexer
//
// Utility functions for the HTML notebook's CasperJS tests.
//
casper.get_notebook_server = function () {
// Get the URL of a notebook server on which to run tests.
var port = casper.cli.get("port");
port = (typeof port === 'undefined') ? '8888' : port;
return casper.cli.get("url") || ('http://127.0.0.1:' + port);
};
casper.open_new_notebook = function () {
// Create and open a new notebook.
var baseUrl = this.get_notebook_server();
this.start(baseUrl);
this.waitFor(this.page_loaded);
this.thenClick('#kernel-python2 a, #kernel-python3 a');
this.waitForPopup('');
this.withPopup('', function () {this.waitForSelector('.CodeMirror-code');});
this.then(function () {
this.open(this.popups[0].url);
});
this.waitFor(this.page_loaded);
// Hook the log and error methods of the console, forcing them to
// serialize their arguments before printing. This allows the
// Objects to cross into the phantom/slimer regime for display.
this.thenEvaluate(function(){
var serialize_arguments = function(f, context) {
return function() {
var pretty_arguments = [];
for (var i = 0; i < arguments.length; i++) {
var value = arguments[i];
if (value instanceof Object) {
var name = value.name || 'Object';
// Print a JSON string representation of the object.
// If we don't do this, [Object object] gets printed
// by casper, which is useless. The long regular
// expression reduces the verbosity of the JSON.
pretty_arguments.push(name + ' {' + JSON.stringify(value, null, ' ')
.replace(/(\s+)?({)?(\s+)?(}(\s+)?,?)?(\s+)?(\s+)?\n/g, '\n')
.replace(/\n(\s+)?\n/g, '\n'));
} else {
pretty_arguments.push(value);
}
}
f.apply(context, pretty_arguments);
};
};
console.log = serialize_arguments(console.log, console);
console.error = serialize_arguments(console.error, console);
});
// Make sure the kernel has started
this.waitFor(this.kernel_running);
// track the IPython busy/idle state
this.thenEvaluate(function () {
require(['base/js/namespace', 'base/js/events'], function (IPython, events) {
events.on('kernel_idle.Kernel',function () {
IPython._status = 'idle';
});
events.on('kernel_busy.Kernel',function () {
IPython._status = 'busy';
});
});
});
// Because of the asynchronous nature of SlimerJS (Gecko), we need to make
// sure the notebook has actually been loaded into the IPython namespace
// before running any tests.
this.waitFor(function() {
return this.evaluate(function () {
return IPython.notebook;
});
});
};
casper.page_loaded = function() {
// Return whether or not the kernel is running.
return this.evaluate(function() {
return typeof IPython !== "undefined" &&
IPython.page !== undefined;
});
};
casper.kernel_running = function() {
// Return whether or not the kernel is running.
return this.evaluate(function() {
return IPython &&
IPython.notebook &&
IPython.notebook.kernel &&
IPython.notebook.kernel.is_connected();
});
};
casper.kernel_disconnected = function() {
return this.evaluate(function() {
return IPython.notebook.kernel.is_fully_disconnected();
});
};
casper.wait_for_kernel_ready = function () {
this.waitFor(this.kernel_running);
this.thenEvaluate(function () {
IPython._kernel_ready = false;
IPython.notebook.kernel.kernel_info(
function () {
IPython._kernel_ready = true;
});
});
this.waitFor(function () {
return this.evaluate(function () {
return IPython._kernel_ready;
});
});
};
casper.shutdown_current_kernel = function () {
// Shut down the current notebook's kernel.
this.thenEvaluate(function() {
IPython.notebook.session.delete();
});
// We close the page right after this so we need to give it time to complete.
this.wait(1000);
};
casper.delete_current_notebook = function () {
// Delete created notebook.
// For some unknown reason, this doesn't work?!?
this.thenEvaluate(function() {
IPython.notebook.delete();
});
};
casper.wait_for_busy = function () {
// Waits for the notebook to enter a busy state.
this.waitFor(function () {
return this.evaluate(function () {
return IPython._status == 'busy';
});
});
};
casper.wait_for_idle = function () {
// Waits for the notebook to idle.
this.waitFor(function () {
return this.evaluate(function () {
return IPython._status == 'idle';
});
});
};
casper.wait_for_output = function (cell_num, out_num) {
// wait for the nth output in a given cell
this.wait_for_idle();
out_num = out_num || 0;
this.then(function() {
this.waitFor(function (c, o) {
return this.evaluate(function get_output(c, o) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs.length > o;
},
// pass parameter from the test suite js to the browser code js
{c : cell_num, o : out_num});
});
},
function then() { },
function timeout() {
this.echo("wait_for_output timed out!");
});
};
casper.wait_for_widget = function (widget_info) {
// wait for a widget msg que to reach 0
//
// Parameters
// ----------
// widget_info : object
// Object which contains info related to the widget. The model_id property
// is used to identify the widget.
// Clear the results of a previous query, if they exist. Make sure a
// dictionary exists to store the async results in.
this.thenEvaluate(function(model_id) {
if (window.pending_msgs === undefined) {
window.pending_msgs = {};
} else {
window.pending_msgs[model_id] = -1;
}
}, {model_id: widget_info.model_id});
// Wait for the pending messages to be 0.
this.waitFor(function () {
var pending = this.evaluate(function (model_id) {
// Get the model. Once the model is had, store it's pending_msgs
// count in the window's dictionary.
IPython.notebook.kernel.widget_manager.get_model(model_id)
.then(function(model) {
window.pending_msgs[model_id] = model.pending_msgs;
});
// Return the pending_msgs result.
return window.pending_msgs[model_id];
}, {model_id: widget_info.model_id});
if (pending === 0) {
return true;
} else {
return false;
}
});
};
casper.get_output_cell = function (cell_num, out_num) {
// return an output of a given cell
out_num = out_num || 0;
var result = casper.evaluate(function (c, o) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs[o];
},
{c : cell_num, o : out_num});
if (!result) {
var num_outputs = casper.evaluate(function (c) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs.length;
},
{c : cell_num});
this.test.assertTrue(false,
"Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
);
} else {
return result;
}
};
casper.get_cells_length = function () {
// return the number of cells in the notebook
var result = casper.evaluate(function () {
return IPython.notebook.get_cells().length;
});
return result;
};
casper.set_cell_text = function(index, text){
// Set the text content of a cell.
this.evaluate(function (index, text) {
var cell = IPython.notebook.get_cell(index);
cell.set_text(text);
}, index, text);
};
casper.get_cell_text = function(index){
// Get the text content of a cell.
return this.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
return cell.get_text();
}, index);
};
casper.insert_cell_at_bottom = function(cell_type){
// Inserts a cell at the bottom of the notebook
// Returns the new cell's index.
return this.evaluate(function (cell_type) {
var cell = IPython.notebook.insert_cell_at_bottom(cell_type);
return IPython.notebook.find_cell_index(cell);
}, cell_type);
};
casper.append_cell = function(text, cell_type) {
// Insert a cell at the bottom of the notebook and set the cells text.
// Returns the new cell's index.
var index = this.insert_cell_at_bottom(cell_type);
if (text !== undefined) {
this.set_cell_text(index, text);
}
return index;
};
casper.execute_cell = function(index, expect_failure){
// Asynchronously executes a cell by index.
// Returns the cell's index.
if (expect_failure === undefined) expect_failure = false;
var that = this;
this.then(function(){
that.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
cell.execute();
}, index);
});
this.wait_for_idle();
this.then(function () {
var error = that.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
var outputs = cell.output_area.outputs;
for (var i = 0; i < outputs.length; i++) {
if (outputs[i].output_type == 'error') {
return outputs[i];
}
}
return false;
}, index);
if (error === null) {
this.test.fail("Failed to check for error output");
}
if (expect_failure && error === false) {
this.test.fail("Expected error while running cell");
} else if (!expect_failure && error !== false) {
this.test.fail("Error running cell:\n" + error.traceback.join('\n'));
}
});
return index;
};
casper.execute_cell_then = function(index, then_callback, expect_failure) {
// Synchronously executes a cell by index.
// Optionally accepts a then_callback parameter. then_callback will get called
// when the cell has finished executing.
// Returns the cell's index.
var return_val = this.execute_cell(index, expect_failure);
this.wait_for_idle();
var that = this;
this.then(function(){
if (then_callback!==undefined) {
then_callback.apply(that, [index]);
}
});
return return_val;
};
casper.wait_for_element = function(index, selector){
// Utility function that allows us to easily wait for an element
// within a cell. Uses JQuery selector to look for the element.
var that = this;
this.waitFor(function() {
return that.cell_element_exists(index, selector);
});
};
casper.cell_element_exists = function(index, selector){
// Utility function that allows us to easily check if an element exists
// within a cell. Uses JQuery selector to look for the element.
return casper.evaluate(function (index, selector) {
var $cell = IPython.notebook.get_cell(index).element;
return $cell.find(selector).length > 0;
}, index, selector);
};
casper.cell_element_function = function(index, selector, function_name, function_args){
// Utility function that allows us to execute a jQuery function on an
// element within a cell.
return casper.evaluate(function (index, selector, function_name, function_args) {
var $cell = IPython.notebook.get_cell(index).element;
var $el = $cell.find(selector);
return $el[function_name].apply($el, function_args);
}, index, selector, function_name, function_args);
};
casper.validate_notebook_state = function(message, mode, cell_index) {
// Validate the entire dual mode state of the notebook. Make sure no more than
// one cell is selected, focused, in edit mode, etc...
// General tests.
this.test.assertEquals(this.get_keyboard_mode(), this.get_notebook_mode(),
message + '; keyboard and notebook modes match');
// Is the selected cell the only cell that is selected?
if (cell_index!==undefined) {
this.test.assert(this.is_only_cell_selected(cell_index),
message + '; cell ' + cell_index + ' is the only cell selected');
}
// Mode specific tests.
if (mode==='command') {
// Are the notebook and keyboard manager in command mode?
this.test.assertEquals(this.get_keyboard_mode(), 'command',
message + '; in command mode');
// Make sure there isn't a single cell in edit mode.
this.test.assert(this.is_only_cell_edit(null),
message + '; all cells in command mode');
this.test.assert(this.is_cell_editor_focused(null),
message + '; no cell editors are focused while in command mode');
} else if (mode==='edit') {
// Are the notebook and keyboard manager in edit mode?
this.test.assertEquals(this.get_keyboard_mode(), 'edit',
message + '; in edit mode');
if (cell_index!==undefined) {
// Is the specified cell the only cell in edit mode?
this.test.assert(this.is_only_cell_edit(cell_index),
message + '; cell ' + cell_index + ' is the only cell in edit mode '+ this.cells_modes());
// Is the specified cell the only cell with a focused code mirror?
this.test.assert(this.is_cell_editor_focused(cell_index),
message + '; cell ' + cell_index + '\'s editor is appropriately focused');
}
} else {
this.test.assert(false, message + '; ' + mode + ' is an unknown mode');
}
};
casper.select_cell = function(index) {
// Select a cell in the notebook.
this.evaluate(function (i) {
IPython.notebook.select(i);
}, {i: index});
};
casper.click_cell_editor = function(index) {
// Emulate a click on a cell's editor.
// Code Mirror does not play nicely with emulated brower events.
// Instead of trying to emulate a click, here we run code similar to
// the code used in Code Mirror that handles the mousedown event on a
// region of codemirror that the user can focus.
this.evaluate(function (i) {
var cm = IPython.notebook.get_cell(i).code_mirror;
if (cm.options.readOnly != "nocursor" && (document.activeElement != cm.display.input)){
cm.display.input.focus();
}
}, {i: index});
};
casper.set_cell_editor_cursor = function(index, line_index, char_index) {
// Set the Code Mirror instance cursor's location.
this.evaluate(function (i, l, c) {
IPython.notebook.get_cell(i).code_mirror.setCursor(l, c);
}, {i: index, l: line_index, c: char_index});
};
casper.focus_notebook = function() {
// Focus the notebook div.
this.evaluate(function (){
$('#notebook').focus();
}, {});
};
casper.trigger_keydown = function() {
// Emulate a keydown in the notebook.
for (var i = 0; i < arguments.length; i++) {
this.evaluate(function (k) {
var element = $(document);
var event = IPython.keyboard.shortcut_to_event(k, 'keydown');
element.trigger(event);
}, {k: arguments[i]});
}
};
casper.get_keyboard_mode = function() {
// Get the mode of the keyboard manager.
return this.evaluate(function() {
return IPython.keyboard_manager.mode;
}, {});
};
casper.get_notebook_mode = function() {
// Get the mode of the notebook.
return this.evaluate(function() {
return IPython.notebook.mode;
}, {});
};
casper.get_cell = function(index) {
// Get a single cell.
//
// Note: Handles to DOM elements stored in the cell will be useless once in
// CasperJS context.
return this.evaluate(function(i) {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell;
}
return null;
}, {i : index});
};
casper.is_cell_editor_focused = function(index) {
// Make sure a cell's editor is the only editor focused on the page.
return this.evaluate(function(i) {
var focused_textarea = $('#notebook .CodeMirror-focused textarea');
if (focused_textarea.length > 1) { throw 'More than one Code Mirror editor is focused at once!'; }
if (i === null) {
return focused_textarea.length === 0;
} else {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell.code_mirror.getInputField() == focused_textarea[0];
}
}
return false;
}, {i : index});
};
casper.is_only_cell_selected = function(index) {
// Check if a cell is the only cell selected.
// Pass null as the index to check if no cells are selected.
return this.is_only_cell_on(index, 'selected', 'unselected');
};
casper.is_only_cell_edit = function(index) {
// Check if a cell is the only cell in edit mode.
// Pass null as the index to check if all of the cells are in command mode.
var cells_length = this.get_cells_length();
for (var j = 0; j < cells_length; j++) {
if (j === index) {
if (!this.cell_mode_is(j, 'edit')) {
return false;
}
} else {
if (this.cell_mode_is(j, 'edit')) {
return false;
}
}
}
return true;
};
casper.is_only_cell_on = function(i, on_class, off_class) {
// Check if a cell is the only cell with the `on_class` DOM class applied to it.
// All of the other cells are checked for the `off_class` DOM class.
// Pass null as the index to check if all of the cells have the `off_class`.
var cells_length = this.get_cells_length();
for (var j = 0; j < cells_length; j++) {
if (j === i) {
if (this.cell_has_class(j, off_class) || !this.cell_has_class(j, on_class)) {
return false;
}
} else {
if (!this.cell_has_class(j, off_class) || this.cell_has_class(j, on_class)) {
return false;
}
}
}
return true;
};
casper.cells_modes = function(){
return this.evaluate(function(){
return IPython.notebook.get_cells().map(function(x,c){return x.mode})
}, {});
};
casper.cell_mode_is = function(index, mode) {
// Check if a cell is in a specific mode
return this.evaluate(function(i, m) {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell.mode === m;
}
return false;
}, {i : index, m: mode});
};
casper.cell_has_class = function(index, classes) {
// Check if a cell has a class.
return this.evaluate(function(i, c) {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell.element.hasClass(c);
}
return false;
}, {i : index, c: classes});
};
casper.is_cell_rendered = function (index) {
return this.evaluate(function(i) {
return !!IPython.notebook.get_cell(i).rendered;
}, {i:index});
};
casper.assert_colors_equal = function (hex_color, local_color, msg) {
// Tests to see if two colors are equal.
//
// Parameters
// hex_color: string
// Hexadecimal color code, with or without preceeding hash character.
// local_color: string
// Local color representation. Can either be hexadecimal (default for
// phantom) or rgb (default for slimer).
// Remove parentheses, hashes, semi-colons, and space characters.
hex_color = hex_color.replace(/[\(\); #]/, '');
local_color = local_color.replace(/[\(\); #]/, '');
// If the local color is rgb, clean it up and replace
if (local_color.substr(0,3).toLowerCase() == 'rgb') {
var components = local_color.substr(3).split(',');
local_color = '';
for (var i = 0; i < components.length; i++) {
var part = parseInt(components[i]).toString(16);
while (part.length < 2) part = '0' + part;
local_color += part;
}
}
this.test.assertEquals(hex_color.toUpperCase(), local_color.toUpperCase(), msg);
};
casper.notebook_test = function(test) {
// Wrap a notebook test to reduce boilerplate.
this.open_new_notebook();
// Echo whether or not we are running this test using SlimerJS
if (this.evaluate(function(){
return typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
})) {
console.log('This test is running in SlimerJS.');
this.slimerjs = true;
}
// Make sure to remove the onbeforeunload callback. This callback is
// responsible for the "Are you sure you want to quit?" type messages.
// PhantomJS ignores these prompts, SlimerJS does not which causes hangs.
this.then(function(){
this.evaluate(function(){
window.onbeforeunload = function(){};
});
});
this.then(test);
// Kill the kernel and delete the notebook.
this.shutdown_current_kernel();
// This is still broken but shouldn't be a problem for now.
// this.delete_current_notebook();
// This is required to clean up the page we just finished with. If we don't call this
// casperjs will leak file descriptors of all the open WebSockets in that page. We
// have to set this.page=null so that next time casper.start runs, it will create a
// new page from scratch.
this.then(function () {
this.page.close();
this.page = null;
});
// Run the browser automation.
this.run(function() {
this.test.done();
});
};
casper.wait_for_dashboard = function () {
// Wait for the dashboard list to load.
casper.waitForSelector('.list_item');
};
casper.open_dashboard = function () {
// Start casper by opening the dashboard page.
var baseUrl = this.get_notebook_server();
this.start(baseUrl);
this.waitFor(this.page_loaded);
this.wait_for_dashboard();
};
casper.dashboard_test = function (test) {
// Open the dashboard page and run a test.
this.open_dashboard();
this.then(test);
this.then(function () {
this.page.close();
this.page = null;
});
// Run the browser automation.
this.run(function() {
this.test.done();
});
};
// note that this will only work for UNIQUE events -- if you want to
// listen for the same event twice, this will not work!
casper.event_test = function (name, events, action, timeout) {
// set up handlers to listen for each of the events
this.thenEvaluate(function (events) {
var make_handler = function (event) {
return function () {
IPython._events_triggered.push(event);
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
delete IPython._event_handlers[event];
};
};
IPython._event_handlers = {};
IPython._events_triggered = [];
for (var i=0; i < events.length; i++) {
IPython._event_handlers[events[i]] = make_handler(events[i]);
IPython.notebook.events.on(events[i], IPython._event_handlers[events[i]]);
}
}, [events]);
// execute the requested action
this.then(action);
// wait for all the events to be triggered
this.waitFor(function () {
return this.evaluate(function (events) {
return IPython._events_triggered.length >= events.length;
}, [events]);
}, undefined, undefined, timeout);
// test that the events were triggered in the proper order
this.then(function () {
var triggered = this.evaluate(function () {
return IPython._events_triggered;
});
var handlers = this.evaluate(function () {
return Object.keys(IPython._event_handlers);
});
this.test.assertEquals(triggered.length, events.length, name + ': ' + events.length + ' events were triggered');
this.test.assertEquals(handlers.length, 0, name + ': all handlers triggered');
for (var i=0; i < events.length; i++) {
this.test.assertEquals(triggered[i], events[i], name + ': ' + events[i] + ' was triggered');
}
});
// turn off any remaining event listeners
this.thenEvaluate(function () {
for (var event in IPython._event_handlers) {
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
delete IPython._event_handlers[event];
}
});
};
casper.options.waitTimeout=10000;
casper.on('waitFor.timeout', function onWaitForTimeout(timeout) {
this.echo("Timeout for " + casper.get_notebook_server());
this.echo("Is the notebook server running?");
});
casper.print_log = function () {
// Pass `console.log` calls from page JS to casper.
this.on('remote.message', function(msg) {
this.echo('Remote message caught: ' + msg);
});
};
casper.on("page.error", function onError(msg, trace) {
// show errors in the browser
this.echo("Page Error");
this.echo(" Message: " + msg.split('\n').join('\n '));
this.echo(" Call stack:");
var local_path = this.get_notebook_server();
for (var i = 0; i < trace.length; i++) {
var frame = trace[i];
var file = frame.file;
// shorten common phantomjs evaluate url
// this will have a different value on slimerjs
if (file === "phantomjs://webpage.evaluate()") {
file = "evaluate";
}
// remove the version tag from the path
file = file.replace(/(\?v=[0-9abcdef]+)/, '');
// remove the local address from the beginning of the path
if (file.indexOf(local_path) === 0) {
file = file.substr(local_path.length);
}
var frame_text = (frame.function.length > 0) ? " in " + frame.function : "";
this.echo(" line " + frame.line + " of " + file + frame_text);
}
});
casper.capture_log = function () {
// show captured errors
var captured_log = [];
var seen_errors = 0;
this.on('remote.message', function(msg) {
captured_log.push(msg);
});
var that = this;
this.test.on("test.done", function (result) {
// test.done runs per-file,
// but suiteResults is per-suite (directory)
var current_errors;
if (this.suiteResults) {
// casper 1.1 has suiteResults
current_errors = this.suiteResults.countErrors() + this.suiteResults.countFailed();
} else {
// casper 1.0 has testResults instead
current_errors = this.testResults.failed;
}
if (current_errors > seen_errors && captured_log.length > 0) {
casper.echo("\nCaptured console.log:");
for (var i = 0; i < captured_log.length; i++) {
var output = String(captured_log[i]).split('\n');
for (var j = 0; j < output.length; j++) {
casper.echo(" " + output[j]);
}
}
}
seen_errors = current_errors;
captured_log = [];
});
};
casper.interact = function() {
// Start an interactive Javascript console.
var system = require('system');
system.stdout.writeLine('JS interactive console.');
system.stdout.writeLine('Type `exit` to quit.');
function read_line() {
system.stdout.writeLine('JS: ');
var line = system.stdin.readLine();
return line;
}
var input = read_line();
while (input.trim() != 'exit') {
var output = this.evaluate(function(code) {
return String(eval(code));
}, {code: input});
system.stdout.writeLine('\nOut: ' + output);
input = read_line();
}
};
casper.capture_log();