mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-30 12:11:32 +08:00
Merge pull request #6818 from jdfreder/race_conditions
Add promises to the widget framework.
This commit is contained in:
commit
7cf7d3f202
@ -605,7 +605,119 @@ define([
|
||||
$.ajax(url, settings);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
var WrappedError = function(message, error){
|
||||
// Wrappable Error class
|
||||
|
||||
// The Error class doesn't actually act on `this`. Instead it always
|
||||
// returns a new instance of Error. Here we capture that instance so we
|
||||
// can apply it's properties to `this`.
|
||||
var tmp = Error.apply(this, [message]);
|
||||
|
||||
// Copy the properties of the error over to this.
|
||||
var properties = Object.getOwnPropertyNames(tmp);
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
this[properties[i]] = tmp[properties[i]];
|
||||
}
|
||||
|
||||
// Keep a stack of the original error messages.
|
||||
if (error instanceof WrappedError) {
|
||||
this.error_stack = error.error_stack;
|
||||
} else {
|
||||
this.error_stack = [error];
|
||||
}
|
||||
this.error_stack.push(tmp);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
WrappedError.prototype = Object.create(Error.prototype, {});
|
||||
|
||||
|
||||
var load_class = function(class_name, module_name, registry) {
|
||||
// Tries to load a class
|
||||
//
|
||||
// Tries to load a class from a module using require.js, if a module
|
||||
// is specified, otherwise tries to load a class from the global
|
||||
// registry, if the global registry is provided.
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
// Try loading the view module using require.js
|
||||
if (module_name) {
|
||||
require([module_name], function(module) {
|
||||
if (module[class_name] === undefined) {
|
||||
reject(new Error('Class '+class_name+' not found in module '+module_name));
|
||||
} else {
|
||||
resolve(module[class_name]);
|
||||
}
|
||||
}, reject);
|
||||
} else {
|
||||
if (registry && registry[class_name]) {
|
||||
resolve(registry[class_name]);
|
||||
} else {
|
||||
reject(new Error('Class '+class_name+' not found in registry '));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var resolve_promises_dict = function(d) {
|
||||
// Resolve a promiseful dictionary.
|
||||
// Returns a single Promise.
|
||||
var keys = Object.keys(d);
|
||||
var values = [];
|
||||
keys.forEach(function(key) {
|
||||
values.push(d[key]);
|
||||
});
|
||||
return Promise.all(values).then(function(v) {
|
||||
d = {};
|
||||
for(var i=0; i<keys.length; i++) {
|
||||
d[keys[i]] = v[i];
|
||||
}
|
||||
return d;
|
||||
});
|
||||
};
|
||||
|
||||
var WrappedError = function(message, error){
|
||||
// Wrappable Error class
|
||||
|
||||
// The Error class doesn't actually act on `this`. Instead it always
|
||||
// returns a new instance of Error. Here we capture that instance so we
|
||||
// can apply it's properties to `this`.
|
||||
var tmp = Error.apply(this, [message]);
|
||||
|
||||
// Copy the properties of the error over to this.
|
||||
var properties = Object.getOwnPropertyNames(tmp);
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
this[properties[i]] = tmp[properties[i]];
|
||||
}
|
||||
|
||||
// Keep a stack of the original error messages.
|
||||
if (error instanceof WrappedError) {
|
||||
this.error_stack = error.error_stack;
|
||||
} else {
|
||||
this.error_stack = [error];
|
||||
}
|
||||
this.error_stack.push(tmp);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
WrappedError.prototype = Object.create(Error.prototype, {});
|
||||
|
||||
var reject = function(message, log) {
|
||||
// Creates a wrappable Promise rejection function.
|
||||
//
|
||||
// Creates a function that returns a Promise.reject with a new WrappedError
|
||||
// that has the provided message and wraps the original error that
|
||||
// caused the promise to reject.
|
||||
return function(error) {
|
||||
var wrapped_error = new WrappedError(message, error);
|
||||
if (log) console.error(wrapped_error);
|
||||
return Promise.reject(wrapped_error);
|
||||
};
|
||||
};
|
||||
|
||||
var utils = {
|
||||
regex_split : regex_split,
|
||||
uuid : uuid,
|
||||
@ -635,6 +747,10 @@ define([
|
||||
XHR_ERROR : XHR_ERROR,
|
||||
wrap_ajax_error : wrap_ajax_error,
|
||||
promising_ajax : promising_ajax,
|
||||
WrappedError: WrappedError,
|
||||
load_class: load_class,
|
||||
resolve_promises_dict: resolve_promises_dict,
|
||||
reject: reject,
|
||||
};
|
||||
|
||||
// Backwards compatability.
|
||||
|
@ -51,7 +51,7 @@ define([
|
||||
|
||||
CommManager.prototype.register_comm = function (comm) {
|
||||
// Register a comm in the mapping
|
||||
this.comms[comm.comm_id] = comm;
|
||||
this.comms[comm.comm_id] = Promise.resolve(comm);
|
||||
comm.kernel = this.kernel;
|
||||
return comm.comm_id;
|
||||
};
|
||||
@ -66,67 +66,62 @@ define([
|
||||
CommManager.prototype.comm_open = function (msg) {
|
||||
var content = msg.content;
|
||||
var that = this;
|
||||
|
||||
var instantiate_comm = function(target) {
|
||||
var comm = new Comm(content.target_name, content.comm_id);
|
||||
that.register_comm(comm);
|
||||
var comm_id = content.comm_id;
|
||||
|
||||
this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
|
||||
this.targets).then(function(target) {
|
||||
|
||||
var comm = new Comm(content.target_name, comm_id);
|
||||
comm.kernel = that.kernel;
|
||||
try {
|
||||
target(comm, msg);
|
||||
var response = target(comm, msg);
|
||||
if (response instanceof Promise) {
|
||||
return response.then(function() { return Promise.resolve(comm); });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Exception opening new comm:", e, e.stack, msg);
|
||||
comm.close();
|
||||
that.unregister_comm(comm);
|
||||
var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
|
||||
console.error(wrapped_error);
|
||||
return Promise.reject(wrapped_error);
|
||||
}
|
||||
};
|
||||
return Promise.resolve(comm);
|
||||
}, utils.reject('Could not open comm', true));
|
||||
return this.comms[comm_id];
|
||||
};
|
||||
|
||||
CommManager.prototype.comm_close = function(msg) {
|
||||
var content = msg.content;
|
||||
if (this.comms[content.comm_id] === undefined) {
|
||||
console.error('Comm promise not found for comm id ' + content.comm_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.target_module) {
|
||||
// Load requirejs module for comm target
|
||||
require([content.target_module], function(mod) {
|
||||
var target = mod[content.target_name];
|
||||
if (target !== undefined) {
|
||||
instantiate_comm(target)
|
||||
} else {
|
||||
console.log("Comm target " + content.target_name +
|
||||
" not found in module " + content.target_module);
|
||||
}
|
||||
}, function(err) { console.log(err); });
|
||||
} else {
|
||||
// No requirejs module specified: look for target in registry
|
||||
var f = this.targets[content.target_name];
|
||||
if (f === undefined) {
|
||||
console.log("No such target registered: ", content.target_name);
|
||||
console.log("Available targets are: ", this.targets);
|
||||
return;
|
||||
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
|
||||
this.unregister_comm(comm);
|
||||
try {
|
||||
comm.handle_close(msg);
|
||||
} catch (e) {
|
||||
console.log("Exception closing comm: ", e, e.stack, msg);
|
||||
}
|
||||
instantiate_comm(f)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
CommManager.prototype.comm_close = function (msg) {
|
||||
CommManager.prototype.comm_msg = function(msg) {
|
||||
var content = msg.content;
|
||||
var comm = this.comms[content.comm_id];
|
||||
if (comm === undefined) {
|
||||
if (this.comms[content.comm_id] === undefined) {
|
||||
console.error('Comm promise not found for comm id ' + content.comm_id);
|
||||
return;
|
||||
}
|
||||
this.unregister_comm(comm);
|
||||
try {
|
||||
comm.handle_close(msg);
|
||||
} catch (e) {
|
||||
console.log("Exception closing comm: ", e, e.stack, msg);
|
||||
}
|
||||
};
|
||||
|
||||
CommManager.prototype.comm_msg = function (msg) {
|
||||
var content = msg.content;
|
||||
var comm = this.comms[content.comm_id];
|
||||
if (comm === undefined) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
comm.handle_msg(msg);
|
||||
} catch (e) {
|
||||
console.log("Exception handling comm msg: ", e, e.stack, msg);
|
||||
}
|
||||
|
||||
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
|
||||
try {
|
||||
comm.handle_msg(msg);
|
||||
} catch (e) {
|
||||
console.log("Exception handling comm msg: ", e, e.stack, msg);
|
||||
}
|
||||
return Promise.resolve(comm);
|
||||
});
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@ -180,7 +175,7 @@ define([
|
||||
|
||||
// methods for handling incoming messages
|
||||
|
||||
Comm.prototype._maybe_callback = function (key, msg) {
|
||||
Comm.prototype._callback = function (key, msg) {
|
||||
var callback = this['_' + key + '_callback'];
|
||||
if (callback) {
|
||||
try {
|
||||
@ -192,11 +187,11 @@ define([
|
||||
};
|
||||
|
||||
Comm.prototype.handle_msg = function (msg) {
|
||||
this._maybe_callback('msg', msg);
|
||||
this._callback('msg', msg);
|
||||
};
|
||||
|
||||
Comm.prototype.handle_close = function (msg) {
|
||||
this._maybe_callback('close', msg);
|
||||
this._callback('close', msg);
|
||||
};
|
||||
|
||||
// For backwards compatability.
|
||||
|
@ -5,8 +5,9 @@ define([
|
||||
"underscore",
|
||||
"backbone",
|
||||
"jquery",
|
||||
"base/js/namespace"
|
||||
], function (_, Backbone, $, IPython) {
|
||||
"base/js/utils",
|
||||
"base/js/namespace",
|
||||
], function (_, Backbone, $, utils, IPython) {
|
||||
"use strict";
|
||||
//--------------------------------------------------------------------
|
||||
// WidgetManager class
|
||||
@ -47,20 +48,25 @@ define([
|
||||
//--------------------------------------------------------------------
|
||||
WidgetManager.prototype.display_view = function(msg, model) {
|
||||
// Displays a view for a particular model.
|
||||
var cell = this.get_msg_cell(msg.parent_header.msg_id);
|
||||
if (cell === null) {
|
||||
console.log("Could not determine where the display" +
|
||||
" message was from. Widget will not be displayed");
|
||||
} else {
|
||||
var that = this;
|
||||
this.create_view(model, {cell: cell, success: function(view) {
|
||||
that._handle_display_view(view);
|
||||
if (cell.widget_subarea) {
|
||||
cell.widget_subarea.append(view.$el);
|
||||
}
|
||||
view.trigger('displayed');
|
||||
}});
|
||||
}
|
||||
var that = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
var cell = that.get_msg_cell(msg.parent_header.msg_id);
|
||||
if (cell === null) {
|
||||
reject(new Error("Could not determine where the display" +
|
||||
" message was from. Widget will not be displayed"));
|
||||
} else if (cell.widget_subarea) {
|
||||
var dummy = $('<div />');
|
||||
cell.widget_subarea.append(dummy);
|
||||
that.create_view(model, {cell: cell}).then(function(view) {
|
||||
that._handle_display_view(view);
|
||||
dummy.replaceWith(view.$el);
|
||||
view.trigger('displayed');
|
||||
resolve(view);
|
||||
}, function(error) {
|
||||
reject(new utils.WrappedError('Could not display view', error));
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WidgetManager.prototype._handle_display_view = function (view) {
|
||||
@ -70,52 +76,39 @@ define([
|
||||
if (this.keyboard_manager) {
|
||||
this.keyboard_manager.register_events(view.$el);
|
||||
|
||||
if (view.additional_elements) {
|
||||
for (var i = 0; i < view.additional_elements.length; i++) {
|
||||
if (view.additional_elements) {
|
||||
for (var i = 0; i < view.additional_elements.length; i++) {
|
||||
this.keyboard_manager.register_events(view.additional_elements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
WidgetManager.prototype.create_view = function(model, options) {
|
||||
// Creates a view for a particular model.
|
||||
|
||||
var view_name = model.get('_view_name');
|
||||
var view_mod = model.get('_view_module');
|
||||
var error = options.error || function(error) { console.log(error); };
|
||||
// Creates a promise for a view of a given model
|
||||
|
||||
// Make sure the view creation is not out of order with
|
||||
// any state updates.
|
||||
model.state_change = model.state_change.then(function() {
|
||||
|
||||
return utils.load_class(model.get('_view_name'), model.get('_view_module'),
|
||||
WidgetManager._view_types).then(function(ViewType) {
|
||||
|
||||
var instantiate_view = function(ViewType) {
|
||||
if (ViewType) {
|
||||
// If a view is passed into the method, use that view's cell as
|
||||
// the cell for the view that is created.
|
||||
options = options || {};
|
||||
if (options.parent !== undefined) {
|
||||
options.cell = options.parent.options.cell;
|
||||
}
|
||||
|
||||
// Create and render the view...
|
||||
var parameters = {model: model, options: options};
|
||||
var view = new ViewType(parameters);
|
||||
view.listenTo(model, 'destroy', view.remove);
|
||||
view.render();
|
||||
model.on('destroy', view.remove, view);
|
||||
if (options.success) {
|
||||
options.success(view);
|
||||
}
|
||||
} else {
|
||||
error({unknown_view: true, view_name: view_name,
|
||||
view_module: view_mod});
|
||||
}
|
||||
};
|
||||
|
||||
if (view_mod) {
|
||||
require([view_mod], function(module) {
|
||||
instantiate_view(module[view_name]);
|
||||
}, error);
|
||||
} else {
|
||||
instantiate_view(WidgetManager._view_types[view_name]);
|
||||
}
|
||||
return view;
|
||||
}, utils.reject("Couldn't create a view for model id '" + String(model.id) + "'"));
|
||||
});
|
||||
return model.state_change;
|
||||
};
|
||||
|
||||
WidgetManager.prototype.get_msg_cell = function (msg_id) {
|
||||
@ -179,24 +172,20 @@ define([
|
||||
};
|
||||
|
||||
WidgetManager.prototype.get_model = function (model_id) {
|
||||
// Look-up a model instance by its id.
|
||||
var model = this._models[model_id];
|
||||
if (model !== undefined && model.id == model_id) {
|
||||
return model;
|
||||
}
|
||||
return null;
|
||||
// Get a promise for a model by model id.
|
||||
return this._models[model_id];
|
||||
};
|
||||
|
||||
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
|
||||
// Handle when a comm is opened.
|
||||
this.create_model({
|
||||
return this.create_model({
|
||||
model_name: msg.content.data.model_name,
|
||||
model_module: msg.content.data.model_module,
|
||||
comm: comm});
|
||||
comm: comm}).catch(utils.reject("Couldn't create a model."));
|
||||
};
|
||||
|
||||
WidgetManager.prototype.create_model = function (options) {
|
||||
// Create and return a new widget model.
|
||||
// Create and return a promise for a new widget model
|
||||
//
|
||||
// Minimally, one must provide the model_name and widget_class
|
||||
// parameters to create a model from Javascript.
|
||||
@ -206,8 +195,9 @@ define([
|
||||
// JS:
|
||||
// IPython.notebook.kernel.widget_manager.create_model({
|
||||
// model_name: 'WidgetModel',
|
||||
// widget_class: 'IPython.html.widgets.widget_int.IntSlider',
|
||||
// init_state_callback: function(model) { console.log('Create success!', model); }});
|
||||
// widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
|
||||
// .then(function(model) { console.log('Create success!', model); },
|
||||
// $.proxy(console.error, console));
|
||||
//
|
||||
// Parameters
|
||||
// ----------
|
||||
@ -220,60 +210,33 @@ define([
|
||||
// widget_class: (optional) string
|
||||
// Target name of the widget in the back-end.
|
||||
// comm: (optional) Comm
|
||||
// success: (optional) callback
|
||||
// Callback for when the model was created successfully.
|
||||
// error: (optional) callback
|
||||
// Callback for when the model wasn't created.
|
||||
// init_state_callback: (optional) callback
|
||||
// Called when the first state push from the back-end is
|
||||
// recieved. Allows you to modify the model after it's
|
||||
// complete state is filled and synced.
|
||||
|
||||
// Make default callbacks if not specified.
|
||||
var error = options.error || function(error) { console.log(error); };
|
||||
|
||||
|
||||
// Create a comm if it wasn't provided.
|
||||
var comm = options.comm;
|
||||
if (!comm) {
|
||||
comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
|
||||
}
|
||||
|
||||
// Create a new model that is connected to the comm.
|
||||
var that = this;
|
||||
var instantiate_model = function(ModelType) {
|
||||
var model_id = comm.comm_id;
|
||||
var widget_model = new ModelType(that, model_id, comm, options.init_state_callback);
|
||||
widget_model.on('comm:close', function () {
|
||||
delete that._models[model_id];
|
||||
var model_id = comm.comm_id;
|
||||
var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
|
||||
.then(function(ModelType) {
|
||||
var widget_model = new ModelType(that, model_id, comm);
|
||||
widget_model.once('comm:close', function () {
|
||||
delete that._models[model_id];
|
||||
});
|
||||
return widget_model;
|
||||
|
||||
}, function(error) {
|
||||
delete that._models[model_id];
|
||||
var wrapped_error = new utils.WrappedError("Couldn't create model", error);
|
||||
return Promise.reject(wrapped_error);
|
||||
});
|
||||
that._models[model_id] = widget_model;
|
||||
if (options.success) {
|
||||
options.success(widget_model);
|
||||
}
|
||||
};
|
||||
|
||||
// Get the model type using require or through the registry.
|
||||
var widget_type_name = options.model_name;
|
||||
var widget_module = options.model_module;
|
||||
if (widget_module) {
|
||||
|
||||
// Load the module containing the widget model
|
||||
require([widget_module], function(mod) {
|
||||
if (mod[widget_type_name]) {
|
||||
instantiate_model(mod[widget_type_name]);
|
||||
} else {
|
||||
error("Error creating widget model: " + widget_type_name
|
||||
+ " not found in " + widget_module);
|
||||
}
|
||||
}, error);
|
||||
} else {
|
||||
|
||||
// No module specified, load from the global models registry
|
||||
instantiate_model(WidgetManager._model_types[widget_type_name]);
|
||||
}
|
||||
this._models[model_id] = model_promise;
|
||||
return model_promise;
|
||||
};
|
||||
|
||||
// Backwards compatability.
|
||||
// Backwards compatibility.
|
||||
IPython.WidgetManager = WidgetManager;
|
||||
|
||||
return {'WidgetManager': WidgetManager};
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
define(["widgets/js/manager",
|
||||
"underscore",
|
||||
"backbone",
|
||||
"jquery",
|
||||
"backbone",
|
||||
"jquery",
|
||||
"base/js/utils",
|
||||
"base/js/namespace",
|
||||
], function(widgetmanager, _, Backbone, $, IPython){
|
||||
], function(widgetmanager, _, Backbone, $, utils, IPython){
|
||||
|
||||
var WidgetModel = Backbone.Model.extend({
|
||||
constructor: function (widget_manager, model_id, comm, init_state_callback) {
|
||||
constructor: function (widget_manager, model_id, comm) {
|
||||
// Constructor
|
||||
//
|
||||
// Creates a WidgetModel instance.
|
||||
@ -20,11 +21,8 @@ define(["widgets/js/manager",
|
||||
// model_id : string
|
||||
// An ID unique to this model.
|
||||
// comm : Comm instance (optional)
|
||||
// init_state_callback : callback (optional)
|
||||
// Called once when the first state message is recieved from
|
||||
// the back-end.
|
||||
this.widget_manager = widget_manager;
|
||||
this.init_state_callback = init_state_callback;
|
||||
this.state_change = Promise.resolve();
|
||||
this._buffered_state_diff = {};
|
||||
this.pending_msgs = 0;
|
||||
this.msg_buffer = null;
|
||||
@ -71,13 +69,12 @@ define(["widgets/js/manager",
|
||||
_handle_comm_msg: function (msg) {
|
||||
// Handle incoming comm msg.
|
||||
var method = msg.content.data.method;
|
||||
var that = this;
|
||||
switch (method) {
|
||||
case 'update':
|
||||
this.set_state(msg.content.data.state);
|
||||
if (this.init_state_callback) {
|
||||
this.init_state_callback.apply(this, [this]);
|
||||
delete this.init_state_callback;
|
||||
}
|
||||
this.state_change = this.state_change.then(function() {
|
||||
return that.set_state(msg.content.data.state);
|
||||
}).catch(utils.reject("Couldn't process update msg for model id '" + String(that.id) + "'", true));
|
||||
break;
|
||||
case 'custom':
|
||||
this.trigger('msg:custom', msg.content.data.content);
|
||||
@ -89,17 +86,17 @@ define(["widgets/js/manager",
|
||||
},
|
||||
|
||||
set_state: function (state) {
|
||||
var that = this;
|
||||
// Handle when a widget is updated via the python side.
|
||||
this.state_lock = state;
|
||||
try {
|
||||
var that = this;
|
||||
WidgetModel.__super__.set.apply(this, [Object.keys(state).reduce(function(obj, key) {
|
||||
obj[key] = that._unpack_models(state[key]);
|
||||
return obj;
|
||||
}, {})]);
|
||||
} finally {
|
||||
this.state_lock = null;
|
||||
}
|
||||
return this._unpack_models(state).then(function(state) {
|
||||
that.state_lock = state;
|
||||
try {
|
||||
WidgetModel.__super__.set.call(that, state);
|
||||
} finally {
|
||||
that.state_lock = null;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}, utils.reject("Couldn't set model state", true));
|
||||
},
|
||||
|
||||
_handle_status: function (msg, callbacks) {
|
||||
@ -259,24 +256,18 @@ define(["widgets/js/manager",
|
||||
_.each(value, function(sub_value, key) {
|
||||
unpacked.push(that._unpack_models(sub_value));
|
||||
});
|
||||
return unpacked;
|
||||
|
||||
return Promise.all(unpacked);
|
||||
} else if (value instanceof Object) {
|
||||
unpacked = {};
|
||||
_.each(value, function(sub_value, key) {
|
||||
unpacked[key] = that._unpack_models(sub_value);
|
||||
});
|
||||
return unpacked;
|
||||
|
||||
return utils.resolve_promises_dict(unpacked);
|
||||
} else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
|
||||
var model = this.widget_manager.get_model(value.slice(10, value.length));
|
||||
if (model) {
|
||||
return model;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
// get_model returns a promise already
|
||||
return this.widget_manager.get_model(value.slice(10, value.length));
|
||||
} else {
|
||||
return value;
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
},
|
||||
|
||||
@ -304,7 +295,7 @@ define(["widgets/js/manager",
|
||||
this.options = parameters.options;
|
||||
this.child_model_views = {};
|
||||
this.child_views = {};
|
||||
this.id = this.id || IPython.utils.uuid();
|
||||
this.id = this.id || utils.uuid();
|
||||
this.model.views[this.id] = this;
|
||||
this.on('displayed', function() {
|
||||
this.is_displayed = true;
|
||||
@ -318,29 +309,19 @@ define(["widgets/js/manager",
|
||||
},
|
||||
|
||||
create_child_view: function(child_model, options) {
|
||||
// Create and return a child view.
|
||||
//
|
||||
// -given a model and (optionally) a view name if the view name is
|
||||
// not given, it defaults to the model's default view attribute.
|
||||
|
||||
// TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
|
||||
// it would be great to have the widget manager add the cell metadata
|
||||
// to the subview without having to add it here.
|
||||
// Create and promise that resolves to a child view of a given model
|
||||
var that = this;
|
||||
var old_callback = options.callback || function(view) {};
|
||||
options = $.extend({ parent: this, success: function(child_view) {
|
||||
options = $.extend({ parent: this }, options || {});
|
||||
return this.model.widget_manager.create_view(child_model, options).then(function(child_view) {
|
||||
// Associate the view id with the model id.
|
||||
if (that.child_model_views[child_model.id] === undefined) {
|
||||
that.child_model_views[child_model.id] = [];
|
||||
}
|
||||
that.child_model_views[child_model.id].push(child_view.id);
|
||||
|
||||
// Remember the view by id.
|
||||
that.child_views[child_view.id] = child_view;
|
||||
old_callback(child_view);
|
||||
}}, options || {});
|
||||
|
||||
this.model.widget_manager.create_view(child_model, options);
|
||||
return child_view;
|
||||
}, utils.reject("Couldn't create child view"));
|
||||
},
|
||||
|
||||
pop_child_view: function(child_model) {
|
||||
|
@ -4,8 +4,9 @@
|
||||
define([
|
||||
"widgets/js/widget",
|
||||
"jqueryui",
|
||||
"base/js/utils",
|
||||
"bootstrap",
|
||||
], function(widget, $){
|
||||
], function(widget, $, utils){
|
||||
|
||||
var BoxView = widget.DOMWidgetView.extend({
|
||||
initialize: function(){
|
||||
@ -75,14 +76,17 @@ define([
|
||||
add_child_model: function(model) {
|
||||
// Called when a model is added to the children list.
|
||||
var that = this;
|
||||
this.create_child_view(model, {callback: function(view) {
|
||||
that.$box.append(view.$el);
|
||||
var dummy = $('<div/>');
|
||||
that.$box.append(dummy);
|
||||
return this.create_child_view(model).then(function(view) {
|
||||
dummy.replaceWith(view.el);
|
||||
|
||||
// Trigger the displayed event of the child view.
|
||||
that.after_displayed(function() {
|
||||
view.trigger('displayed');
|
||||
});
|
||||
}});
|
||||
return view;
|
||||
}, utils.reject("Couldn't add child view to box", true));
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -114,9 +114,10 @@ define([
|
||||
accordion_group.container_index = container_index;
|
||||
this.model_containers[model.id] = accordion_group;
|
||||
|
||||
this.create_child_view(model, {callback: function(view) {
|
||||
accordion_inner.append(view.$el);
|
||||
|
||||
var dummy = $('<div/>');
|
||||
accordion_inner.append(dummy);
|
||||
return this.create_child_view(model).then(function(view) {
|
||||
dummy.replaceWith(view.$el);
|
||||
that.update();
|
||||
that.update_titles();
|
||||
|
||||
@ -124,7 +125,8 @@ define([
|
||||
that.after_displayed(function() {
|
||||
view.trigger('displayed');
|
||||
});
|
||||
}});
|
||||
return view;
|
||||
}, utils.reject("Couldn't add child view to box", true));
|
||||
},
|
||||
});
|
||||
|
||||
@ -186,36 +188,40 @@ define([
|
||||
.css('list-style-type', 'none')
|
||||
.appendTo(this.$tabs);
|
||||
|
||||
this.create_child_view(model, {callback: function(view) {
|
||||
|
||||
var tab_text = $('<a />')
|
||||
.attr('href', '#' + uuid)
|
||||
.attr('data-toggle', 'tab')
|
||||
.text('Page ' + index)
|
||||
.appendTo(tab)
|
||||
.click(function (e) {
|
||||
|
||||
// Calling model.set will trigger all of the other views of the
|
||||
// model to update.
|
||||
that.model.set("selected_index", index, {updated_view: that});
|
||||
that.touch();
|
||||
that.select_page(index);
|
||||
});
|
||||
tab.tab_text_index = that.containers.push(tab_text) - 1;
|
||||
|
||||
var dummy = $('<div />');
|
||||
var contents_div = $('<div />', {id: uuid})
|
||||
.addClass('tab-pane')
|
||||
.addClass('fade')
|
||||
.append(dummy)
|
||||
.appendTo(that.$tab_contents);
|
||||
|
||||
return this.create_child_view(model).then(function(view) {
|
||||
dummy.replaceWith(view.$el);
|
||||
view.parent_tab = tab;
|
||||
|
||||
var tab_text = $('<a />')
|
||||
.attr('href', '#' + uuid)
|
||||
.attr('data-toggle', 'tab')
|
||||
.text('Page ' + index)
|
||||
.appendTo(tab)
|
||||
.click(function (e) {
|
||||
|
||||
// Calling model.set will trigger all of the other views of the
|
||||
// model to update.
|
||||
that.model.set("selected_index", index, {updated_view: that});
|
||||
that.touch();
|
||||
that.select_page(index);
|
||||
});
|
||||
tab.tab_text_index = that.containers.push(tab_text) - 1;
|
||||
|
||||
var contents_div = $('<div />', {id: uuid})
|
||||
.addClass('tab-pane')
|
||||
.addClass('fade')
|
||||
.append(view.$el)
|
||||
.appendTo(that.$tab_contents);
|
||||
view.parent_container = contents_div;
|
||||
|
||||
// Trigger the displayed event of the child view.
|
||||
that.after_displayed(function() {
|
||||
view.trigger('displayed');
|
||||
});
|
||||
}});
|
||||
return view;
|
||||
}, utils.reject("Couldn't add child view to box", true));
|
||||
},
|
||||
|
||||
update: function(options) {
|
||||
|
@ -27,9 +27,9 @@
|
||||
bootstrap: 'components/bootstrap/js/bootstrap.min',
|
||||
bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
|
||||
jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
|
||||
moment: "components/moment/moment",
|
||||
moment: 'components/moment/moment',
|
||||
codemirror: 'components/codemirror',
|
||||
termjs: "components/term.js/src/term",
|
||||
termjs: 'components/term.js/src/term',
|
||||
contents: '{{ contents_js_source }}',
|
||||
},
|
||||
shim: {
|
||||
|
@ -22,6 +22,35 @@ casper.open_new_notebook = function () {
|
||||
});
|
||||
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
|
||||
@ -151,10 +180,31 @@ casper.wait_for_widget = function (widget_info) {
|
||||
// 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 (m) {
|
||||
return IPython.notebook.kernel.widget_manager.get_model(m).pending_msgs;
|
||||
}, {m: widget_info.model_id});
|
||||
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;
|
||||
@ -285,6 +335,15 @@ casper.execute_cell_then = function(index, then_callback, expect_failure) {
|
||||
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.
|
||||
@ -655,7 +714,10 @@ casper.print_log = function () {
|
||||
|
||||
casper.on("page.error", function onError(msg, trace) {
|
||||
// show errors in the browser
|
||||
this.echo("Page Error!");
|
||||
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;
|
||||
@ -664,12 +726,15 @@ casper.on("page.error", function onError(msg, trace) {
|
||||
if (file === "phantomjs://webpage.evaluate()") {
|
||||
file = "evaluate";
|
||||
}
|
||||
this.echo("line " + frame.line + " of " + file);
|
||||
if (frame.function.length > 0) {
|
||||
this.echo("in " + frame.function);
|
||||
// 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);
|
||||
}
|
||||
this.echo(msg);
|
||||
});
|
||||
|
||||
|
||||
@ -680,7 +745,8 @@ casper.capture_log = function () {
|
||||
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)
|
||||
@ -696,12 +762,38 @@ casper.capture_log = function () {
|
||||
if (current_errors > seen_errors && captured_log.length > 0) {
|
||||
casper.echo("\nCaptured console.log:");
|
||||
for (var i = 0; i < captured_log.length; i++) {
|
||||
casper.echo(" " + captured_log[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();
|
||||
|
@ -18,12 +18,11 @@ casper.notebook_test(function () {
|
||||
this.evaluate(function() {
|
||||
IPython.notebook.kernel.widget_manager.create_model({
|
||||
model_name: 'WidgetModel',
|
||||
widget_class: 'IPython.html.widgets.widget_int.IntSlider',
|
||||
init_state_callback: function(model) {
|
||||
widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
|
||||
.then(function(model) {
|
||||
console.log('Create success!', model);
|
||||
window.slider_id = model.id;
|
||||
}
|
||||
});
|
||||
}, function(error) { console.log(error); });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -59,13 +59,27 @@ casper.notebook_test(function () {
|
||||
JSON.stringify(input) + ' passed through Model._pack_model unchanged');
|
||||
};
|
||||
var test_unpack = function (input) {
|
||||
var output = that.evaluate(function(input) {
|
||||
that.thenEvaluate(function(input) {
|
||||
window.results = undefined;
|
||||
var model = new IPython.WidgetModel(IPython.notebook.kernel.widget_manager, undefined);
|
||||
var results = model._unpack_models(input);
|
||||
return results;
|
||||
model._unpack_models(input).then(function(results) {
|
||||
window.results = results;
|
||||
});
|
||||
}, {input: input});
|
||||
that.test.assert(recursive_compare(input, output),
|
||||
JSON.stringify(input) + ' passed through Model._unpack_model unchanged');
|
||||
|
||||
that.waitFor(function check() {
|
||||
return that.evaluate(function() {
|
||||
return window.results;
|
||||
});
|
||||
});
|
||||
|
||||
that.then(function() {
|
||||
var results = that.evaluate(function() {
|
||||
return window.results;
|
||||
});
|
||||
that.test.assert(recursive_compare(input, results),
|
||||
JSON.stringify(input) + ' passed through Model._unpack_model unchanged');
|
||||
});
|
||||
};
|
||||
var test_packing = function(input) {
|
||||
test_pack(input);
|
||||
@ -84,7 +98,7 @@ casper.notebook_test(function () {
|
||||
test_packing([String('hi'), Date("Thu Nov 13 2014 13:46:21 GMT-0500")])
|
||||
|
||||
// Test multi-set, single touch code. First create a custom widget.
|
||||
this.evaluate(function() {
|
||||
this.thenEvaluate(function() {
|
||||
var MultiSetView = IPython.DOMWidgetView.extend({
|
||||
render: function(){
|
||||
this.model.set('a', 1);
|
||||
|
@ -1,85 +1,90 @@
|
||||
// Test widget bool class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
|
||||
// Create a checkbox and togglebutton.
|
||||
var bool_index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var bool_index = this.append_cell(
|
||||
'bool_widgets = [widgets.Checkbox(description="Title", value=True),\n' +
|
||||
' widgets.ToggleButton(description="Title", value=True)]\n' +
|
||||
'display(bool_widgets[0])\n' +
|
||||
'display(bool_widgets[1])\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(bool_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create bool widget cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
var widget_checkbox_selector = '.widget-area .widget-subarea .widget-hbox input';
|
||||
var widget_togglebutton_selector = '.widget-area .widget-subarea button';
|
||||
this.wait_for_element(bool_index, widget_checkbox_selector);
|
||||
this.wait_for_element(bool_index, widget_togglebutton_selector);
|
||||
|
||||
// Continue the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(bool_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .widget-hbox input'),
|
||||
this.test.assert(this.cell_element_exists(bool_index,
|
||||
widget_checkbox_selector),
|
||||
'Checkbox exists.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea .widget-hbox input', 'prop', ['checked']),
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
widget_checkbox_selector, 'prop', ['checked']),
|
||||
'Checkbox is checked.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
this.test.assert(this.cell_element_exists(bool_index,
|
||||
'.widget-area .widget-subarea .widget-hbox .widget-label'),
|
||||
'Checkbox label exists.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
'.widget-area .widget-subarea .widget-hbox .widget-label', 'html')=="Title",
|
||||
'Checkbox labeled correctly.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea button'),
|
||||
this.test.assert(this.cell_element_exists(bool_index,
|
||||
widget_togglebutton_selector),
|
||||
'Toggle button exists.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea button', 'html')=="Title",
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
widget_togglebutton_selector, 'html')=="Title",
|
||||
'Toggle button labeled correctly.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea button', 'hasClass', ['active']),
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
widget_togglebutton_selector, 'hasClass', ['active']),
|
||||
'Toggle button is toggled.');
|
||||
|
||||
});
|
||||
|
||||
// Try changing the state of the widgets programatically.
|
||||
index = this.append_cell(
|
||||
'bool_widgets[0].value = False\n' +
|
||||
'bool_widgets[1].value = False\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Change bool widget value cell executed with correct output.');
|
||||
|
||||
this.test.assert(! this.cell_element_function(bool_index,
|
||||
'.widget-area .widget-subarea .widget-hbox input', 'prop', ['checked']),
|
||||
widget_checkbox_selector, 'prop', ['checked']),
|
||||
'Checkbox is not checked. (1)');
|
||||
|
||||
this.test.assert(! this.cell_element_function(bool_index,
|
||||
'.widget-area .widget-subarea button', 'hasClass', ['active']),
|
||||
widget_togglebutton_selector, 'hasClass', ['active']),
|
||||
'Toggle button is not toggled. (1)');
|
||||
|
||||
// Try toggling the bool by clicking on the checkbox.
|
||||
this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox input', 'click');
|
||||
this.cell_element_function(bool_index, widget_checkbox_selector, 'click');
|
||||
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
'.widget-area .widget-subarea .widget-hbox input', 'prop', ['checked']),
|
||||
widget_checkbox_selector, 'prop', ['checked']),
|
||||
'Checkbox is checked. (2)');
|
||||
|
||||
// Try toggling the bool by clicking on the toggle button.
|
||||
this.cell_element_function(bool_index, '.widget-area .widget-subarea button', 'click');
|
||||
this.cell_element_function(bool_index, widget_togglebutton_selector, 'click');
|
||||
|
||||
this.test.assert(this.cell_element_function(bool_index,
|
||||
'.widget-area .widget-subarea button', 'hasClass', ['active']),
|
||||
widget_togglebutton_selector, 'hasClass', ['active']),
|
||||
'Toggle button is toggled. (3)');
|
||||
|
||||
});
|
||||
|
@ -1,12 +1,10 @@
|
||||
// Test container class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
|
||||
// Create a box widget.
|
||||
var container_index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var container_index = this.append_cell(
|
||||
'container = widgets.Box()\n' +
|
||||
'button = widgets.Button()\n'+
|
||||
'container.children = [button]\n'+
|
||||
@ -14,24 +12,32 @@ casper.notebook_test(function () {
|
||||
'container._dom_classes = ["my-test-class"]\n'+
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(container_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create container cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
var widget_box_selector = '.widget-area .widget-subarea .widget-box';
|
||||
var widget_box_button_selector = '.widget-area .widget-subarea .widget-box button';
|
||||
this.wait_for_element(container_index, widget_box_selector);
|
||||
this.wait_for_element(container_index, widget_box_button_selector);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(container_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .widget-box'),
|
||||
this.test.assert(this.cell_element_exists(container_index,
|
||||
widget_box_selector),
|
||||
'Widget container exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
this.test.assert(this.cell_element_exists(container_index,
|
||||
'.widget-area .widget-subarea .my-test-class'),
|
||||
'_dom_classes works.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .my-test-class button'),
|
||||
this.test.assert(this.cell_element_exists(container_index,
|
||||
widget_box_button_selector),
|
||||
'Container parent/child relationship works.');
|
||||
});
|
||||
|
||||
@ -61,20 +67,26 @@ casper.notebook_test(function () {
|
||||
'_dom_classes can be used to remove a class.');
|
||||
});
|
||||
|
||||
index = this.append_cell(
|
||||
var boxalone_index = this.append_cell(
|
||||
'display(button)\n'+
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(index, function(index){
|
||||
|
||||
this.execute_cell_then(boxalone_index, function(index){
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Display container child executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(! this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .widget-box'),
|
||||
// Wait for the widget to actually display.
|
||||
var widget_button_selector = '.widget-area .widget-subarea button';
|
||||
this.wait_for_element(boxalone_index, widget_button_selector);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(! this.cell_element_exists(boxalone_index,
|
||||
widget_box_selector),
|
||||
'Parent container not displayed.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea button'),
|
||||
this.test.assert(this.cell_element_exists(boxalone_index,
|
||||
widget_button_selector),
|
||||
'Child displayed.');
|
||||
});
|
||||
});
|
@ -1,12 +1,8 @@
|
||||
// Test widget button class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
var button_index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var button_index = this.append_cell(
|
||||
'button = widgets.Button(description="Title")\n' +
|
||||
'display(button)\n' +
|
||||
'print("Success")\n' +
|
||||
@ -14,24 +10,30 @@ casper.notebook_test(function () {
|
||||
' display("Clicked")\n' +
|
||||
'button.on_click(handle_click)');
|
||||
this.execute_cell_then(button_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create button cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
var widget_button_selector = '.widget-area .widget-subarea button';
|
||||
this.wait_for_element(button_index, widget_button_selector);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(button_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea button'),
|
||||
this.test.assert(this.cell_element_exists(button_index,
|
||||
widget_button_selector),
|
||||
'Widget button exists.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea button', 'html')=='Title',
|
||||
this.test.assert(this.cell_element_function(button_index,
|
||||
widget_button_selector, 'html')=='Title',
|
||||
'Set button description.');
|
||||
|
||||
this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea button', 'click');
|
||||
this.cell_element_function(button_index,
|
||||
widget_button_selector, 'click');
|
||||
});
|
||||
|
||||
this.wait_for_output(button_index, 1);
|
||||
|
@ -1,26 +1,28 @@
|
||||
// Test widget float class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var float_text = {};
|
||||
float_text.query = '.widget-area .widget-subarea .my-second-float-text input';
|
||||
float_text.index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'float_widget = widgets.FloatText()\n' +
|
||||
'display(float_widget)\n' +
|
||||
'float_widget._dom_classes = ["my-second-float-text"]\n' +
|
||||
'print(float_widget.model_id)\n');
|
||||
this.execute_cell_then(float_text.index, function(index){
|
||||
float_text.model_id = this.get_output_cell(index).text.trim();
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
});
|
||||
|
||||
// Wait for the widget to actually display.
|
||||
this.wait_for_element(float_text.index, float_text.query);
|
||||
|
||||
// Continue with the tests
|
||||
this.then(function(){
|
||||
this.test.assert(this.cell_element_exists(float_text.index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, float_text.query),
|
||||
this.test.assert(this.cell_element_exists(float_text.index, float_text.query),
|
||||
'Widget float textbox exists.');
|
||||
|
||||
this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
|
||||
@ -64,18 +66,23 @@ casper.notebook_test(function () {
|
||||
'[display(floatrange[i]) for i in range(2)]\n' +
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(slider.index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create float range cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
this.wait_for_element(slider.index, slider.query);
|
||||
this.wait_for_element(slider.index, float_text_query);
|
||||
|
||||
this.then(function(){
|
||||
this.test.assert(this.cell_element_exists(slider.index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, slider.query),
|
||||
this.test.assert(this.cell_element_exists(slider.index, slider.query),
|
||||
'Widget slider exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, float_text_query),
|
||||
this.test.assert(this.cell_element_exists(slider.index, float_text_query),
|
||||
'Widget float textbox exists.');
|
||||
});
|
||||
|
||||
|
@ -26,19 +26,23 @@ casper.notebook_test(function () {
|
||||
'display(image)\n' +
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(image_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create image executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widget to actually display.
|
||||
var img_selector = '.widget-area .widget-subarea img';
|
||||
this.wait_for_element(image_index, img_selector);
|
||||
|
||||
this.then(function(){
|
||||
this.test.assert(this.cell_element_exists(image_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
var img_sel = '.widget-area .widget-subarea img';
|
||||
this.test.assert(this.cell_element_exists(index, img_sel), 'Image exists.');
|
||||
this.test.assert(this.cell_element_exists(image_index, img_selector), 'Image exists.');
|
||||
|
||||
// Verify that the image's base64 data has made it into the DOM.
|
||||
var img_src = this.cell_element_function(image_index, img_sel, 'attr', ['src']);
|
||||
var img_src = this.cell_element_function(image_index, img_selector, 'attr', ['src']);
|
||||
this.test.assert(img_src.indexOf(test_jpg) > -1, 'Image src data exists.');
|
||||
});
|
||||
});
|
@ -1,26 +1,28 @@
|
||||
// Test widget int class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var int_text = {};
|
||||
int_text.query = '.widget-area .widget-subarea .my-second-int-text input';
|
||||
int_text.index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'int_widget = widgets.IntText()\n' +
|
||||
'display(int_widget)\n' +
|
||||
'int_widget._dom_classes = ["my-second-int-text"]\n' +
|
||||
'print(int_widget.model_id)\n');
|
||||
this.execute_cell_then(int_text.index, function(index){
|
||||
int_text.model_id = this.get_output_cell(index).text.trim();
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
});
|
||||
|
||||
// Wait for the widget to actually display.
|
||||
this.wait_for_element(int_text.index, int_text.query);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(int_text.index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, int_text.query),
|
||||
this.test.assert(this.cell_element_exists(int_text.index, int_text.query),
|
||||
'Widget int textbox exists.');
|
||||
|
||||
this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
|
||||
@ -54,13 +56,6 @@ casper.notebook_test(function () {
|
||||
this.test.assertEquals(this.get_output_cell(index).text, '12\n',
|
||||
'Invald int textbox value caught and filtered.');
|
||||
});
|
||||
|
||||
index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
|
||||
var slider_query = '.widget-area .widget-subarea .slider';
|
||||
var int_text2 = {};
|
||||
@ -73,15 +68,22 @@ casper.notebook_test(function () {
|
||||
'print(intrange[0].model_id)\n');
|
||||
this.execute_cell_then(int_text2.index, function(index){
|
||||
int_text2.model_id = this.get_output_cell(index).text.trim();
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
this.wait_for_element(int_text2.index, int_text2.query);
|
||||
this.wait_for_element(int_text2.index, slider_query);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function(){
|
||||
this.test.assert(this.cell_element_exists(int_text2.index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, slider_query),
|
||||
this.test.assert(this.cell_element_exists(int_text2.index, slider_query),
|
||||
'Widget slider exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, int_text2.query),
|
||||
this.test.assert(this.cell_element_exists(int_text2.index, int_text2.query),
|
||||
'Widget int textbox exists.');
|
||||
});
|
||||
|
||||
|
@ -58,21 +58,30 @@ casper.notebook_test(function () {
|
||||
this.execute_cell_then(selection_index, function(index){
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create selection cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widgets to actually display.
|
||||
this.wait_for_element(selection_index, combo_selector);
|
||||
this.wait_for_element(selection_index, multibtn_selector);
|
||||
this.wait_for_element(selection_index, radio_selector);
|
||||
this.wait_for_element(selection_index, list_selector);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(selection_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, combo_selector),
|
||||
this.test.assert(this.cell_element_exists(selection_index, combo_selector),
|
||||
'Widget combobox exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, multibtn_selector),
|
||||
this.test.assert(this.cell_element_exists(selection_index, multibtn_selector),
|
||||
'Widget multibutton exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, radio_selector),
|
||||
this.test.assert(this.cell_element_exists(selection_index, radio_selector),
|
||||
'Widget radio buttons exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, list_selector),
|
||||
this.test.assert(this.cell_element_exists(selection_index, list_selector),
|
||||
'Widget list exists.');
|
||||
|
||||
// Verify that no items are selected.
|
||||
|
@ -18,20 +18,22 @@ casper.notebook_test(function () {
|
||||
'multicontainer.selected_index = 0\n' +
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(multicontainer1_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create multicontainer cell executed with correct output. (1)');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widget to actually display.
|
||||
this.wait_for_element(multicontainer1_index, multicontainer1_query);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(multicontainer1_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, multicontainer1_query),
|
||||
this.test.assert(this.cell_element_exists(multicontainer1_index, multicontainer1_query),
|
||||
'Widget tab list exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, multicontainer1_query),
|
||||
'First widget tab list exists.');
|
||||
|
||||
// JQuery selector is 1 based
|
||||
this.click(multicontainer1_query + ' li:nth-child(2) a');
|
||||
});
|
||||
@ -74,23 +76,28 @@ casper.notebook_test(function () {
|
||||
'multicontainer.selected_index = 0\n' +
|
||||
'print("Success")\n');
|
||||
this.execute_cell_then(multicontainer2_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create multicontainer cell executed with correct output. (2)');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widget to actually display.
|
||||
this.wait_for_element(multicontainer2_index, multicontainer2_query);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function() {
|
||||
this.test.assert(this.cell_element_exists(multicontainer2_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, multicontainer2_query),
|
||||
this.test.assert(this.cell_element_exists(multicontainer2_index, multicontainer2_query),
|
||||
'Widget accordion exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index, multicontainer2_query +
|
||||
this.test.assert(this.cell_element_exists(multicontainer2_index, multicontainer2_query +
|
||||
' .panel:nth-child(1) .panel-collapse'),
|
||||
'First accordion page exists.');
|
||||
|
||||
// JQuery selector is 1 based
|
||||
this.test.assert(this.cell_element_function(index, multicontainer2_query +
|
||||
this.test.assert(this.cell_element_function(multicontainer2_index, multicontainer2_query +
|
||||
' .panel.panel-default:nth-child(3) .panel-heading .accordion-toggle',
|
||||
'html')=='good', 'Accordion page title set (before display).');
|
||||
|
||||
|
@ -1,12 +1,8 @@
|
||||
// Test widget string class
|
||||
casper.notebook_test(function () {
|
||||
index = this.append_cell(
|
||||
var string_index = this.append_cell(
|
||||
'from IPython.html import widgets\n' +
|
||||
'from IPython.display import display, clear_output\n' +
|
||||
'print("Success")');
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var string_index = this.append_cell(
|
||||
'string_widget = [widgets.Text(value = "xyz", placeholder = "abc"),\n' +
|
||||
' widgets.Textarea(value = "xyz", placeholder = "def"),\n' +
|
||||
' widgets.HTML(value = "xyz"),\n' +
|
||||
@ -14,40 +10,50 @@ casper.notebook_test(function () {
|
||||
'[display(widget) for widget in string_widget]\n'+
|
||||
'print("Success")');
|
||||
this.execute_cell_then(string_index, function(index){
|
||||
|
||||
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
|
||||
'Create string widget cell executed with correct output.');
|
||||
});
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
// Wait for the widget to actually display.
|
||||
var textbox_selector = '.widget-area .widget-subarea .widget-hbox input[type=text]';
|
||||
var textarea_selector = '.widget-area .widget-subarea .widget-hbox textarea';
|
||||
var latex_selector = '.widget-area .widget-subarea div span.MathJax_Preview';
|
||||
this.wait_for_element(string_index, textbox_selector);
|
||||
this.wait_for_element(string_index, textarea_selector);
|
||||
this.wait_for_element(string_index, latex_selector);
|
||||
|
||||
// Continue with the tests.
|
||||
this.then(function(){
|
||||
this.test.assert(this.cell_element_exists(string_index,
|
||||
'.widget-area .widget-subarea'),
|
||||
'Widget subarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .widget-hbox input[type=text]'),
|
||||
this.test.assert(this.cell_element_exists(string_index,
|
||||
textbox_selector),
|
||||
'Textbox exists.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(index,
|
||||
'.widget-area .widget-subarea .widget-hbox textarea'),
|
||||
this.test.assert(this.cell_element_exists(string_index,
|
||||
textarea_selector),
|
||||
'Textarea exists.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea .widget-hbox textarea', 'val')=='xyz',
|
||||
this.test.assert(this.cell_element_function(string_index,
|
||||
textarea_selector, 'val')=='xyz',
|
||||
'Python set textarea value.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea .widget-hbox input[type=text]', 'val')=='xyz',
|
||||
this.test.assert(this.cell_element_function(string_index,
|
||||
textbox_selector, 'val')=='xyz',
|
||||
'Python set textbox value.');
|
||||
|
||||
this.test.assert(this.cell_element_exists(string_index,
|
||||
'.widget-area .widget-subarea div span.MathJax_Preview'),
|
||||
latex_selector),
|
||||
'MathJax parsed the LaTeX successfully.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea .widget-hbox textarea', 'attr', ['placeholder'])=='def',
|
||||
this.test.assert(this.cell_element_function(string_index,
|
||||
textarea_selector, 'attr', ['placeholder'])=='def',
|
||||
'Python set textarea placeholder.');
|
||||
|
||||
this.test.assert(this.cell_element_function(index,
|
||||
'.widget-area .widget-subarea .widget-hbox input[type=text]', 'attr', ['placeholder'])=='abc',
|
||||
this.test.assert(this.cell_element_function(string_index,
|
||||
textbox_selector, 'attr', ['placeholder'])=='abc',
|
||||
'Python set textbox placehoder.');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user