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