mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-02 14:27:31 +00:00
first commit
This commit is contained in:
0
.venv/Lib/site-packages/notebook/tests/__init__.py
Normal file
0
.venv/Lib/site-packages/notebook/tests/__init__.py
Normal file
58
.venv/Lib/site-packages/notebook/tests/base/highlight.js
Normal file
58
.venv/Lib/site-packages/notebook/tests/base/highlight.js
Normal file
@ -0,0 +1,58 @@
|
||||
casper.notebook_test(function () {
|
||||
this.on('remote.callback', function(data){
|
||||
if(data.error_expected){
|
||||
that.test.assertEquals(
|
||||
data.error,
|
||||
true,
|
||||
"!highlight: " + data.provided + " errors"
|
||||
);
|
||||
}else{
|
||||
that.test.assertEquals(
|
||||
data.observed,
|
||||
data.expected,
|
||||
"highlight: " + data.provided + " as " + data.expected
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var that = this;
|
||||
// syntax highlighting
|
||||
[
|
||||
{to: "gfm"},
|
||||
{to: "python"},
|
||||
{to: "ipython"},
|
||||
{to: "ipythongfm"},
|
||||
{to: "text/x-markdown", from: [".md"]},
|
||||
{to: "text/x-python", from: [".py", "Python"]},
|
||||
{to: "application/json", from: ["json", "JSON"]},
|
||||
{to: "text/x-ruby", from: [".rb", "ruby", "Ruby"]},
|
||||
{to: "application/ld+json", from: ["json-ld", "JSON-LD"]},
|
||||
{from: [".pyc"], error: true},
|
||||
{from: ["../"], error: true},
|
||||
{from: ["//"], error: true},
|
||||
].map(function (mode) {
|
||||
(mode.from || []).concat(mode.to || []).map(function(from){
|
||||
casper.evaluate(function(from, expected, error_expected){
|
||||
IPython.utils.requireCodeMirrorMode(from, function(observed){
|
||||
window.callPhantom({
|
||||
provided: from,
|
||||
expected: expected,
|
||||
observed: observed,
|
||||
error_expected: error_expected
|
||||
});
|
||||
}, function(error){
|
||||
window.callPhantom({
|
||||
provided: from,
|
||||
expected: expected,
|
||||
error: true,
|
||||
error_expected: error_expected
|
||||
});
|
||||
});
|
||||
}, {
|
||||
from: from,
|
||||
expected: mode.to,
|
||||
error_expected: mode.error
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
95
.venv/Lib/site-packages/notebook/tests/base/keyboard.js
Normal file
95
.venv/Lib/site-packages/notebook/tests/base/keyboard.js
Normal file
@ -0,0 +1,95 @@
|
||||
|
||||
|
||||
var normalized_shortcuts = [
|
||||
'ctrl-shift-m',
|
||||
'alt-meta-p',
|
||||
];
|
||||
|
||||
var to_normalize = [
|
||||
['shift-%', 'shift-5'],
|
||||
['ShiFT-MeTa-CtRl-AlT-m', 'alt-ctrl-meta-shift-m'],
|
||||
];
|
||||
|
||||
var unshifted = "` 1 2 3 4 5 6 7 8 9 0 - = q w e r t y u i o p [ ] \\ a s d f g h j k l ; ' z x c v b n m , . /";
|
||||
// shifted = '~ ! @ # $ % ^ & * ( ) _ + Q W E R T Y U I O P { } | A S D F G H J K L : " Z X C V B N M < > ?';
|
||||
|
||||
var ambiguous_expect = function(ch){
|
||||
// `-` is ambiguous in shortcut context as a separator, so map it to `minus`
|
||||
if(ch === '-'){
|
||||
return 'minus';
|
||||
}
|
||||
return ch;
|
||||
};
|
||||
|
||||
casper.notebook_test(function () {
|
||||
var that = this;
|
||||
|
||||
this.then(function () {
|
||||
this.each(unshifted.split(' '), function (self, item) {
|
||||
var result = this.evaluate(function (sc) {
|
||||
var e = IPython.keyboard.shortcut_to_event(sc);
|
||||
var sc2 = IPython.keyboard.event_to_shortcut(e);
|
||||
return sc2;
|
||||
}, item);
|
||||
this.test.assertEquals(result, ambiguous_expect(item), 'Shortcut to event roundtrip: '+item);
|
||||
});
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.each(to_normalize, function (self, item) {
|
||||
var result = this.evaluate(function (pair) {
|
||||
return IPython.keyboard.normalize_shortcut(pair[0]);
|
||||
}, item);
|
||||
this.test.assertEquals(result, item[1], 'Normalize shortcut: '+item[0]);
|
||||
});
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.each(normalized_shortcuts, function (self, item) {
|
||||
var result = this.evaluate(function (sc) {
|
||||
var e = IPython.keyboard.shortcut_to_event(sc);
|
||||
var sc2 = IPython.keyboard.event_to_shortcut(e);
|
||||
return sc2;
|
||||
}, item);
|
||||
this.test.assertEquals(result, item, 'Shortcut to event roundtrip: '+item);
|
||||
});
|
||||
});
|
||||
|
||||
this.then(function(){
|
||||
|
||||
var shortcuts_test = {
|
||||
'i,e,e,e,e,e' : '[[5E]]',
|
||||
'i,d,d,q,d' : '[[TEST1]]',
|
||||
'i,d,d' : '[[TEST1 WILL BE SHADOWED]]',
|
||||
'i,d,k' : '[[SHOULD SHADOW TEST2]]',
|
||||
'i,d,k,r,q' : '[[TEST2 NOT SHADOWED]]',
|
||||
';,up,down,up,down,left,right,left,right,b,a' : '[[KONAMI]]',
|
||||
'ctrl-x,meta-c,meta-b,u,t,t,e,r,f,l,y' : '[[XKCD]]'
|
||||
|
||||
};
|
||||
|
||||
that.msgs = [];
|
||||
that.on('remote.message', function(msg) {
|
||||
that.msgs.push(msg);
|
||||
});
|
||||
that.evaluate(function (obj) {
|
||||
for(var k in obj){
|
||||
if ({}.hasOwnProperty.call(obj, k)) {
|
||||
IPython.keyboard_manager.command_shortcuts.add_shortcut(k, function(){console.log(obj[k]);});
|
||||
}
|
||||
}
|
||||
}, shortcuts_test);
|
||||
|
||||
var longer_first = false;
|
||||
var longer_last = false;
|
||||
for(var m in that.msgs){
|
||||
if ({}.hasOwnProperty.call(that.msgs, m)) {
|
||||
longer_first = longer_first||(that.msgs[m].match(/you are overriting/)!= null);
|
||||
longer_last = longer_last ||(that.msgs[m].match(/will be shadowed/) != null);
|
||||
}
|
||||
}
|
||||
this.test.assert(longer_first, 'no warning if registering shorter shortcut');
|
||||
this.test.assert(longer_last , 'no warning if registering longer shortcut');
|
||||
});
|
||||
|
||||
});
|
45
.venv/Lib/site-packages/notebook/tests/base/misc.js
Normal file
45
.venv/Lib/site-packages/notebook/tests/base/misc.js
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
//
|
||||
// Miscellaneous javascript tests
|
||||
//
|
||||
casper.notebook_test(function () {
|
||||
var jsver = this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text('import notebook; print(notebook.__version__)');
|
||||
cell.execute();
|
||||
return IPython.version;
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
|
||||
// refactor this into just a get_output(0)
|
||||
this.then(function () {
|
||||
var result = this.get_output_cell(0);
|
||||
this.test.assertEquals(result.text.trim(), jsver, 'IPython.version in JS matches server-side.');
|
||||
});
|
||||
|
||||
// verify that requirejs loads the same CodeCell prototype at runtime as build time
|
||||
this.thenEvaluate(function () {
|
||||
require(['notebook/js/codecell'], function (codecell) {
|
||||
codecell.CodeCell.prototype.test = function () {
|
||||
return 'ok';
|
||||
}
|
||||
window._waitForMe = true;
|
||||
})
|
||||
})
|
||||
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return window._waitForMe;
|
||||
});
|
||||
})
|
||||
|
||||
this.then(function () {
|
||||
var result = this.evaluate(function () {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
return cell.test();
|
||||
});
|
||||
this.test.assertEquals(result, 'ok', "runtime-requirejs loads the same modules")
|
||||
})
|
||||
|
||||
});
|
57
.venv/Lib/site-packages/notebook/tests/base/security.js
Normal file
57
.venv/Lib/site-packages/notebook/tests/base/security.js
Normal file
@ -0,0 +1,57 @@
|
||||
safe_tests = [
|
||||
"<p>Hi there</p>",
|
||||
'<h1 class="foo">Hi There!</h1>',
|
||||
'<a data-cite="foo">citation</a>',
|
||||
'<div><span>Hi There</span></div>',
|
||||
];
|
||||
|
||||
unsafe_tests = [
|
||||
"<script>alert(999);</script>",
|
||||
'<a onmouseover="alert(999)">999</a>',
|
||||
'<a onmouseover=alert(999)>999</a>',
|
||||
'<IMG """><SCRIPT>alert("XSS")</SCRIPT>">',
|
||||
'<IMG SRC=# onmouseover="alert(999)">',
|
||||
'<<SCRIPT>alert(999);//<</SCRIPT>',
|
||||
'<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >',
|
||||
'<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">',
|
||||
'<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert(999);">',
|
||||
'<IFRAME SRC="javascript:alert(999);"></IFRAME>',
|
||||
'<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>',
|
||||
'<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>',
|
||||
// CSS is scrubbed
|
||||
'<style src="http://untrusted/style.css"></style>',
|
||||
'<style>div#notebook { background-color: alert-red; }</style>',
|
||||
'<div style="background-color: alert-red;"></div>',
|
||||
];
|
||||
|
||||
var truncate = function (s, n) {
|
||||
// truncate a string with an ellipsis
|
||||
if (s.length > n) {
|
||||
return s.substr(0, n-3) + '...';
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
casper.notebook_test(function () {
|
||||
this.each(safe_tests, function (self, item) {
|
||||
var sanitized = self.evaluate(function (item) {
|
||||
return IPython.security.sanitize_html(item);
|
||||
}, item);
|
||||
|
||||
// string equality may be too strict, but it works for now
|
||||
this.test.assertEquals(sanitized, item, "Safe: '" + truncate(item, 32) + "'");
|
||||
});
|
||||
|
||||
this.each(unsafe_tests, function (self, item) {
|
||||
var sanitized = self.evaluate(function (item) {
|
||||
return IPython.security.sanitize_html(item);
|
||||
}, item);
|
||||
|
||||
this.test.assertNotEquals(sanitized, item,
|
||||
"Sanitized: '" + truncate(item, 32) +
|
||||
"' => '" + truncate(sanitized, 32) + "'"
|
||||
);
|
||||
this.test.assertEquals(sanitized.indexOf("alert"), -1, "alert removed");
|
||||
});
|
||||
});
|
93
.venv/Lib/site-packages/notebook/tests/base/utils.js
Normal file
93
.venv/Lib/site-packages/notebook/tests/base/utils.js
Normal file
@ -0,0 +1,93 @@
|
||||
casper.notebook_test(function () {
|
||||
// Test fixConsole
|
||||
// Note, \u001b is the unicode notation of octal \033 which is not officially in js
|
||||
var input = [
|
||||
"\u001b[0m[\u001b[0minfo\u001b[0m] \u001b[0mtext\u001b[0m",
|
||||
"\u001b[0m[\u001b[33mwarn\u001b[0m] \u001b[0m\tmore text\u001b[0m",
|
||||
"\u001b[0m[\u001b[33mwarn\u001b[0m] \u001b[0m https://some/url/to/a/file.ext\u001b[0m",
|
||||
"\u001b[0m[\u001b[31merror\u001b[0m] \u001b[0m\u001b[0m",
|
||||
"\u001b[0m[\u001b[31merror\u001b[0m] \u001b[0m\teven more text\u001b[0m",
|
||||
"\u001b[?25hBuilding wheels for collected packages: scipy",
|
||||
"\x1b[38;5;28;01mtry\x1b[39;00m",
|
||||
"\u001b[0m[\u001b[31merror\u001b[0m] \u001b[0m\t\tand more more text\u001b[0m",
|
||||
"normal\x1b[43myellowbg\x1b[35mmagentafg\x1b[1mbold\x1b[49mdefaultbg\x1b[39mdefaultfg\x1b[22mnormal",
|
||||
].join("\n");
|
||||
|
||||
var output = [
|
||||
"[info] text",
|
||||
'[<span class="ansi-yellow-fg">warn</span>] \tmore text',
|
||||
'[<span class="ansi-yellow-fg">warn</span>] https://some/url/to/a/file.ext',
|
||||
'[<span class="ansi-red-fg">error</span>] ',
|
||||
'[<span class="ansi-red-fg">error</span>] \teven more text',
|
||||
"Building wheels for collected packages: scipy",
|
||||
'<span class="ansi-bold" style="color: rgb(0,135,0)">try</span>',
|
||||
'[<span class="ansi-red-fg">error</span>] \t\tand more more text',
|
||||
'normal<span class="ansi-yellow-bg">yellowbg</span><span class="ansi-magenta-fg ansi-yellow-bg">magentafg</span><span class="ansi-magenta-intense-fg ansi-yellow-bg ansi-bold">bold</span><span class="ansi-magenta-intense-fg ansi-bold">defaultbg</span><span class="ansi-bold">defaultfg</span>normal',
|
||||
].join("\n");
|
||||
|
||||
var result = this.evaluate(function (input) {
|
||||
return IPython.utils.fixConsole(input);
|
||||
}, input);
|
||||
|
||||
this.test.assertEquals(result, output, "IPython.utils.fixConsole() handles [0m correctly");
|
||||
|
||||
// Test fixOverwrittenChars
|
||||
var overwriting_test_cases = [
|
||||
{input: "ABC\rDEF", result: "DEF"},
|
||||
{input: "ABC\r\nDEF", result: "ABC\nDEF"},
|
||||
{input: "123\b456", result: "12456"},
|
||||
{input: "123\n\b456", result: "123\n\b456"},
|
||||
{input: "\b456", result: "\b456"}
|
||||
];
|
||||
|
||||
var that = this;
|
||||
overwriting_test_cases.forEach(function(testcase){
|
||||
var result = that.evaluate(function (input) {
|
||||
return IPython.utils.fixOverwrittenChars(input);
|
||||
}, testcase.input);
|
||||
that.test.assertEquals(result, testcase.result, "Overwriting characters processed");
|
||||
});
|
||||
|
||||
var input = [
|
||||
'hasrn\r\n',
|
||||
'hasn\n',
|
||||
'\n',
|
||||
'abcdef\r',
|
||||
'hello\n',
|
||||
'ab3\r',
|
||||
'x2\r\r',
|
||||
'1\r',
|
||||
].join('');
|
||||
|
||||
var output = [
|
||||
'hasrn\n',
|
||||
'hasn\n',
|
||||
'\n',
|
||||
'hellof\n',
|
||||
'123\r'
|
||||
].join('');
|
||||
|
||||
var result = this.evaluate(function (input) {
|
||||
return IPython.utils.fixCarriageReturn(input);
|
||||
}, input);
|
||||
|
||||
this.test.assertEquals(result, output, "IPython.utils.fixCarriageReturns works");
|
||||
|
||||
// Test load_extensions
|
||||
|
||||
this.thenEvaluate(function() {
|
||||
define('nbextensions/a', [], function() { window.a = true; });
|
||||
define('nbextensions/c', [], function() { window.c = true; });
|
||||
require(['base/js/utils'], function(utils) {
|
||||
utils.load_extensions('a', 'b', 'c');
|
||||
});
|
||||
}).then(function() {
|
||||
this.waitFor(function() {
|
||||
return this.evaluate(function() { return window.a; });
|
||||
});
|
||||
|
||||
this.waitFor(function() {
|
||||
return this.evaluate(function() { return window.a; });
|
||||
});
|
||||
});
|
||||
});
|
7
.venv/Lib/site-packages/notebook/tests/conftest.py
Normal file
7
.venv/Lib/site-packages/notebook/tests/conftest.py
Normal file
@ -0,0 +1,7 @@
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption('--integration_tests', action='store_true', dest="integration_tests",
|
||||
default=False, help="enable integration tests")
|
||||
|
||||
def pytest_configure(config):
|
||||
if not config.option.integration_tests:
|
||||
setattr(config.option, 'markexpr', 'not integration_tests')
|
258
.venv/Lib/site-packages/notebook/tests/launchnotebook.py
Normal file
258
.venv/Lib/site-packages/notebook/tests/launchnotebook.py
Normal file
@ -0,0 +1,258 @@
|
||||
"""Base class for notebook tests."""
|
||||
|
||||
from binascii import hexlify
|
||||
from contextlib import contextmanager
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
from threading import Thread, Event
|
||||
import time
|
||||
from unittest import TestCase
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests
|
||||
from tornado.ioloop import IOLoop
|
||||
import zmq
|
||||
|
||||
import jupyter_core.paths
|
||||
from traitlets.config import Config
|
||||
from ..notebookapp import NotebookApp, urlencode_unix_socket
|
||||
from ..utils import url_path_join
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
|
||||
MAX_WAITTIME = 30 # seconds to wait for notebook server to start
|
||||
POLL_INTERVAL = 0.1 # time between attempts
|
||||
|
||||
# TimeoutError is a builtin on Python 3. This can be removed when we stop
|
||||
# supporting Python 2.
|
||||
class TimeoutError(Exception):
|
||||
pass
|
||||
|
||||
class NotebookTestBase(TestCase):
|
||||
"""A base class for tests that need a running notebook.
|
||||
|
||||
This create some empty config and runtime directories
|
||||
and then starts the notebook server with them.
|
||||
"""
|
||||
|
||||
port = 12341
|
||||
config = None
|
||||
# run with a base URL that would be escaped,
|
||||
# to test that we don't double-escape URLs
|
||||
url_prefix = '/a%40b/'
|
||||
|
||||
@classmethod
|
||||
def wait_until_alive(cls):
|
||||
"""Wait for the server to be alive"""
|
||||
url = cls.base_url() + 'api/contents'
|
||||
for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)):
|
||||
try:
|
||||
cls.fetch_url(url)
|
||||
except ModuleNotFoundError as error:
|
||||
# Errors that should be immediately thrown back to caller
|
||||
raise error
|
||||
except Exception as e:
|
||||
if not cls.notebook_thread.is_alive():
|
||||
raise RuntimeError("The notebook server failed to start") from e
|
||||
time.sleep(POLL_INTERVAL)
|
||||
else:
|
||||
return
|
||||
|
||||
raise TimeoutError("The notebook server didn't start up correctly.")
|
||||
|
||||
@classmethod
|
||||
def wait_until_dead(cls):
|
||||
"""Wait for the server process to terminate after shutdown"""
|
||||
cls.notebook_thread.join(timeout=MAX_WAITTIME)
|
||||
if cls.notebook_thread.is_alive():
|
||||
raise TimeoutError("Undead notebook server")
|
||||
|
||||
@classmethod
|
||||
def auth_headers(cls):
|
||||
headers = {}
|
||||
if cls.token:
|
||||
headers['Authorization'] = f'token {cls.token}'
|
||||
return headers
|
||||
|
||||
@staticmethod
|
||||
def fetch_url(url):
|
||||
return requests.get(url)
|
||||
|
||||
@classmethod
|
||||
def request(cls, verb, path, **kwargs):
|
||||
"""Send a request to my server
|
||||
|
||||
with authentication and everything.
|
||||
"""
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers.update(cls.auth_headers())
|
||||
response = requests.request(verb,
|
||||
url_path_join(cls.base_url(), path),
|
||||
**kwargs)
|
||||
return response
|
||||
|
||||
@classmethod
|
||||
def get_patch_env(cls):
|
||||
return {
|
||||
'HOME': cls.home_dir,
|
||||
'PYTHONPATH': os.pathsep.join(sys.path),
|
||||
'IPYTHONDIR': pjoin(cls.home_dir, '.ipython'),
|
||||
'JUPYTER_NO_CONFIG': '1', # needed in the future
|
||||
'JUPYTER_CONFIG_DIR' : cls.config_dir,
|
||||
'JUPYTER_DATA_DIR' : cls.data_dir,
|
||||
'JUPYTER_RUNTIME_DIR': cls.runtime_dir,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_argv(cls):
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def get_bind_args(cls):
|
||||
return dict(port=cls.port)
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.tmp_dir = TemporaryDirectory()
|
||||
def tmp(*parts):
|
||||
path = os.path.join(cls.tmp_dir.name, *parts)
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
return path
|
||||
|
||||
cls.home_dir = tmp('home')
|
||||
data_dir = cls.data_dir = tmp('data')
|
||||
config_dir = cls.config_dir = tmp('config')
|
||||
runtime_dir = cls.runtime_dir = tmp('runtime')
|
||||
cls.notebook_dir = tmp('notebooks')
|
||||
cls.env_patch = patch.dict('os.environ', cls.get_patch_env())
|
||||
cls.env_patch.start()
|
||||
# Patch systemwide & user-wide data & config directories, to isolate
|
||||
# the tests from oddities of the local setup. But leave Python env
|
||||
# locations alone, so data files for e.g. nbconvert are accessible.
|
||||
# If this isolation isn't sufficient, you may need to run the tests in
|
||||
# a virtualenv or conda env.
|
||||
cls.path_patch = patch.multiple(
|
||||
jupyter_core.paths,
|
||||
SYSTEM_JUPYTER_PATH=[tmp('share', 'jupyter')],
|
||||
SYSTEM_CONFIG_PATH=[tmp('etc', 'jupyter')],
|
||||
)
|
||||
cls.path_patch.start()
|
||||
|
||||
config = cls.config or Config()
|
||||
config.NotebookNotary.db_file = ':memory:'
|
||||
|
||||
cls.token = hexlify(os.urandom(4)).decode('ascii')
|
||||
|
||||
started = Event()
|
||||
def start_thread():
|
||||
try:
|
||||
bind_args = cls.get_bind_args()
|
||||
app = cls.notebook = NotebookApp(
|
||||
port_retries=0,
|
||||
open_browser=False,
|
||||
config_dir=cls.config_dir,
|
||||
data_dir=cls.data_dir,
|
||||
runtime_dir=cls.runtime_dir,
|
||||
notebook_dir=cls.notebook_dir,
|
||||
base_url=cls.url_prefix,
|
||||
config=config,
|
||||
allow_root=True,
|
||||
token=cls.token,
|
||||
**bind_args
|
||||
)
|
||||
if "asyncio" in sys.modules:
|
||||
app._init_asyncio_patch()
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
# Patch the current loop in order to match production
|
||||
# behavior
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
# don't register signal handler during tests
|
||||
app.init_signal = lambda : None
|
||||
# clear log handlers and propagate to root for nose to capture it
|
||||
# needs to be redone after initialize, which reconfigures logging
|
||||
app.log.propagate = True
|
||||
app.log.handlers = []
|
||||
app.initialize(argv=cls.get_argv())
|
||||
app.log.propagate = True
|
||||
app.log.handlers = []
|
||||
loop = IOLoop.current()
|
||||
loop.add_callback(started.set)
|
||||
app.start()
|
||||
finally:
|
||||
# set the event, so failure to start doesn't cause a hang
|
||||
started.set()
|
||||
app.session_manager.close()
|
||||
cls.notebook_thread = Thread(target=start_thread)
|
||||
cls.notebook_thread.daemon = True
|
||||
cls.notebook_thread.start()
|
||||
started.wait()
|
||||
cls.wait_until_alive()
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
cls.notebook.stop()
|
||||
cls.wait_until_dead()
|
||||
cls.env_patch.stop()
|
||||
cls.path_patch.stop()
|
||||
cls.tmp_dir.cleanup()
|
||||
# cleanup global zmq Context, to ensure we aren't leaving dangling sockets
|
||||
def cleanup_zmq():
|
||||
zmq.Context.instance().term()
|
||||
t = Thread(target=cleanup_zmq)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
t.join(5) # give it a few seconds to clean up (this should be immediate)
|
||||
# if term never returned, there's zmq stuff still open somewhere, so shout about it.
|
||||
if t.is_alive():
|
||||
raise RuntimeError("Failed to teardown zmq Context, open sockets likely left lying around.")
|
||||
|
||||
@classmethod
|
||||
def base_url(cls):
|
||||
return f'http://localhost:{cls.port}{cls.url_prefix}'
|
||||
|
||||
|
||||
class UNIXSocketNotebookTestBase(NotebookTestBase):
|
||||
# Rely on `/tmp` to avoid any Linux socket length max buffer
|
||||
# issues. Key on PID for process-wise concurrency.
|
||||
sock = f'/tmp/.notebook.{os.getpid()}.sock'
|
||||
|
||||
@classmethod
|
||||
def get_bind_args(cls):
|
||||
return dict(sock=cls.sock)
|
||||
|
||||
@classmethod
|
||||
def base_url(cls):
|
||||
return f'{urlencode_unix_socket(cls.sock)}{cls.url_prefix}'
|
||||
|
||||
@staticmethod
|
||||
def fetch_url(url):
|
||||
# Lazily import so it is not required at the module level
|
||||
if os.name != 'nt':
|
||||
import requests_unixsocket
|
||||
with requests_unixsocket.monkeypatch():
|
||||
return requests.get(url)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def assert_http_error(status, msg=None):
|
||||
try:
|
||||
yield
|
||||
except requests.HTTPError as e:
|
||||
real_status = e.response.status_code
|
||||
assert real_status == status, \
|
||||
f"Expected status {status}, got {real_status}"
|
||||
if msg:
|
||||
assert msg in str(e), e
|
||||
else:
|
||||
assert False, "Expected HTTP error status"
|
@ -0,0 +1 @@
|
||||
console.log('z');
|
192
.venv/Lib/site-packages/notebook/tests/notebook/attachments.js
Normal file
192
.venv/Lib/site-packages/notebook/tests/notebook/attachments.js
Normal file
@ -0,0 +1,192 @@
|
||||
//
|
||||
// Test cell attachments
|
||||
//
|
||||
var fs = require('fs');
|
||||
casper.notebook_test(function () {
|
||||
// -- Test the Edit->Insert Image menu to insert new attachments
|
||||
"use strict";
|
||||
casper.test.info("Testing attachments insertion through the menuitem");
|
||||
|
||||
this.viewport(1024, 768);
|
||||
|
||||
// Click on menuitem
|
||||
var selector = '#insert_image > a';
|
||||
this.waitForSelector(selector);
|
||||
this.thenEvaluate(function(sel) {
|
||||
Jupyter.notebook.to_markdown();
|
||||
var cell = Jupyter.notebook.get_selected_cell();
|
||||
cell.set_text("");
|
||||
cell.unrender();
|
||||
|
||||
$(sel).click();
|
||||
}, selector);
|
||||
// Wait for the dialog to be shown
|
||||
this.waitUntilVisible(".modal-body");
|
||||
this.wait(200);
|
||||
|
||||
// Select the image file to insert
|
||||
|
||||
// For some reason, this doesn't seem to work in a reliable way in
|
||||
// phantomjs. So we manually set the input's files attribute
|
||||
//this.page.uploadFile('.modal-body input[name=file]', 'test.png')
|
||||
this.then(function() {
|
||||
var fname = 'notebook/tests/_testdata/black_square_22.png';
|
||||
if (!fs.exists(fname)) {
|
||||
this.test.fail(
|
||||
" does not exist, are you running the tests " +
|
||||
"from the root directory ? "
|
||||
);
|
||||
}
|
||||
this.fill('form#insert-image-form', {'file': fname});
|
||||
});
|
||||
|
||||
// Validate and render the markdown cell
|
||||
this.thenClick('#btn_ok');
|
||||
this.thenEvaluate(function() {
|
||||
Jupyter.notebook.get_cell(0).render();
|
||||
});
|
||||
this.wait(300);
|
||||
// Check that an <img> tag has been inserted and that it contains the
|
||||
// image
|
||||
this.then(function() {
|
||||
var img = this.evaluate(function() {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
var img = $("div.text_cell_render").find("img");
|
||||
return {
|
||||
src: img.attr("src"),
|
||||
width: img.width(),
|
||||
height: img.height(),
|
||||
};
|
||||
});
|
||||
this.test.assertType(img, "object", "Image('image/png')");
|
||||
this.test.assertEquals(img.src.split(',')[0],
|
||||
"data:image/png;base64",
|
||||
"Image data-uri prefix");
|
||||
this.test.assertEquals(img.width, 2, "width == 2");
|
||||
this.test.assertEquals(img.height, 2, "height == 2");
|
||||
});
|
||||
|
||||
//this.then(function() {
|
||||
//this.capture('test.png');
|
||||
//});
|
||||
|
||||
// -- Use the Edit->Copy/Paste Cell Attachments menu items
|
||||
selector = '#copy_cell_attachments > a';
|
||||
this.waitForSelector(selector);
|
||||
this.thenClick(selector);
|
||||
|
||||
// append a new cell
|
||||
this.append_cell('', 'markdown');
|
||||
this.thenEvaluate(function() {
|
||||
Jupyter.notebook.select_next();
|
||||
});
|
||||
|
||||
// and paste the attachments into it
|
||||
selector = '#paste_cell_attachments > a';
|
||||
this.waitForSelector(selector);
|
||||
this.thenClick(selector);
|
||||
|
||||
// check that the new cell has attachments
|
||||
this.then(function() {
|
||||
var cell_attachments = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_selected_cell().attachments;
|
||||
});
|
||||
var orig_cell_attachments = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_cell(0).attachments;
|
||||
});
|
||||
// Check that the two cells have the same attachments
|
||||
this.test.assertEquals(cell_attachments, orig_cell_attachments,
|
||||
"pasted attachments ok");
|
||||
});
|
||||
|
||||
// copy/paste cell includes attachments
|
||||
selector = '#copy_cell > a';
|
||||
this.waitForSelector(selector);
|
||||
this.thenClick(selector);
|
||||
|
||||
selector = '#paste_cell_below > a';
|
||||
this.waitForSelector(selector);
|
||||
this.thenClick(selector);
|
||||
|
||||
// check that the new cell has attachments
|
||||
this.then(function() {
|
||||
var cell_attachments = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_selected_cell().attachments;
|
||||
});
|
||||
var orig_cell_attachments = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_cell(0).attachments;
|
||||
});
|
||||
// Check that the two cells have the same attachments
|
||||
this.test.assertEquals(cell_attachments, orig_cell_attachments,
|
||||
"pasted cell has attachments");
|
||||
});
|
||||
|
||||
var nbname = 'attachments_test.ipynb';
|
||||
this.thenEvaluate(function(nbname) {
|
||||
Jupyter.notebook.set_notebook_name(nbname);
|
||||
}, {nbname:nbname});
|
||||
|
||||
// -- Save the notebook. This should cause garbage collection for the
|
||||
// second cell (since we just pasted the attachments but there is no
|
||||
// markdown referencing them)
|
||||
this.thenEvaluate(function(nbname) {
|
||||
Jupyter._checkpoint_created = false;
|
||||
require(['base/js/events'], function (events) {
|
||||
events.on('checkpoint_created.Notebook', function (evt, data) {
|
||||
Jupyter._checkpoint_created = true;
|
||||
});
|
||||
});
|
||||
|
||||
Jupyter.notebook.save_checkpoint();
|
||||
}, {nbname:nbname});
|
||||
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function(){
|
||||
return Jupyter._checkpoint_created;
|
||||
});
|
||||
});
|
||||
|
||||
this.then(function(){
|
||||
this.open_dashboard();
|
||||
});
|
||||
|
||||
|
||||
this.then(function(){
|
||||
var notebook_url = this.evaluate(function(nbname){
|
||||
var escaped_name = encodeURIComponent(nbname);
|
||||
var return_this_thing = null;
|
||||
$("a.item_link").map(function (i,a) {
|
||||
if (a.href.indexOf(escaped_name) >= 0) {
|
||||
return_this_thing = a.href;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return return_this_thing;
|
||||
}, {nbname:nbname});
|
||||
this.test.assertNotEquals(notebook_url, null, "Escaped URL in notebook list");
|
||||
// open the notebook
|
||||
this.open(notebook_url);
|
||||
});
|
||||
|
||||
// wait for the notebook
|
||||
this.waitFor(this.kernel_running);
|
||||
this.waitFor(function() {
|
||||
return this.evaluate(function () {
|
||||
return Jupyter && Jupyter.notebook && true;
|
||||
});
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
var cell0 = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_cell(0);
|
||||
});
|
||||
var cell1 = this.evaluate(function() {
|
||||
return Jupyter.notebook.get_cell(1);
|
||||
});
|
||||
this.test.assert('black_square_22.png' in cell0.attachments,
|
||||
'cell0 has kept its attachments');
|
||||
this.test.assertEquals(Object.keys(cell1.attachments).length, 0,
|
||||
'cell1 attachments have been garbage collected');
|
||||
});
|
||||
});
|
||||
|
192
.venv/Lib/site-packages/notebook/tests/notebook/display_id.js
Normal file
192
.venv/Lib/site-packages/notebook/tests/notebook/display_id.js
Normal file
@ -0,0 +1,192 @@
|
||||
//
|
||||
// Various output tests
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
|
||||
function get_outputs(cell_idx) {
|
||||
var outputs_json = casper.evaluate(function (cell_idx) {
|
||||
var cell = Jupyter.notebook.get_cell(cell_idx);
|
||||
return JSON.stringify(cell.output_area.outputs);
|
||||
}, {cell_idx: cell_idx});
|
||||
return JSON.parse(outputs_json);
|
||||
}
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
Jupyter.notebook.insert_cell_at_index("code", 0);
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
cell.set_text([
|
||||
"ip = get_ipython()",
|
||||
"from IPython.display import display",
|
||||
"def display_with_id(obj, display_id, update=False, execute_result=False):",
|
||||
" iopub = ip.kernel.iopub_socket",
|
||||
" session = get_ipython().kernel.session",
|
||||
" data, md = ip.display_formatter.format(obj)",
|
||||
" transient = {'display_id': display_id}",
|
||||
" content = {'data': data, 'metadata': md, 'transient': transient}",
|
||||
" if execute_result:",
|
||||
" msg_type = 'execute_result'",
|
||||
" content['execution_count'] = ip.execution_count",
|
||||
" else:",
|
||||
" msg_type = 'update_display_data' if update else 'display_data'",
|
||||
" session.send(iopub, msg_type, content, parent=ip.parent_header)",
|
||||
"",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
Jupyter.notebook.insert_cell_at_index("code", 1);
|
||||
var cell = Jupyter.notebook.get_cell(1);
|
||||
cell.set_text([
|
||||
"display('above')",
|
||||
"display_with_id(1, 'here')",
|
||||
"display('below')",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.wait_for_output(1);
|
||||
this.wait_for_idle()
|
||||
|
||||
this.then(function () {
|
||||
var outputs = get_outputs(1);
|
||||
this.test.assertEquals(outputs.length, 3, 'cell 1 has the right number of outputs');
|
||||
this.test.assertEquals(outputs[1].transient.display_id, 'here', 'has transient display_id');
|
||||
this.test.assertEquals(outputs[1].data['text/plain'], '1', 'display_with_id produces output');
|
||||
});
|
||||
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
Jupyter.notebook.insert_cell_at_index("code", 2);
|
||||
var cell = Jupyter.notebook.get_cell(2);
|
||||
cell.set_text([
|
||||
"display_with_id(2, 'here')",
|
||||
"display_with_id(3, 'there')",
|
||||
"display_with_id(4, 'here')",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.wait_for_output(2);
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var outputs1 = get_outputs(1);
|
||||
this.test.assertEquals(outputs1[1].data['text/plain'], '4', '');
|
||||
this.test.assertEquals(outputs1.length, 3, 'cell 1 still has the right number of outputs');
|
||||
var outputs2 = get_outputs(2);
|
||||
this.test.assertEquals(outputs2.length, 3, 'cell 2 has the right number of outputs');
|
||||
this.test.assertEquals(outputs2[0].transient.display_id, 'here', 'check display id 0');
|
||||
this.test.assertEquals(outputs2[0].data['text/plain'], '4', 'output[2][0]');
|
||||
this.test.assertEquals(outputs2[1].transient.display_id, 'there', 'display id 1');
|
||||
this.test.assertEquals(outputs2[1].data['text/plain'], '3', 'output[2][1]');
|
||||
this.test.assertEquals(outputs2[2].transient.display_id, 'here', 'display id 2');
|
||||
this.test.assertEquals(outputs2[2].data['text/plain'], '4', 'output[2][2]');
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.echo("Test output callback overrides work with display ids");
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
Jupyter.notebook.insert_cell_at_index("code", 3);
|
||||
var cell = Jupyter.notebook.get_cell(3);
|
||||
cell.set_text([
|
||||
"display_with_id(5, 'here')",
|
||||
"display_with_id(6, 'here', update=True)",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
var kernel = IPython.notebook.kernel;
|
||||
var msg_id = cell.last_msg_id;
|
||||
var callback_id = 'mycallbackid'
|
||||
cell.iopub_messages = [];
|
||||
var add_msg = function(msg) {
|
||||
msg.content.output_type = msg.msg_type;
|
||||
cell.iopub_messages.push(msg.content);
|
||||
};
|
||||
kernel.set_callbacks_for_msg(callback_id, {
|
||||
iopub: {
|
||||
output: add_msg,
|
||||
clear_output: add_msg,
|
||||
}
|
||||
}, false);
|
||||
kernel.output_callback_overrides_push(msg_id, callback_id);
|
||||
});
|
||||
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(3);
|
||||
return cell.iopub_messages.length >= 2;
|
||||
});
|
||||
});
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var returned = this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(3);
|
||||
return [cell.output_area.outputs, cell.iopub_messages];
|
||||
});
|
||||
var cell_results = returned[0];
|
||||
var callback_results = returned[1];
|
||||
this.test.assertEquals(cell_results.length, 0, "correct number of cell outputs");
|
||||
this.test.assertEquals(callback_results.length, 2, "correct number of callback outputs");
|
||||
this.test.assertEquals(callback_results[0].output_type, 'display_data', 'check output_type 0');
|
||||
this.test.assertEquals(callback_results[0].transient.display_id, 'here', 'check display id 0');
|
||||
this.test.assertEquals(callback_results[0].data['text/plain'], '5', 'value');
|
||||
this.test.assertEquals(callback_results[1].output_type, 'update_display_data', 'check output_type 1');
|
||||
this.test.assertEquals(callback_results[1].transient.display_id, 'here', 'display id 1');
|
||||
this.test.assertEquals(callback_results[1].data['text/plain'], '6', 'value');
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
Jupyter.notebook.insert_cell_at_index("code", 4);
|
||||
var cell = Jupyter.notebook.get_cell(4);
|
||||
cell.set_text([
|
||||
"display_with_id(7, 'here')",
|
||||
"display_with_id(8, 'here', update=True)",
|
||||
"display_with_id(9, 'result', execute_result=True)"
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
|
||||
Jupyter.notebook.insert_cell_at_index("code", 5);
|
||||
var cell = Jupyter.notebook.get_cell(5);
|
||||
cell.set_text([
|
||||
"display_with_id(10, 'result', update=True)",
|
||||
"1",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.wait_for_output(4);
|
||||
this.wait_for_output(5);
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var returned = JSON.parse(this.evaluate(function () {
|
||||
var cell3 = Jupyter.notebook.get_cell(3);
|
||||
var cell4 = Jupyter.notebook.get_cell(4);
|
||||
return JSON.stringify([cell4.output_area.outputs, cell3.iopub_messages]);
|
||||
}));
|
||||
var cell_results = returned[0];
|
||||
var callback_results = returned[1];
|
||||
this.test.assertEquals(cell_results.length, 2, "correct number of cell outputs");
|
||||
this.test.assertEquals(callback_results.length, 4, "correct number of callback outputs");
|
||||
this.test.assertEquals(callback_results[0].output_type, 'display_data', 'check output_type 0');
|
||||
this.test.assertEquals(callback_results[0].transient.display_id, 'here', 'check display id 0');
|
||||
this.test.assertEquals(callback_results[0].data['text/plain'], '5', 'value');
|
||||
this.test.assertEquals(callback_results[1].output_type, 'update_display_data', 'check output_type 1');
|
||||
this.test.assertEquals(callback_results[1].transient.display_id, 'here', 'display id 1');
|
||||
this.test.assertEquals(callback_results[1].data['text/plain'], '6', 'value');
|
||||
this.test.assertEquals(callback_results[2].output_type, 'display_data', 'check output_type 2');
|
||||
this.test.assertEquals(callback_results[2].transient.display_id, 'here', 'check display id 2');
|
||||
this.test.assertEquals(callback_results[2].data['text/plain'], '7', 'value');
|
||||
this.test.assertEquals(callback_results[3].output_type, 'update_display_data', 'check output_type 3');
|
||||
this.test.assertEquals(callback_results[3].transient.display_id, 'here', 'display id 3');
|
||||
this.test.assertEquals(callback_results[3].data['text/plain'], '8', 'value');
|
||||
|
||||
this.test.assertEquals(cell_results[1].data['text/plain'], '10', 'update execute_result')
|
||||
});
|
||||
|
||||
|
||||
});
|
116
.venv/Lib/site-packages/notebook/tests/notebook/dualmode.js
Normal file
116
.venv/Lib/site-packages/notebook/tests/notebook/dualmode.js
Normal file
@ -0,0 +1,116 @@
|
||||
// Test the notebook dual mode feature.
|
||||
|
||||
// Test
|
||||
casper.notebook_test(function () {
|
||||
var a = 'print("a")';
|
||||
var index = this.append_cell(a);
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var b = 'print("b")';
|
||||
index = this.append_cell(b);
|
||||
this.execute_cell_then(index);
|
||||
|
||||
var c = 'print("c")';
|
||||
index = this.append_cell(c);
|
||||
this.execute_cell_then(index);
|
||||
|
||||
this.then(function () {
|
||||
if (this.slimerjs) {
|
||||
// When running in xvfb, the Slimer window doesn't always have focus
|
||||
// immediately. By clicking on a new element on the page we can force
|
||||
// it to gain focus.
|
||||
this.click_cell_editor(1);
|
||||
this.click_cell_editor(0);
|
||||
}
|
||||
|
||||
this.validate_notebook_state('initial state', 'edit', 0);
|
||||
this.trigger_keydown('esc');
|
||||
this.validate_notebook_state('esc', 'command', 0);
|
||||
this.trigger_keydown('down');
|
||||
this.validate_notebook_state('down', 'command', 1);
|
||||
this.trigger_keydown('enter');
|
||||
this.validate_notebook_state('enter', 'edit', 1);
|
||||
this.trigger_keydown('j');
|
||||
this.validate_notebook_state('j in edit mode', 'edit', 1);
|
||||
this.trigger_keydown('esc');
|
||||
this.validate_notebook_state('esc', 'command', 1);
|
||||
this.trigger_keydown('j');
|
||||
this.validate_notebook_state('j in command mode', 'command', 2);
|
||||
this.click_cell_editor(0);
|
||||
this.validate_notebook_state('click cell 0', 'edit', 0);
|
||||
this.click_cell_editor(3);
|
||||
this.validate_notebook_state('click cell 3', 'edit', 3);
|
||||
this.trigger_keydown('esc');
|
||||
this.validate_notebook_state('esc', 'command', 3);
|
||||
|
||||
// Open keyboard help
|
||||
this.evaluate(function(){
|
||||
$('#keyboard_shortcuts a').click();
|
||||
}, {});
|
||||
});
|
||||
|
||||
// Wait for the dialog to fade in completely.
|
||||
this.waitForSelector('div.modal', function() {
|
||||
this.evaluate(function(){
|
||||
IPython.modal_shown = false;
|
||||
$('div.modal').on('shown.bs.modal', function (){
|
||||
IPython.modal_shown = true;
|
||||
});
|
||||
$('div.modal').on('hidden.bs.modal', function (){
|
||||
IPython.modal_shown = false;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function(){
|
||||
return IPython.modal_shown;
|
||||
});
|
||||
},
|
||||
function() {
|
||||
this.trigger_keydown('k');
|
||||
this.validate_notebook_state('k in command mode while keyboard help is up', 'command', 3);
|
||||
|
||||
// Close keyboard help
|
||||
this.evaluate(function(){
|
||||
$('div.modal-footer button.btn-default').click();
|
||||
}, {});
|
||||
});
|
||||
|
||||
// Wait for the dialog to fade out completely.
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function(){
|
||||
return !IPython.modal_shown;
|
||||
});
|
||||
},
|
||||
function() {
|
||||
|
||||
this.trigger_keydown('k');
|
||||
this.validate_notebook_state('k in command mode', 'command', 2);
|
||||
this.click_cell_editor(0);
|
||||
this.validate_notebook_state('click cell 0', 'edit', 0);
|
||||
this.focus_notebook();
|
||||
this.validate_notebook_state('focus #notebook', 'command', 0);
|
||||
this.click_cell_editor(0);
|
||||
this.validate_notebook_state('click cell 0', 'edit', 0);
|
||||
this.focus_notebook();
|
||||
this.validate_notebook_state('focus #notebook', 'command', 0);
|
||||
this.click_cell_editor(3);
|
||||
this.validate_notebook_state('click cell 3', 'edit', 3);
|
||||
|
||||
// Cell deletion
|
||||
this.trigger_keydown('esc', 'd', 'd');
|
||||
this.test.assertEquals(this.get_cells_length(), 3, 'dd actually deletes a cell');
|
||||
this.validate_notebook_state('dd', 'command', 2);
|
||||
|
||||
// Make sure that if the time between d presses is too long, nothing gets removed.
|
||||
this.trigger_keydown('d');
|
||||
});
|
||||
this.wait(1000);
|
||||
this.then(function () {
|
||||
this.trigger_keydown('d');
|
||||
this.test.assertEquals(this.get_cells_length(), 3, "d, 1 second wait, d doesn't delete a cell");
|
||||
this.validate_notebook_state('d, 1 second wait, d', 'command', 2);
|
||||
});
|
||||
});
|
@ -0,0 +1,198 @@
|
||||
|
||||
// Test
|
||||
casper.notebook_test(function () {
|
||||
var a = 'ab\n\ncd';
|
||||
var b = 'print("b")';
|
||||
var c = 'print("c")';
|
||||
var d = '"d"';
|
||||
var e = '"e"';
|
||||
var f = '"f"';
|
||||
var g = '"g"';
|
||||
var N = 7;
|
||||
|
||||
var that = this;
|
||||
var cell_is_mergeable = function (index) {
|
||||
// Get the mergeable status of a cell.
|
||||
return that.evaluate(function (index) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
return cell.is_mergeable();
|
||||
}, index);
|
||||
};
|
||||
|
||||
var cell_is_splittable = function (index) {
|
||||
// Get the splittable status of a cell.
|
||||
return that.evaluate(function (index) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
return cell.is_splittable();
|
||||
}, index);
|
||||
};
|
||||
|
||||
var close_dialog = function () {
|
||||
this.evaluate(function(){
|
||||
$('div.modal-footer button.btn-default').click();
|
||||
}, {});
|
||||
};
|
||||
|
||||
this.then(function () {
|
||||
// Split and merge cells
|
||||
this.select_cell(0);
|
||||
this.trigger_keydown('a', 'enter'); // Create cell above and enter edit mode.
|
||||
this.validate_notebook_state('a, enter', 'edit', 0);
|
||||
this.set_cell_text(0, 'abcd');
|
||||
this.set_cell_editor_cursor(0, 0, 2);
|
||||
this.test.assertEquals(this.get_cell_text(0), 'abcd', 'Verify that cell 0 has the new contents.');
|
||||
this.trigger_keydown('ctrl-shift--'); // Split
|
||||
this.test.assertEquals(this.get_cell_text(0), 'ab', 'split; Verify that cell 0 has the first half.');
|
||||
this.test.assertEquals(this.get_cell_text(1), 'cd', 'split; Verify that cell 1 has the second half.');
|
||||
this.validate_notebook_state('split', 'edit', 1);
|
||||
this.select_cell(0); // Move up to cell 0
|
||||
this.evaluate(function() { IPython.notebook.extend_selection_by(1);});
|
||||
this.trigger_keydown('shift-m'); // Merge
|
||||
this.validate_notebook_state('merge', 'command', 0);
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'merge; Verify that cell 0 has the merged contents.');
|
||||
});
|
||||
|
||||
// add some more cells and test splitting/merging when a cell is not deletable
|
||||
this.then(function () {
|
||||
this.append_cell(b);
|
||||
this.append_cell(c);
|
||||
this.append_cell(d);
|
||||
this.append_cell(e);
|
||||
this.append_cell(f);
|
||||
this.append_cell(g);
|
||||
});
|
||||
|
||||
this.thenEvaluate(function() {
|
||||
IPython.notebook.get_cell(1).metadata.deletable = false;
|
||||
});
|
||||
|
||||
// Check that merge/split status are correct
|
||||
this.then(function () {
|
||||
this.test.assert(cell_is_splittable(0), 'Cell 0 is splittable');
|
||||
this.test.assert(cell_is_mergeable(0), 'Cell 0 is mergeable');
|
||||
this.test.assert(!cell_is_splittable(1), 'Cell 1 is not splittable');
|
||||
this.test.assert(!cell_is_mergeable(1), 'Cell 1 is not mergeable');
|
||||
this.test.assert(cell_is_splittable(2), 'Cell 2 is splittable');
|
||||
this.test.assert(cell_is_mergeable(2), 'Cell 2 is mergeable');
|
||||
});
|
||||
|
||||
// Try to merge cell 0 above, nothing should happen
|
||||
this.then(function () {
|
||||
this.select_cell(0);
|
||||
});
|
||||
this.thenEvaluate(function() {
|
||||
IPython.notebook.merge_cell_above();
|
||||
});
|
||||
this.then(function() {
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Merge cell 0 above: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 0 above: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 0 above: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 0 above: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('merge up', 'command', 0);
|
||||
});
|
||||
|
||||
// Try to merge cell 0 below with cell 1, should not work, as 1 is locked
|
||||
this.then(function () {
|
||||
this.trigger_keydown('esc');
|
||||
this.select_cell(0);
|
||||
this.select_cell(1,false);
|
||||
this.trigger_keydown('shift-m');
|
||||
this.trigger_keydown('esc');
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Merge cell 0 down: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 0 down: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 0 down: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 0 down: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('merge 0 with 1', 'command', 1);
|
||||
});
|
||||
|
||||
// Try to merge cell 1 above with cell 0
|
||||
this.then(function () {
|
||||
this.select_cell(1);
|
||||
});
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.merge_cell_above();
|
||||
});
|
||||
this.then(function () {
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Merge cell 1 up: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 1 up: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 1 up: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 1 up: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('merge up', 'command', 1);
|
||||
});
|
||||
|
||||
// Try to split cell 1
|
||||
this.then(function () {
|
||||
this.select_cell(1);
|
||||
this.trigger_keydown('enter');
|
||||
this.set_cell_editor_cursor(1, 0, 2);
|
||||
this.trigger_keydown('ctrl-shift--'); // Split
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Split cell 1: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Split cell 1: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Split cell 1: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Split cell 1: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('ctrl-shift--', 'edit', 1);
|
||||
});
|
||||
|
||||
// Try to merge cell 1 down, should fail, as 1 is locked
|
||||
this.then(function () {
|
||||
this.trigger_keydown('esc');
|
||||
this.select_cell(1);
|
||||
this.select_cell(2, false); // extend selection
|
||||
this.trigger_keydown('shift-m');
|
||||
this.trigger_keydown('esc');
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Merge cell 1 down: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 1 down: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 1 down: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 1 down: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('Merge 1 with 2', 'command', 2);
|
||||
});
|
||||
|
||||
// Try to merge cell 2 above with cell 1, should fail, 1 is locked
|
||||
this.then(function () {
|
||||
this.select_cell(2);
|
||||
});
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.merge_cell_above();
|
||||
});
|
||||
this.then(function () {
|
||||
this.test.assertEquals(this.get_cells_length(), N, 'Merge cell 2 up: There are still '+N+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 2 up: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 2 up: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 2 up: Cell 2 is unchanged');
|
||||
this.validate_notebook_state('merge up', 'command', 2);
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.trigger_keydown('esc');
|
||||
this.select_cell(3);
|
||||
this.select_cell(4, false); // extend selection
|
||||
this.trigger_keydown('shift-m');
|
||||
this.trigger_keydown('esc');
|
||||
this.test.assertEquals(this.get_cells_length(), N-1 , 'Merge cell 3 with 4: There are now '+(N-1)+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 3 with 4: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 3 with 4: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 3 with 4: Cell 2 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(3), d+'\n\n'+e, 'Merge cell 3 with 4: Cell 3 is merged');
|
||||
this.test.assertEquals(this.get_cell_text(4), f, 'Merge cell 3 with 4: Cell 5 is now cell 4');
|
||||
this.test.assertEquals(this.get_cell_text(5), g, 'Merge cell 3 with 4: Cell 6 is now cell 5');
|
||||
this.validate_notebook_state('actual merge', 'command', 3);
|
||||
});
|
||||
|
||||
|
||||
this.then(function () {
|
||||
this.trigger_keydown('esc');
|
||||
this.select_cell(4);
|
||||
// shift-m on single selection does nothing.
|
||||
this.trigger_keydown('shift-m');
|
||||
this.trigger_keydown('esc');
|
||||
this.test.assertEquals(this.get_cells_length(), N-2 , 'Merge cell 4 with 5: There are now '+(N-2)+' cells');
|
||||
this.test.assertEquals(this.get_cell_text(0), a, 'Merge cell 4 with 5: Cell 0 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(1), b, 'Merge cell 4 with 5: Cell 1 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(2), c, 'Merge cell 4 with 5: Cell 2 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(3), d+'\n\n'+e, 'Merge cell 4 with 5: Cell 3 is unchanged');
|
||||
this.test.assertEquals(this.get_cell_text(4), f+'\n\n'+g, 'Merge cell 4 with 5: Cell 4 and 5 are merged');
|
||||
this.validate_notebook_state('merge on single cell merge with below', 'command', 4);
|
||||
});
|
||||
|
||||
|
||||
});
|
@ -0,0 +1,178 @@
|
||||
//
|
||||
// Test that the correct cells are executed when there are marked cells.
|
||||
//
|
||||
casper.notebook_test(function () {
|
||||
var that = this;
|
||||
var assert_outputs = function (expected, msg_prefix) {
|
||||
var msg, i;
|
||||
msg_prefix = "(assert_outputs) "+(msg_prefix || 'no prefix')+": ";
|
||||
for (i = 0; i < that.get_cells_length(); i++) {
|
||||
if (expected[i] === undefined) {
|
||||
msg = msg_prefix + 'cell ' + i + ' not executed';
|
||||
that.test.assertFalse(that.cell_has_outputs(i), msg);
|
||||
|
||||
} else {
|
||||
msg = msg_prefix + 'cell ' + i + ' executed';
|
||||
var out = (that.get_output_cell(i, undefined, msg_prefix)||{test:'<no cells>'}).text
|
||||
that.test.assertEquals(out, expected[i], msg + ', out is: '+out);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.then(function () {
|
||||
this.set_cell_text(0, 'print("a")');
|
||||
this.append_cell('print("b")');
|
||||
this.append_cell('print("c")');
|
||||
this.append_cell('print("d")');
|
||||
this.test.assertEquals(this.get_cells_length(), 4, "correct number of cells");
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.select_cell(1);
|
||||
this.select_cell(2, false);
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.clear_all_output();
|
||||
});
|
||||
})
|
||||
|
||||
this.then(function(){
|
||||
this.select_cell(1);
|
||||
this.validate_notebook_state('before execute 1', 'command', 1);
|
||||
this.select_cell(1);
|
||||
this.select_cell(2, false);
|
||||
this.trigger_keydown('ctrl-enter');
|
||||
});
|
||||
|
||||
this.wait_for_output(1);
|
||||
this.wait_for_output(2);
|
||||
|
||||
this.then(function () {
|
||||
assert_outputs([undefined, 'b\n', 'c\n', undefined], 'run selected 1');
|
||||
this.validate_notebook_state('run selected cells 1', 'command', 2);
|
||||
});
|
||||
|
||||
|
||||
// execute and insert below when there are selected cells
|
||||
this.then(function () {
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.clear_all_output();
|
||||
});
|
||||
|
||||
this.select_cell(1);
|
||||
this.validate_notebook_state('before execute 2', 'command', 1);
|
||||
this.evaluate(function () {
|
||||
$("#run_cell_insert_below").click();
|
||||
});
|
||||
});
|
||||
|
||||
this.wait_for_output(1);
|
||||
|
||||
this.then(function () {
|
||||
assert_outputs([undefined, 'b\n', undefined, undefined , undefined],'run selected cells 2');
|
||||
this.validate_notebook_state('run selected cells 2', 'edit', 2);
|
||||
});
|
||||
|
||||
// check that it doesn't affect run all above
|
||||
this.then(function () {
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.clear_all_output();
|
||||
});
|
||||
|
||||
this.select_cell(1);
|
||||
this.validate_notebook_state('before execute 3', 'command', 1);
|
||||
this.evaluate(function () {
|
||||
$("#run_all_cells_above").click();
|
||||
});
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
|
||||
this.then(function () {
|
||||
assert_outputs(['a\n', undefined, undefined, undefined],'run cells above');
|
||||
this.validate_notebook_state('run cells above', 'command', 0);
|
||||
});
|
||||
|
||||
// check that it doesn't affect run all below
|
||||
this.then(function () {
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.clear_all_output();
|
||||
});
|
||||
|
||||
this.select_cell(1);
|
||||
this.validate_notebook_state('before execute 4', 'command', 1);
|
||||
this.evaluate(function () {
|
||||
$("#run_all_cells_below").click();
|
||||
});
|
||||
});
|
||||
|
||||
this.wait_for_output(1);
|
||||
this.wait_for_output(2);
|
||||
this.wait_for_output(3);
|
||||
|
||||
this.then(function () {
|
||||
assert_outputs([undefined, 'b\n', undefined, 'c\n', 'd\n'],'run cells below');
|
||||
this.validate_notebook_state('run cells below', 'command', 4);
|
||||
});
|
||||
|
||||
// check that it doesn't affect run all
|
||||
this.then(function () {
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.clear_all_output();
|
||||
});
|
||||
|
||||
this.select_cell(1);
|
||||
this.validate_notebook_state('before execute 5', 'command', 1);
|
||||
this.evaluate(function () {
|
||||
$("#run_all_cells").click();
|
||||
});
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
this.wait_for_output(1);
|
||||
this.wait_for_output(2);
|
||||
this.wait_for_output(3);
|
||||
|
||||
this.then(function () {
|
||||
assert_outputs(['a\n', 'b\n', undefined, 'c\n', 'd\n'],'run all cells');
|
||||
this.validate_notebook_state('run all cells', 'command', 4);
|
||||
});
|
||||
|
||||
this.then(function(){
|
||||
this.set_cell_text(0, 'print("x")');
|
||||
this.set_cell_text(1, 'print("y")');
|
||||
|
||||
this.select_cell(0);
|
||||
this.select_cell(1, false);
|
||||
this.trigger_keydown('alt-enter');
|
||||
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
this.wait_for_output(1);
|
||||
this.then(function () {
|
||||
assert_outputs(['x\n', 'y\n', undefined, undefined, 'c\n', 'd\n'],'run selection and insert below');
|
||||
this.validate_notebook_state('run selection insert below', 'edit', 2);
|
||||
});
|
||||
|
||||
this.then(function(){
|
||||
this.set_cell_text(1, 'print("z")');
|
||||
this.set_cell_text(2, 'print("a")');
|
||||
|
||||
this.select_cell(1);
|
||||
this.select_cell(2, false);
|
||||
this.evaluate(function () {
|
||||
$("#run_cell_select_below").click();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
this.wait_for_output(1);
|
||||
this.wait_for_output(2);
|
||||
this.then(function () {
|
||||
assert_outputs(['x\n', 'z\n', 'a\n', undefined, 'c\n', 'd\n'],'run selection and select below');
|
||||
this.validate_notebook_state('run selection select below', 'command', 3);
|
||||
});
|
||||
});
|
23
.venv/Lib/site-packages/notebook/tests/notebook/inject_js.js
Normal file
23
.venv/Lib/site-packages/notebook/tests/notebook/inject_js.js
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Test robustness about JS injection in different place
|
||||
//
|
||||
// This assume malicious document arrive to the frontend.
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
var messages = [];
|
||||
this.on('remote.alert', function (msg) {
|
||||
messages.push(msg);
|
||||
});
|
||||
|
||||
this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
var json = cell.toJSON();
|
||||
json.execution_count = "<script> alert('hello from input prompts !')</script>";
|
||||
cell.fromJSON(json);
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.test.assert(messages.length == 0, "Captured log message from script tag injection !");
|
||||
});
|
||||
});
|
64
.venv/Lib/site-packages/notebook/tests/notebook/markdown.js
Normal file
64
.venv/Lib/site-packages/notebook/tests/notebook/markdown.js
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// Test that a Markdown cell is rendered to HTML.
|
||||
//
|
||||
casper.notebook_test(function () {
|
||||
"use strict";
|
||||
|
||||
var text = 'multi\nline';
|
||||
this.evaluate(function (text) {
|
||||
var cell = IPython.notebook.insert_cell_at_index('markdown', 0);
|
||||
cell.set_text(text);
|
||||
}, {text: text});
|
||||
|
||||
// Test markdown code blocks
|
||||
function mathjax_render_test(input_string, result, message){
|
||||
casper.thenEvaluate(function (text){
|
||||
window._test_result = null;
|
||||
require(['base/js/mathjaxutils'],function(mathjaxutils){
|
||||
window._test_result = mathjaxutils.remove_math(text);
|
||||
});
|
||||
}, {text: input_string});
|
||||
casper.waitFor(function() {
|
||||
return casper.evaluate(function(){
|
||||
return window._test_result!==null;
|
||||
});
|
||||
});
|
||||
casper.then(function(){
|
||||
var return_val = casper.evaluate(function(){
|
||||
var blah = window._test_result;
|
||||
delete window._test_result;
|
||||
return blah;
|
||||
});
|
||||
this.test.assertEquals(return_val[0], result[0], message+" markdown");
|
||||
this.test.assertEquals(return_val[1].length, result[1].length, message+" math instance count");
|
||||
for(var i=0; i<return_val[1].length; i++){
|
||||
this.test.assertEquals(return_val[1][i], result[1][i], message+" math instance "+i);
|
||||
};
|
||||
});
|
||||
};
|
||||
var input_string_1 = 'x \\\\(a_{0}+ b_{T}\\\\) y \\\\(a_{0}+ b_{T}\\\\) z';
|
||||
var expected_result_1 = ['x @@0@@ y @@1@@ z', ['\\\\(a_{0}+ b_{T}\\\\)','\\\\(a_{0}+ b_{T}\\\\)']];
|
||||
var message_1 = "multiple inline(LaTeX style) with underscores";
|
||||
|
||||
var input_string_2 = 'x \\\\[a_{0}+ b_{T}\\\\] y \\\\[a_{0}+ b_{T}\\\\] z';
|
||||
var expected_result_2 = ['x @@0@@ y @@1@@ z', ['\\\\[a_{0}+ b_{T}\\\\]','\\\\[a_{0}+ b_{T}\\\\]']];
|
||||
var message_2 = "multiple equation (LaTeX style) with underscores";
|
||||
|
||||
var input_string_3 = 'x $a_{0}+ b_{T}$ y $a_{0}+ b_{T}$ z';
|
||||
var expected_result_3 = ['x @@0@@ y @@1@@ z',['$a_{0}+ b_{T}$','$a_{0}+ b_{T}$']];
|
||||
var message_3 = "multiple inline(TeX style) with underscores";
|
||||
|
||||
var input_string_4 = 'x $$a_{0}+ b_{T}$$ y $$a_{0}+ b_{T}$$ z';
|
||||
var expected_result_4 = ['x @@0@@ y @@1@@ z', ['$$a_{0}+ b_{T}$$','$$a_{0}+ b_{T}$$']];
|
||||
var message_4 = "multiple equation(TeX style) with underscores";
|
||||
|
||||
var input_string_5 = 'x \\begin{equation}a_{0}+ b_{T}\\end{equation} y \\begin{equation}a_{0}+ b_{T}\\end{equation} z';
|
||||
var expected_result_5 = ['x @@0@@ y @@1@@ z',['\\begin{equation}a_{0}+ b_{T}\\end{equation}','\\begin{equation}a_{0}+ b_{T}\\end{equation}']];
|
||||
var message_5 = "multiple equations with underscores";
|
||||
|
||||
mathjax_render_test(input_string_1, expected_result_1, message_1);
|
||||
mathjax_render_test(input_string_2, expected_result_2, message_2);
|
||||
mathjax_render_test(input_string_3, expected_result_3, message_3);
|
||||
mathjax_render_test(input_string_4, expected_result_4, message_4);
|
||||
mathjax_render_test(input_string_5, expected_result_5, message_5);
|
||||
});
|
258
.venv/Lib/site-packages/notebook/tests/notebook/output.js
Normal file
258
.venv/Lib/site-packages/notebook/tests/notebook/output.js
Normal file
@ -0,0 +1,258 @@
|
||||
//
|
||||
// Various output tests
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
|
||||
this.compare_outputs = function(results, expected) {
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var r = results[i];
|
||||
var ex = expected[i];
|
||||
this.test.assertEquals(r.output_type, ex.output_type, "output " + i + " = " + r.output_type);
|
||||
if (r.output_type === 'stream') {
|
||||
this.test.assertEquals(r.name, ex.name, "stream " + i + " = " + r.name);
|
||||
this.test.assertEquals(r.text, ex.text, "content " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.test_coalesced_output = function (msg, code, expected) {
|
||||
this.then(function () {
|
||||
this.echo("Test coalesced output: " + msg);
|
||||
});
|
||||
|
||||
this.thenEvaluate(function (code) {
|
||||
IPython.notebook.insert_cell_at_index("code", 0);
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text(code);
|
||||
cell.execute();
|
||||
}, {code: code});
|
||||
|
||||
this.wait_for_output(0);
|
||||
|
||||
this.then(function () {
|
||||
var results = this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
return cell.output_area.outputs;
|
||||
});
|
||||
this.test.assertEquals(results.length, expected.length, "correct number of outputs");
|
||||
this.compare_outputs(results, expected);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.insert_cell_at_index("code", 0);
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text([
|
||||
"import sys",
|
||||
"from IPython.display import display, clear_output"
|
||||
].join("\n")
|
||||
);
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.test_coalesced_output("stdout", [
|
||||
"print(1)",
|
||||
"sys.stdout.flush()",
|
||||
"print(2)",
|
||||
"sys.stdout.flush()",
|
||||
"print(3)"
|
||||
].join("\n"), [{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "1\n2\n3\n"
|
||||
}]
|
||||
);
|
||||
|
||||
this.test_coalesced_output("stdout+sdterr", [
|
||||
"print(1)",
|
||||
"sys.stdout.flush()",
|
||||
"print(2)",
|
||||
"print(3, file=sys.stderr)"
|
||||
].join("\n"), [{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "1\n2\n"
|
||||
},{
|
||||
output_type: "stream",
|
||||
name: "stderr",
|
||||
text: "3\n"
|
||||
}]
|
||||
);
|
||||
|
||||
this.test_coalesced_output("display splits streams", [
|
||||
"print(1)",
|
||||
"sys.stdout.flush()",
|
||||
"display(2)",
|
||||
"print(3)"
|
||||
].join("\n"), [{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "1\n"
|
||||
},{
|
||||
output_type: "display_data",
|
||||
},{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "3\n"
|
||||
}]
|
||||
);
|
||||
this.test_coalesced_output("test nested svg", [
|
||||
'from IPython.display import SVG',
|
||||
'nested_svg="""',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" >',
|
||||
' <svg x="0">',
|
||||
' <rect x="10" y="10" height="80" width="80" style="fill: #0000ff"/>',
|
||||
' </svg>',
|
||||
' <svg x="100">',
|
||||
' <rect x="10" y="10" height="80" width="80" style="fill: #00cc00"/>',
|
||||
' </svg>',
|
||||
'</svg>"""',
|
||||
'SVG(nested_svg)'
|
||||
].join("\n"), [{
|
||||
output_type: "execute_result",
|
||||
data: {
|
||||
"text/plain" : "<IPython.core.display.SVG object>",
|
||||
"image/svg+xml": [
|
||||
'<svg height="200" width="100" xmlns="http://www.w3.org/2000/svg">',
|
||||
' <svg x="0">',
|
||||
' <rect height="80" style="fill: #0000ff" width="80" x="10" y="10"/>',
|
||||
' </svg>',
|
||||
' <svg x="100">',
|
||||
' <rect height="80" style="fill: #00cc00" width="80" x="10" y="10"/>',
|
||||
' </svg>',
|
||||
'</svg>'].join("\n")
|
||||
},
|
||||
}]
|
||||
);
|
||||
|
||||
this.then(function () {
|
||||
this.echo("Test output callback overrides");
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.insert_cell_at_index("code", 0);
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text(["print(1)",
|
||||
"sys.stdout.flush()",
|
||||
"print(2)",
|
||||
"sys.stdout.flush()",
|
||||
"print(3, file=sys.stderr)",
|
||||
"sys.stdout.flush()",
|
||||
"display(2)",
|
||||
"clear_output()",
|
||||
"sys.stdout.flush()",
|
||||
"print('remove handler')",
|
||||
"sys.stdout.flush()",
|
||||
"print('back to cell')",
|
||||
"sys.stdout.flush()",
|
||||
].join('\n'));
|
||||
cell.execute();
|
||||
var kernel = IPython.notebook.kernel;
|
||||
var msg_id = cell.last_msg_id;
|
||||
var callback_id = 'mycallbackid'
|
||||
cell.iopub_messages = [];
|
||||
var add_msg = function(msg) {
|
||||
if (msg.content.text==="remove handler\n") {
|
||||
kernel.output_callback_overrides_pop(msg_id);
|
||||
}
|
||||
msg.content.output_type = msg.msg_type;
|
||||
cell.iopub_messages.push(msg.content);
|
||||
};
|
||||
kernel.set_callbacks_for_msg(callback_id, {
|
||||
iopub: {
|
||||
output: add_msg,
|
||||
clear_output: add_msg,
|
||||
}
|
||||
}, false);
|
||||
kernel.output_callback_overrides_push(msg_id, callback_id);
|
||||
});
|
||||
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var expected_callback = [{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "1\n"
|
||||
}, {
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "2\n"
|
||||
}, {
|
||||
output_type: "stream",
|
||||
name: "stderr",
|
||||
text: "3\n"
|
||||
},{
|
||||
output_type: "display_data",
|
||||
},{
|
||||
output_type: "clear_output",
|
||||
},{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "remove handler\n"
|
||||
},]
|
||||
var expected_cell = [{
|
||||
output_type: "stream",
|
||||
name: "stdout",
|
||||
text: "back to cell\n"
|
||||
}]
|
||||
var returned = this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
return [cell.output_area.outputs, cell.iopub_messages];
|
||||
});
|
||||
var cell_results = returned[0];
|
||||
var callback_results = returned[1];
|
||||
this.test.assertEquals(cell_results.length, expected_cell.length, "correct number of cell outputs");
|
||||
this.test.assertEquals(callback_results.length, expected_callback.length, "correct number of callback outputs");
|
||||
this.compare_outputs(cell_results, expected_cell);
|
||||
this.compare_outputs(callback_results, expected_callback);
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
this.echo("Test output callback overrides get execute_results messages too");
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.insert_cell_at_index("code", 0);
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text("'end'");
|
||||
cell.execute();
|
||||
var kernel = IPython.notebook.kernel;
|
||||
var msg_id = cell.last_msg_id;
|
||||
var callback_id = 'mycallbackid2'
|
||||
cell.iopub_messages = [];
|
||||
var add_msg = function(msg) {
|
||||
msg.content.output_type = msg.msg_type;
|
||||
cell.iopub_messages.push(msg.content);
|
||||
};
|
||||
kernel.set_callbacks_for_msg(callback_id, {
|
||||
iopub: {
|
||||
output: add_msg,
|
||||
clear_output: add_msg,
|
||||
}
|
||||
}, false);
|
||||
kernel.output_callback_overrides_push(msg_id, callback_id);
|
||||
});
|
||||
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var expected_callback = [{
|
||||
output_type: "execute_result",
|
||||
data: {
|
||||
"text/plain" : "'end'"
|
||||
}
|
||||
}];
|
||||
var expected_cell = [];
|
||||
var returned = this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
return [cell.output_area.outputs, cell.iopub_messages];
|
||||
});
|
||||
var cell_results = returned[0];
|
||||
var callback_results = returned[1];
|
||||
this.test.assertEquals(cell_results.length, expected_cell.length, "correct number of cell outputs");
|
||||
this.test.assertEquals(callback_results.length, expected_callback.length, "correct number of callback outputs");
|
||||
this.compare_outputs(callback_results, expected_callback);
|
||||
});
|
||||
});
|
254
.venv/Lib/site-packages/notebook/tests/notebook/roundtrip.js
Normal file
254
.venv/Lib/site-packages/notebook/tests/notebook/roundtrip.js
Normal file
@ -0,0 +1,254 @@
|
||||
// Test opening a rich notebook, saving it, and reopening it again.
|
||||
//
|
||||
//toJSON fromJSON toJSON and do a string comparison
|
||||
|
||||
|
||||
// this is just a copy of OutputArea.mime_mape_r in IPython/html/static/notebook/js/outputarea.js
|
||||
mime = {
|
||||
"text" : "text/plain",
|
||||
"html" : "text/html",
|
||||
"svg" : "image/svg+xml",
|
||||
"png" : "image/png",
|
||||
"jpeg" : "image/jpeg",
|
||||
"latex" : "text/latex",
|
||||
"json" : "application/json",
|
||||
"javascript" : "application/javascript",
|
||||
};
|
||||
|
||||
var black_dot_jpeg="u\"\"\"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDACodICUgGiolIiUvLSoyP2lEPzo6P4FcYUxpmYagnpaG\nk5GovfLNqLPltZGT0v/V5fr/////o8v///////L/////2wBDAS0vLz83P3xERHz/rpOu////////\n////////////////////////////////////////////////////////////wgARCAABAAEDAREA\nAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAABP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEA\nAhADEAAAARn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAA\nAAAAAAAA/9oACAEDAQE/AX//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AX//xAAUEAEA\nAAAAAAAAAAAAAAAAAAAA/9oACAEBAAY/An//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nIX//2gAMAwEAAgADAAAAEB//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/EH//xAAUEQEA\nAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/EH//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nEH//2Q==\"\"\"";
|
||||
var black_dot_png = 'u\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QA\\niAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AA\\nAAACAAHiIbwzAAAAAElFTkSuQmCC\"';
|
||||
var svg = "\"<svg width='1cm' height='1cm' viewBox='0 0 1000 500'><defs><style>rect {fill:red;}; </style></defs><rect id='r1' x='200' y='100' width='600' height='300' /></svg>\"";
|
||||
|
||||
// helper function to ensure that the short_name is found in the toJSON
|
||||
// representation, while the original in-memory cell retains its long mimetype
|
||||
// name, and that fromJSON also gets its long mimetype name
|
||||
function assert_has(short_name, json, result, result2) {
|
||||
var long_name = mime[short_name];
|
||||
this.test.assertFalse(json[0].data.hasOwnProperty(short_name),
|
||||
"toJSON() representation doesn't use " + short_name);
|
||||
this.test.assertTrue(json[0].data.hasOwnProperty(long_name),
|
||||
'toJSON() representation uses ' + long_name);
|
||||
this.test.assertTrue(result.data.hasOwnProperty(long_name),
|
||||
'toJSON() original embedded JSON keeps ' + long_name);
|
||||
this.test.assertTrue(result2.data.hasOwnProperty(long_name),
|
||||
'fromJSON() embedded ' + short_name + ' gets mime key ' + long_name);
|
||||
}
|
||||
|
||||
// helper function for checkout that the first two cells have a particular
|
||||
// output_type (either 'execute_result' or 'display_data'), and checks the to/fromJSON
|
||||
// for a set of mimetype keys, ensuring the old short names ('javascript', 'text',
|
||||
// 'png', etc) are not used.
|
||||
function check_output_area(output_type, keys) {
|
||||
this.wait_for_output(0);
|
||||
var json = this.evaluate(function() {
|
||||
var json = IPython.notebook.get_cell(0).output_area.toJSON();
|
||||
// appended cell will initially be empty, let's add some output
|
||||
IPython.notebook.get_cell(1).output_area.fromJSON(json);
|
||||
return json;
|
||||
});
|
||||
// The evaluate call above happens asynchronously: wait for cell[1] to have output
|
||||
this.wait_for_output(1);
|
||||
var result = this.get_output_cell(0);
|
||||
var result2 = this.get_output_cell(1);
|
||||
this.test.assertEquals(result.output_type, output_type,
|
||||
'testing ' + output_type + ' for ' + keys.join(' and '));
|
||||
|
||||
for (var idx in keys) {
|
||||
assert_has.apply(this, [keys[idx], json, result, result2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper function to clear the first two cells, set the text of and execute
|
||||
// the first one
|
||||
function clear_and_execute(that, code) {
|
||||
that.evaluate(function() {
|
||||
IPython.notebook.get_cell(0).clear_output();
|
||||
IPython.notebook.get_cell(1).clear_output();
|
||||
});
|
||||
that.then(function () {
|
||||
that.set_cell_text(0, code);
|
||||
that.execute_cell(0);
|
||||
that.wait_for_idle();
|
||||
});
|
||||
}
|
||||
|
||||
casper.notebook_test(function () {
|
||||
this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
// "we have to make messes to find out who we are"
|
||||
cell.set_text([
|
||||
"%%javascript",
|
||||
"IPython.notebook.insert_cell_below('code')"
|
||||
].join('\n')
|
||||
);
|
||||
});
|
||||
|
||||
this.execute_cell_then(0, function () {
|
||||
var result = this.get_output_cell(0);
|
||||
var num_cells = this.get_cells_length();
|
||||
this.test.assertEquals(num_cells, 2, '%%javascript magic works');
|
||||
this.test.assertTrue(result.data.hasOwnProperty('application/javascript'),
|
||||
'testing JS embedded with mime key');
|
||||
});
|
||||
|
||||
//this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
|
||||
this.then(function () {
|
||||
clear_and_execute(this, [
|
||||
"%%javascript",
|
||||
"var a=5;"
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
|
||||
this.then(function () {
|
||||
check_output_area.apply(this, ['display_data', ['javascript']]);
|
||||
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this, '%lsmagic');
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'json']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"x = %lsmagic\nfrom IPython.display import display; display(x)");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'json']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Latex; Latex('$X^2$')");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'latex']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Latex, display; display(Latex('$X^2$'))");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'latex']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import HTML; HTML('<b>it works!</b>')");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'html']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from base64 import b64decode;" +
|
||||
"black_dot_png = b64decode(" + black_dot_png + ");" +
|
||||
"black_dot_jpeg = b64decode(" + black_dot_jpeg + ")"
|
||||
);
|
||||
});
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import HTML, display; display(HTML('<b>it works!</b>'))");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'html']]);
|
||||
});
|
||||
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Image; Image(black_dot_png)");
|
||||
});
|
||||
this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'png']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Image, display; display(Image(black_dot_png))");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'png']]);
|
||||
});
|
||||
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Image; Image(black_dot_jpeg, format='jpeg')");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'jpeg']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.display import Image, display; display(Image(black_dot_jpeg, format='jpeg'))");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'jpeg']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.core.display import SVG; SVG(" + svg + ")");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['execute_result', ['text', 'svg']]);
|
||||
});
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this,
|
||||
"from IPython.core.display import SVG, display; display(SVG(" + svg + "))");
|
||||
});
|
||||
|
||||
this.then(function ( ) {
|
||||
check_output_area.apply(this, ['display_data', ['text', 'svg']]);
|
||||
});
|
||||
|
||||
this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
|
||||
|
||||
this.then(function() {
|
||||
clear_and_execute(this, [
|
||||
"from IPython.core.formatters import HTMLFormatter",
|
||||
"x = HTMLFormatter()",
|
||||
"x.format_type = 'text/superfancymimetype'",
|
||||
"get_ipython().display_formatter.formatters['text/superfancymimetype'] = x",
|
||||
"from IPython.display import HTML, display",
|
||||
'display(HTML("yo"))',
|
||||
"HTML('hello')"].join('\n')
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
this.wait_for_output(0, 1);
|
||||
|
||||
this.then(function () {
|
||||
var long_name = 'text/superfancymimetype';
|
||||
var result = this.get_output_cell(0);
|
||||
this.test.assertTrue(result.data.hasOwnProperty(long_name),
|
||||
'display_data custom mimetype ' + long_name);
|
||||
result = this.get_output_cell(0, 1);
|
||||
this.test.assertTrue(result.data.hasOwnProperty(long_name),
|
||||
'execute_result custom mimetype ' + long_name);
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Test validation in append_output
|
||||
//
|
||||
// Invalid output data is stripped and logged.
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
// this.printLog();
|
||||
var messages = [];
|
||||
this.on('remote.message', function (msg) {
|
||||
messages.push(msg);
|
||||
});
|
||||
|
||||
this.evaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text( "dp = get_ipython().display_pub\n" +
|
||||
"dp.publish({'text/plain' : '5', 'image/png' : 5})"
|
||||
);
|
||||
cell.execute();
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
this.on('remote.message', function () {});
|
||||
|
||||
this.then(function () {
|
||||
var output = this.get_output_cell(0);
|
||||
this.test.assert(messages.length > 0, "Captured log message");
|
||||
this.test.assertEquals(messages[messages.length-1].substr(0,26), "Invalid type for image/png", "Logged Invalid type message");
|
||||
this.test.assertEquals(output.data['image/png'], undefined, "Non-string png data was stripped");
|
||||
this.test.assertEquals(output.data['text/plain'], '5', "text data is fine");
|
||||
});
|
||||
});
|
123
.venv/Lib/site-packages/notebook/tests/notebook/tags.js
Normal file
123
.venv/Lib/site-packages/notebook/tests/notebook/tags.js
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Various tagging
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
|
||||
function get_tag_metadata () {
|
||||
return casper.evaluate(function () {
|
||||
return Jupyter.notebook.get_cell(0).metadata.tags;
|
||||
});
|
||||
}
|
||||
|
||||
function get_tag_elements () {
|
||||
return casper.evaluate(function () {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
return $.map(
|
||||
cell.element.find('.cell-tag'),
|
||||
function (el) {
|
||||
return $(el.childNodes[0]).text();
|
||||
}
|
||||
);
|
||||
})
|
||||
}
|
||||
// wait for cell toolbar item to be present (necessary?)
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function() {
|
||||
return $('#menu-cell-toolbar-submenu')
|
||||
.find('[data-name=Tags]')
|
||||
.length;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var tag_items = this.evaluate(function() {
|
||||
return $('#menu-cell-toolbar-submenu')
|
||||
.find('[data-name=Tags]')
|
||||
.length;
|
||||
});
|
||||
this.test.assertEquals(
|
||||
tag_items,
|
||||
1,
|
||||
"Tag cell toolbar item is present");
|
||||
})
|
||||
|
||||
// activate tags toolbar via menubar
|
||||
this.thenEvaluate(function () {
|
||||
$('#menu-cell-toolbar-submenu')
|
||||
.find('[data-name=Tags]')
|
||||
.find('a')
|
||||
.click();
|
||||
});
|
||||
|
||||
// wait for one tag container
|
||||
this.waitForSelector('.tags_button_container');
|
||||
this.then(function () {
|
||||
var elements = this.evaluate(function () {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
var tag_input = cell.element
|
||||
.find('.tags-input input');
|
||||
return tag_input.length;
|
||||
})
|
||||
this.test.assertEquals(
|
||||
elements,
|
||||
1,
|
||||
"tags-input element exists");
|
||||
})
|
||||
|
||||
// apply some tags
|
||||
this.thenEvaluate(function () {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
var tag_input = cell.element
|
||||
.find('.tags-input input');
|
||||
// add some tags separated by commas and spaces,
|
||||
// including duplicates
|
||||
tag_input.val('tag1, tüg2 tåg3,tag4,,,tag5 tag1');
|
||||
cell.element
|
||||
.find('.tags-input button')
|
||||
.click();
|
||||
});
|
||||
|
||||
var all_tags = ['tag1', 'tüg2', 'tåg3', 'tag4', 'tag5'];
|
||||
// verify that tags are applied
|
||||
this.then(function () {
|
||||
var tags = get_tag_metadata();
|
||||
this.test.assertEquals(
|
||||
tags,
|
||||
all_tags,
|
||||
"tags have been applied to metadata"
|
||||
);
|
||||
|
||||
var tag_elements = get_tag_elements();
|
||||
this.test.assertEquals(
|
||||
tag_elements,
|
||||
all_tags,
|
||||
"tags elements have been added"
|
||||
);
|
||||
});
|
||||
|
||||
// remove first tag by clicking 'X'
|
||||
this.thenEvaluate(function () {
|
||||
var cell = Jupyter.notebook.get_cell(0);
|
||||
var X = cell.element
|
||||
.find('.tag-container .remove-tag-btn')
|
||||
.first();
|
||||
X.click();
|
||||
});
|
||||
|
||||
this.then(function () {
|
||||
var expected_tags = all_tags.slice(1);
|
||||
var tags = get_tag_metadata();
|
||||
this.test.assertEquals(
|
||||
tags,
|
||||
expected_tags,
|
||||
"clicking X removes tags from metadata"
|
||||
);
|
||||
|
||||
var tag_elements = get_tag_elements();
|
||||
this.test.assertEquals(
|
||||
tag_elements,
|
||||
expected_tags,
|
||||
"clicking X removes tags from UI"
|
||||
);
|
||||
})
|
||||
});
|
143
.venv/Lib/site-packages/notebook/tests/selenium/conftest.py
Normal file
143
.venv/Lib/site-packages/notebook/tests/selenium/conftest.py
Normal file
@ -0,0 +1,143 @@
|
||||
import json
|
||||
import nbformat
|
||||
from nbformat.v4 import new_notebook, new_code_cell
|
||||
import os
|
||||
import pytest
|
||||
import requests
|
||||
from subprocess import Popen
|
||||
import sys
|
||||
from tempfile import mkstemp
|
||||
from testpath.tempdir import TemporaryDirectory
|
||||
import time
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from selenium.webdriver import Firefox, Remote, Chrome
|
||||
from .utils import Notebook
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
def _wait_for_server(proc, info_file_path):
|
||||
"""Wait 30 seconds for the notebook server to start"""
|
||||
for i in range(300):
|
||||
if proc.poll() is not None:
|
||||
raise RuntimeError("Notebook server failed to start")
|
||||
if os.path.exists(info_file_path):
|
||||
try:
|
||||
with open(info_file_path) as f:
|
||||
return json.load(f)
|
||||
except ValueError:
|
||||
# If the server is halfway through writing the file, we may
|
||||
# get invalid JSON; it should be ready next iteration.
|
||||
pass
|
||||
time.sleep(0.1)
|
||||
raise RuntimeError("Didn't find %s in 30 seconds", info_file_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def notebook_server():
|
||||
info = {}
|
||||
with TemporaryDirectory() as td:
|
||||
nbdir = info['nbdir'] = pjoin(td, 'notebooks')
|
||||
os.makedirs(pjoin(nbdir, 'sub ∂ir1', 'sub ∂ir 1a'))
|
||||
os.makedirs(pjoin(nbdir, 'sub ∂ir2', 'sub ∂ir 1b'))
|
||||
|
||||
info['extra_env'] = {
|
||||
'JUPYTER_CONFIG_DIR': pjoin(td, 'jupyter_config'),
|
||||
'JUPYTER_RUNTIME_DIR': pjoin(td, 'jupyter_runtime'),
|
||||
'IPYTHONDIR': pjoin(td, 'ipython'),
|
||||
}
|
||||
env = os.environ.copy()
|
||||
env.update(info['extra_env'])
|
||||
|
||||
command = [sys.executable, '-m', 'notebook',
|
||||
'--no-browser',
|
||||
'--notebook-dir', nbdir,
|
||||
# run with a base URL that would be escaped,
|
||||
# to test that we don't double-escape URLs
|
||||
'--NotebookApp.base_url=/a@b/',
|
||||
]
|
||||
print("command=", command)
|
||||
proc = info['popen'] = Popen(command, cwd=nbdir, env=env)
|
||||
info_file_path = pjoin(td, 'jupyter_runtime',
|
||||
f'nbserver-{proc.pid:d}.json')
|
||||
info.update(_wait_for_server(proc, info_file_path))
|
||||
|
||||
print("Notebook server info:", info)
|
||||
yield info
|
||||
|
||||
# Shut the server down
|
||||
requests.post(urljoin(info['url'], 'api/shutdown'),
|
||||
headers={'Authorization': 'token '+info['token']})
|
||||
|
||||
|
||||
def make_sauce_driver():
|
||||
"""This function helps travis create a driver on Sauce Labs.
|
||||
|
||||
This function will err if used without specifying the variables expected
|
||||
in that context.
|
||||
"""
|
||||
|
||||
username = os.environ["SAUCE_USERNAME"]
|
||||
access_key = os.environ["SAUCE_ACCESS_KEY"]
|
||||
capabilities = {
|
||||
"tunnel-identifier": os.environ["TRAVIS_JOB_NUMBER"],
|
||||
"build": os.environ["TRAVIS_BUILD_NUMBER"],
|
||||
"tags": [os.environ['TRAVIS_PYTHON_VERSION'], 'CI'],
|
||||
"platform": "Windows 10",
|
||||
"browserName": os.environ['JUPYTER_TEST_BROWSER'],
|
||||
"version": "latest",
|
||||
}
|
||||
if capabilities['browserName'] == 'firefox':
|
||||
# Attempt to work around issue where browser loses authentication
|
||||
capabilities['version'] = '57.0'
|
||||
hub_url = f"{username}:{access_key}@localhost:4445"
|
||||
print("Connecting remote driver on Sauce Labs")
|
||||
driver = Remote(desired_capabilities=capabilities,
|
||||
command_executor=f"http://{hub_url}/wd/hub")
|
||||
return driver
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def selenium_driver():
|
||||
if os.environ.get('SAUCE_USERNAME'):
|
||||
driver = make_sauce_driver()
|
||||
elif os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome':
|
||||
driver = Chrome()
|
||||
else:
|
||||
driver = Firefox()
|
||||
|
||||
yield driver
|
||||
|
||||
# Teardown
|
||||
driver.quit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def authenticated_browser(selenium_driver, notebook_server):
|
||||
selenium_driver.jupyter_server_info = notebook_server
|
||||
selenium_driver.get("{url}?token={token}".format(**notebook_server))
|
||||
return selenium_driver
|
||||
|
||||
@pytest.fixture
|
||||
def notebook(authenticated_browser):
|
||||
tree_wh = authenticated_browser.current_window_handle
|
||||
yield Notebook.new_notebook(authenticated_browser)
|
||||
authenticated_browser.switch_to.window(tree_wh)
|
||||
|
||||
@pytest.fixture
|
||||
def prefill_notebook(selenium_driver, notebook_server):
|
||||
def inner(cells):
|
||||
cells = [new_code_cell(c) if isinstance(c, str) else c
|
||||
for c in cells]
|
||||
nb = new_notebook(cells=cells)
|
||||
fd, path = mkstemp(dir=notebook_server['nbdir'], suffix='.ipynb')
|
||||
with open(fd, 'w', encoding='utf-8') as f:
|
||||
nbformat.write(nb, f)
|
||||
fname = os.path.basename(path)
|
||||
selenium_driver.get(
|
||||
"{url}notebooks/{}?token={token}".format(fname, **notebook_server)
|
||||
)
|
||||
return Notebook(selenium_driver)
|
||||
|
||||
return inner
|
@ -0,0 +1,53 @@
|
||||
"""Utilities for driving Selenium interactively to develop tests.
|
||||
|
||||
These are not used in the tests themselves - rather, the developer writing tests
|
||||
can use them to experiment with Selenium.
|
||||
"""
|
||||
from selenium.webdriver import Firefox
|
||||
|
||||
from notebook.tests.selenium.utils import Notebook
|
||||
from notebook.notebookapp import list_running_servers
|
||||
|
||||
class NoServerError(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def quick_driver(lab=False):
|
||||
"""Quickly create a selenium driver pointing at an active noteboook server.
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.quick_selenium import quick_driver
|
||||
driver = quick_driver
|
||||
|
||||
Note: you need to manually close the driver that opens with driver.quit()
|
||||
"""
|
||||
try:
|
||||
server = list(list_running_servers())[0]
|
||||
except IndexError as e:
|
||||
raise NoServerError('You need a server running before you can run '
|
||||
'this command') from e
|
||||
driver = Firefox()
|
||||
auth_url = '{url}?token={token}'.format(**server)
|
||||
driver.get(auth_url)
|
||||
|
||||
# If this redirects us to a lab page and we don't want that;
|
||||
# then we need to redirect ourselves to the classic notebook view
|
||||
if driver.current_url.endswith('/lab') and not lab:
|
||||
driver.get(driver.current_url.rstrip('lab')+'tree')
|
||||
return driver
|
||||
|
||||
|
||||
def quick_notebook():
|
||||
"""Quickly create a new classic notebook in a selenium driver
|
||||
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.quick_selenium import quick_notebook
|
||||
nb = quick_notebook()
|
||||
|
||||
Note: you need to manually close the driver that opens with nb.browser.quit()
|
||||
"""
|
||||
return Notebook.new_notebook(quick_driver())
|
@ -0,0 +1,50 @@
|
||||
"""Tests buffering of execution requests."""
|
||||
|
||||
from .utils import wait_for_selector
|
||||
|
||||
|
||||
def wait_for_cell_text_output(notebook, index):
|
||||
cell = notebook.cells[index]
|
||||
output = wait_for_selector(cell, ".output_text", single=True)
|
||||
return output.text
|
||||
|
||||
|
||||
def wait_for_kernel_ready(notebook):
|
||||
wait_for_selector(notebook.browser, ".kernel_idle_icon")
|
||||
|
||||
|
||||
def test_kernels_buffer_without_conn(prefill_notebook):
|
||||
"""Test that execution request made while disconnected is buffered."""
|
||||
notebook = prefill_notebook(["print(1 + 2)"])
|
||||
|
||||
wait_for_kernel_ready(notebook)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.stop_channels();")
|
||||
notebook.execute_cell(0)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.reconnect();")
|
||||
wait_for_kernel_ready(notebook)
|
||||
|
||||
assert wait_for_cell_text_output(notebook, 0) == "3"
|
||||
|
||||
|
||||
def test_buffered_cells_execute_in_order(prefill_notebook):
|
||||
"""Test that buffered requests execute in order."""
|
||||
notebook = prefill_notebook(['', 'k=1', 'k+=1', 'k*=3', 'print(k)'])
|
||||
|
||||
# Repeated execution of cell queued up in the kernel executes
|
||||
# each execution request in order.
|
||||
wait_for_kernel_ready(notebook)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.stop_channels();")
|
||||
# k == 1
|
||||
notebook.execute_cell(1)
|
||||
# k == 2
|
||||
notebook.execute_cell(2)
|
||||
# k == 6
|
||||
notebook.execute_cell(3)
|
||||
# k == 7
|
||||
notebook.execute_cell(2)
|
||||
notebook.execute_cell(4)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.reconnect();")
|
||||
wait_for_kernel_ready(notebook)
|
||||
|
||||
# Check that current value of k is 7
|
||||
assert wait_for_cell_text_output(notebook, 4) == "7"
|
@ -0,0 +1,27 @@
|
||||
"""Tests clipboard by copying, cutting and pasting multiple cells"""
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import wait_for_selector, wait_for_xpath
|
||||
|
||||
def test_clipboard_multiselect(prefill_notebook):
|
||||
notebook = prefill_notebook(['', '1', '2', '3', '4', '5a', '6b', '7c', '8d'])
|
||||
|
||||
assert notebook.get_cells_contents() == ['', '1', '2', '3', '4', '5a', '6b', '7c', '8d']
|
||||
|
||||
# Select the first 3 cells with value and replace the last 3
|
||||
[notebook.body.send_keys(Keys.UP) for i in range(8)]
|
||||
notebook.select_cell_range(1, 3)
|
||||
notebook.body.send_keys("c")
|
||||
notebook.select_cell_range(6, 8)
|
||||
wait_for_xpath(notebook.browser, '//a[text()="Edit"]', single=True).click()
|
||||
wait_for_selector(notebook.browser, '#paste_cell_replace', single=True).click()
|
||||
|
||||
assert notebook.get_cells_contents() == ['', '1', '2', '3', '4', '5a', '1', '2', '3']
|
||||
|
||||
# Select the last four cells, cut them and paste them below the first cell
|
||||
notebook.select_cell_range(5, 8)
|
||||
wait_for_selector(notebook.browser, '.fa-cut.fa', single=True).click()
|
||||
for i in range(8):
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys("v")
|
||||
|
||||
assert notebook.get_cells_contents() == ['', '5a', '1', '2', '3', '1', '2', '3', '4']
|
@ -0,0 +1,65 @@
|
||||
import os
|
||||
|
||||
from notebook.utils import url_path_join
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
class PageError(Exception):
|
||||
"""Error for an action being incompatible with the current jupyter web page."""
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
|
||||
def url_in_tree(browser, url=None):
|
||||
if url is None:
|
||||
url = browser.current_url
|
||||
tree_url = url_path_join(browser.jupyter_server_info['url'], 'tree')
|
||||
return url.startswith(tree_url)
|
||||
|
||||
|
||||
def get_list_items(browser):
|
||||
"""Gets list items from a directory listing page
|
||||
|
||||
Raises PageError if not in directory listing page (url has tree in it)
|
||||
"""
|
||||
if not url_in_tree(browser):
|
||||
raise PageError("You are not in the notebook's file tree view."
|
||||
"This function can only be used the file tree context.")
|
||||
# we need to make sure that at least one item link loads
|
||||
wait_for_selector(browser, '.item_link')
|
||||
|
||||
return [{
|
||||
'link': a.get_attribute('href'),
|
||||
'label': a.find_element_by_class_name('item_name').text,
|
||||
'element': a,
|
||||
} for a in browser.find_elements_by_class_name('item_link')]
|
||||
|
||||
def only_dir_links(browser):
|
||||
"""Return only links that point at other directories in the tree"""
|
||||
items = get_list_items(browser)
|
||||
return [i for i in items
|
||||
if url_in_tree(browser, i['link']) and i['label'] != '..']
|
||||
|
||||
def test_items(authenticated_browser):
|
||||
visited_dict = {}
|
||||
# Going down the tree to collect links
|
||||
while True:
|
||||
wait_for_selector(authenticated_browser, '.item_link')
|
||||
current_url = authenticated_browser.current_url
|
||||
items = visited_dict[current_url] = only_dir_links(authenticated_browser)
|
||||
try:
|
||||
item = items[0]
|
||||
item["element"].click()
|
||||
assert authenticated_browser.current_url == item['link']
|
||||
except IndexError:
|
||||
break
|
||||
# Going back up the tree while we still have unvisited links
|
||||
while visited_dict:
|
||||
current_items = only_dir_links(authenticated_browser)
|
||||
current_items_links = [item["link"] for item in current_items]
|
||||
stored_items = visited_dict.pop(authenticated_browser.current_url)
|
||||
stored_items_links = [item["link"] for item in stored_items]
|
||||
assert stored_items_links == current_items_links
|
||||
authenticated_browser.back()
|
||||
|
@ -0,0 +1,57 @@
|
||||
def cell_is_deletable(nb, index):
|
||||
JS = f'return Jupyter.notebook.get_cell({index}).is_deletable();'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def remove_all_cells(notebook):
|
||||
for i in range(len(notebook.cells)):
|
||||
notebook.delete_cell(0)
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_delete_cells(prefill_notebook):
|
||||
a, b, c = INITIAL_CELLS
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
# Validate initial state
|
||||
assert notebook.get_cells_contents() == [a, b, c]
|
||||
for cell in range(0, 3):
|
||||
assert cell_is_deletable(notebook, cell)
|
||||
|
||||
notebook.set_cell_metadata(0, 'deletable', 'false')
|
||||
notebook.set_cell_metadata(1, 'deletable', 0
|
||||
)
|
||||
assert not cell_is_deletable(notebook, 0)
|
||||
assert cell_is_deletable(notebook, 1)
|
||||
assert cell_is_deletable(notebook, 2)
|
||||
|
||||
# Try to delete cell a (should not be deleted)
|
||||
notebook.delete_cell(0)
|
||||
assert notebook.get_cells_contents() == [a, b, c]
|
||||
|
||||
# Try to delete cell b (should succeed)
|
||||
notebook.delete_cell(1)
|
||||
assert notebook.get_cells_contents() == [a, c]
|
||||
|
||||
# Try to delete cell c (should succeed)
|
||||
notebook.delete_cell(1)
|
||||
assert notebook.get_cells_contents() == [a]
|
||||
|
||||
# Change the deletable state of cell a
|
||||
notebook.set_cell_metadata(0, 'deletable', 'true')
|
||||
|
||||
# Try to delete cell a (should succeed)
|
||||
notebook.delete_cell(0)
|
||||
assert len(notebook.cells) == 1 # it contains an empty cell
|
||||
|
||||
# Make sure copied cells are deletable
|
||||
notebook.edit_cell(index=0, content=a)
|
||||
notebook.set_cell_metadata(0, 'deletable', 'false')
|
||||
assert not cell_is_deletable(notebook, 0)
|
||||
notebook.to_command_mode()
|
||||
notebook.current_cell.send_keys('cv')
|
||||
assert len(notebook.cells) == 2
|
||||
assert cell_is_deletable(notebook, 1)
|
||||
|
||||
notebook.set_cell_metadata(0, 'deletable', 'true') # to perform below test, remove all the cells
|
||||
remove_all_cells(notebook)
|
||||
assert len(notebook.cells) == 1 # notebook should create one automatically on empty notebook
|
@ -0,0 +1,65 @@
|
||||
"""Test display of images
|
||||
|
||||
The effect of shape metadata is validated, using Image(retina=True)
|
||||
"""
|
||||
|
||||
from .utils import wait_for_tag
|
||||
|
||||
|
||||
# 2x2 black square in b64 jpeg and png
|
||||
b64_image_data = {
|
||||
"image/png" : b'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAC0lEQVR4nGNgQAYAAA4AAamRc7EA\\nAAAASUVORK5CYII',
|
||||
"image/jpeg" : b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a\nHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy\nMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAACAAIDASIA\nAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA\nAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3\nODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm\np6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA\nAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx\nBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK\nU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3\nuLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5/ooo\noAoo2Qoo'
|
||||
}
|
||||
|
||||
|
||||
def imports(notebook):
|
||||
commands = [
|
||||
'import base64',
|
||||
'from IPython.display import display, Image',
|
||||
]
|
||||
notebook.edit_cell(index=0, content="\n".join(commands))
|
||||
notebook.execute_cell(0)
|
||||
|
||||
|
||||
def validate_img(notebook, cell_index, image_fmt, retina):
|
||||
"""Validate that image renders as expected."""
|
||||
|
||||
b64data = b64_image_data[image_fmt]
|
||||
commands = [
|
||||
f'b64data = {b64data}',
|
||||
'data = base64.decodebytes(b64data)',
|
||||
f'display(Image(data, retina={retina}))'
|
||||
]
|
||||
notebook.append("\n".join(commands))
|
||||
notebook.execute_cell(cell_index)
|
||||
|
||||
# Find the image element that was just displayed
|
||||
wait_for_tag(notebook.cells[cell_index], "img", single=True)
|
||||
img_element = notebook.cells[cell_index].find_element_by_tag_name("img")
|
||||
|
||||
src = img_element.get_attribute("src")
|
||||
prefix = src.split(',')[0]
|
||||
expected_prefix = f"data:{image_fmt};base64"
|
||||
assert prefix == expected_prefix
|
||||
|
||||
expected_size = 1 if retina else 2
|
||||
assert img_element.size["width"] == expected_size
|
||||
assert img_element.size["height"] == expected_size
|
||||
assert img_element.get_attribute("width") == str(expected_size)
|
||||
assert img_element.get_attribute("height") == str(expected_size)
|
||||
|
||||
|
||||
def test_display_image(notebook):
|
||||
imports(notebook)
|
||||
# PNG, non-retina
|
||||
validate_img(notebook, 1, "image/png", False)
|
||||
|
||||
# PNG, retina display
|
||||
validate_img(notebook, 2, "image/png", True)
|
||||
|
||||
# JPEG, non-retina
|
||||
validate_img(notebook, 3, "image/jpeg", False)
|
||||
|
||||
# JPEG, retina display
|
||||
validate_img(notebook, 4, "image/jpeg", True)
|
@ -0,0 +1,94 @@
|
||||
"""Test display isolation.
|
||||
|
||||
An object whose metadata contains an "isolated" tag must be isolated
|
||||
from the rest of the document.
|
||||
"""
|
||||
from .utils import wait_for_tag
|
||||
|
||||
|
||||
def test_display_isolation(notebook):
|
||||
import_ln = "from IPython.core.display import HTML, SVG, display, display_svg"
|
||||
notebook.edit_cell(index=0, content=import_ln)
|
||||
notebook.execute_cell(notebook.current_cell)
|
||||
try:
|
||||
isolated_html(notebook)
|
||||
isolated_svg(notebook)
|
||||
finally:
|
||||
# Ensure we switch from iframe back to default content even if test fails
|
||||
notebook.browser.switch_to.default_content()
|
||||
|
||||
|
||||
def isolated_html(notebook):
|
||||
"""Test HTML display isolation.
|
||||
|
||||
HTML styling rendered without isolation will affect the whole
|
||||
document, whereas styling applied with isolation will affect only
|
||||
the local display object.
|
||||
"""
|
||||
red = 'rgb(255, 0, 0)'
|
||||
blue = 'rgb(0, 0, 255)'
|
||||
test_str = "<div id='test'>Should turn red from non-isolation</div>"
|
||||
notebook.add_and_execute_cell(content=f"display(HTML({test_str!r}))")
|
||||
non_isolated = (
|
||||
f"<style>div{{color:{red};}}</style>"
|
||||
f"<div id='non-isolated'>Should be red</div>")
|
||||
display_ni = f"display(HTML({non_isolated!r}), metadata={{'isolated':False}})"
|
||||
notebook.add_and_execute_cell(content=display_ni)
|
||||
isolated = (
|
||||
f"<style>div{{color:{blue};}}</style>"
|
||||
f"<div id='isolated'>Should be blue</div>")
|
||||
display_i = f"display(HTML({isolated!r}), metadata={{'isolated':True}})"
|
||||
notebook.add_and_execute_cell(content=display_i)
|
||||
|
||||
iframe = wait_for_tag(notebook.browser, "iframe", single=True)
|
||||
|
||||
# The non-isolated div will be in the body
|
||||
non_isolated_div = notebook.body.find_element_by_id("non-isolated")
|
||||
assert non_isolated_div.value_of_css_property("color") == red
|
||||
|
||||
# The non-isolated styling will have affected the output of other cells
|
||||
test_div = notebook.body.find_element_by_id("test")
|
||||
assert test_div.value_of_css_property("color") == red
|
||||
|
||||
# The isolated div will be in an iframe, only that element will be blue
|
||||
notebook.browser.switch_to.frame(iframe)
|
||||
isolated_div = notebook.browser.find_element_by_id("isolated")
|
||||
assert isolated_div.value_of_css_property("color") == blue
|
||||
notebook.browser.switch_to.default_content()
|
||||
# Clean up the html test cells
|
||||
for i in range(1, len(notebook.cells)):
|
||||
notebook.delete_cell(1)
|
||||
|
||||
|
||||
def isolated_svg(notebook):
|
||||
"""Test that multiple isolated SVGs have different scopes.
|
||||
|
||||
Asserts that there no CSS leaks between two isolated SVGs.
|
||||
"""
|
||||
yellow = "rgb(255, 255, 0)"
|
||||
black = "rgb(0, 0, 0)"
|
||||
svg_1_str = f"""s1 = '''<svg width="1cm" height="1cm" viewBox="0 0 1000 500"><defs><style>rect {{fill:{yellow};}}; </style></defs><rect id="r1" x="200" y="100" width="600" height="300" /></svg>'''"""
|
||||
svg_2_str = """s2 = '''<svg width="1cm" height="1cm" viewBox="0 0 1000 500"><rect id="r2" x="200" y="100" width="600" height="300" /></svg>'''"""
|
||||
|
||||
notebook.add_and_execute_cell(content=svg_1_str)
|
||||
notebook.add_and_execute_cell(content=svg_2_str)
|
||||
notebook.add_and_execute_cell(
|
||||
content="display_svg(SVG(s1), metadata=dict(isolated=True))")
|
||||
notebook.add_and_execute_cell(
|
||||
content="display_svg(SVG(s2), metadata=dict(isolated=True))")
|
||||
iframes = wait_for_tag(notebook.browser, "iframe", wait_for_n=2)
|
||||
|
||||
# The first rectangle will be red
|
||||
notebook.browser.switch_to.frame(iframes[0])
|
||||
isolated_svg_1 = notebook.browser.find_element_by_id('r1')
|
||||
assert isolated_svg_1.value_of_css_property("fill") == yellow
|
||||
notebook.browser.switch_to.default_content()
|
||||
|
||||
# The second rectangle will be black
|
||||
notebook.browser.switch_to.frame(iframes[1])
|
||||
isolated_svg_2 = notebook.browser.find_element_by_id('r2')
|
||||
assert isolated_svg_2.value_of_css_property("fill") == black
|
||||
|
||||
# Clean up the svg test cells
|
||||
for i in range(1, len(notebook.cells)):
|
||||
notebook.delete_cell(1)
|
@ -0,0 +1,103 @@
|
||||
"""Tests arrow keys on both command and edit mode"""
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
def test_dualmode_arrows(notebook):
|
||||
|
||||
# Tests in command mode.
|
||||
# Setting up the cells to test the keys to move up.
|
||||
notebook.to_command_mode()
|
||||
[notebook.body.send_keys("b") for i in range(3)]
|
||||
|
||||
# Use both "k" and up arrow keys to moving up and enter a value.
|
||||
# Once located on the top cell, use the up arrow keys to prove the top cell is still selected.
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("2")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("1")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("0")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0", "1", "2", ""]
|
||||
|
||||
# Use the "k" key on the top cell as well
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" edit #1")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", ""]
|
||||
|
||||
# Setting up the cells to test the keys to move down
|
||||
[notebook.body.send_keys("j") for i in range(3)]
|
||||
[notebook.body.send_keys("a") for i in range(2)]
|
||||
notebook.body.send_keys("k")
|
||||
|
||||
# Use both "j" key and down arrow keys to moving down and enter a value.
|
||||
# Once located on the bottom cell, use the down arrow key to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("3")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("4")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("5")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5"]
|
||||
|
||||
# Use the "j" key on the top cell as well
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" edit #1")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1"]
|
||||
|
||||
# On the bottom cell, use both left and right arrow keys to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.LEFT)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(", #2")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1, #2"]
|
||||
notebook.body.send_keys(Keys.RIGHT)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" and #3")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1, #2 and #3"]
|
||||
|
||||
|
||||
# Tests in edit mode.
|
||||
# First, erase the previous content and then setup the cells to test the keys to move up.
|
||||
[notebook.browser.find_element_by_class_name("fa-cut.fa").click() for i in range(6)]
|
||||
[notebook.body.send_keys("b") for i in range(2)]
|
||||
notebook.body.send_keys("a")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
|
||||
# Use the up arrow key to move down and enter a value.
|
||||
# We will use the left arrow key to move one char to the left since moving up on last character only moves selector to the first one.
|
||||
# Once located on the top cell, use the up arrow key to prove the top cell is still selected.
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys("1")
|
||||
notebook.body.send_keys(Keys.LEFT)
|
||||
[notebook.body.send_keys(Keys.UP) for i in range(2)]
|
||||
notebook.body.send_keys("0")
|
||||
|
||||
# Use the down arrow key to move down and enter a value.
|
||||
# We will use the right arrow key to move one char to the right since moving down puts selector to the last character.
|
||||
# Once located on the bottom cell, use the down arrow key to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.RIGHT)
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys("2")
|
||||
[notebook.body.send_keys(Keys.DOWN) for i in range(2)]
|
||||
notebook.body.send_keys("3")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0", "1", "2", "3"]
|
@ -0,0 +1,58 @@
|
||||
"""Test keyboard shortcuts that change the cell's mode."""
|
||||
|
||||
def test_dualmode_cellmode(notebook):
|
||||
def get_cell_cm_mode(index):
|
||||
code_mirror_mode = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cell(%s).code_mirror.getMode().name;"%index)
|
||||
return code_mirror_mode
|
||||
|
||||
|
||||
index = 0
|
||||
a = 'hello\nmulti\nline'
|
||||
|
||||
notebook.edit_cell(index=index, content=a)
|
||||
|
||||
"""check for the default cell type"""
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("r")
|
||||
assert notebook.get_cell_type(index) == 'raw'
|
||||
assert get_cell_cm_mode(index) == 'null'
|
||||
|
||||
"""check cell type after changing to markdown"""
|
||||
notebook.body.send_keys("1")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '# ' + a
|
||||
assert get_cell_cm_mode(index) == 'ipythongfm'
|
||||
|
||||
notebook.body.send_keys("2")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '## ' + a
|
||||
|
||||
notebook.body.send_keys("3")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '### ' + a
|
||||
|
||||
notebook.body.send_keys("4")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '#### ' + a
|
||||
|
||||
notebook.body.send_keys("5")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '##### ' + a
|
||||
|
||||
notebook.body.send_keys("6")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
|
||||
notebook.body.send_keys("m")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
|
||||
notebook.body.send_keys("y")
|
||||
assert notebook.get_cell_type(index) == 'code'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
assert get_cell_cm_mode(index) == 'ipython'
|
||||
|
||||
notebook.body.send_keys("1")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '# ' + a
|
@ -0,0 +1,54 @@
|
||||
"""Test"""
|
||||
from .utils import shift, validate_dualmode_state
|
||||
|
||||
INITIAL_CELLS = ['', 'print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_dualmode_clipboard(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
_, a, b, c = INITIAL_CELLS
|
||||
for i in range(1, 4):
|
||||
notebook.execute_cell(i)
|
||||
|
||||
#Copy/past/cut
|
||||
num_cells = len(notebook.cells)
|
||||
assert notebook.get_cell_contents(1) == a #Cell 1 is a
|
||||
|
||||
notebook.focus_cell(1)
|
||||
notebook.body.send_keys("x") #Cut
|
||||
validate_dualmode_state(notebook, 'command', 1)
|
||||
assert notebook.get_cell_contents(1) == b #Cell 2 is now where cell 1 was
|
||||
assert len(notebook.cells) == num_cells-1 #A cell was removed
|
||||
|
||||
notebook.focus_cell(2)
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 3)
|
||||
assert notebook.get_cell_contents(3) == a #Cell 3 has the cut contents
|
||||
assert len(notebook.cells) == num_cells #A cell was added
|
||||
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 4)
|
||||
assert notebook.get_cell_contents(4) == a #Cell a has the cut contents
|
||||
assert len(notebook.cells) == num_cells+1 #A cell was added
|
||||
|
||||
notebook.focus_cell(1)
|
||||
notebook.body.send_keys("c") #Copy
|
||||
validate_dualmode_state(notebook, 'command', 1)
|
||||
assert notebook.get_cell_contents(1) == b #Cell 1 is b
|
||||
|
||||
notebook.focus_cell(2)
|
||||
notebook.body.send_keys("c") #Copy
|
||||
validate_dualmode_state(notebook, 'command', 2)
|
||||
assert notebook.get_cell_contents(2) == c #Cell 2 is c
|
||||
|
||||
notebook.focus_cell(4)
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 5)
|
||||
assert notebook.get_cell_contents(2) == c #Cell 2 has the copied contents
|
||||
assert notebook.get_cell_contents(5) == c #Cell 5 has the copied contents
|
||||
assert len(notebook.cells) == num_cells+2 #A cell was added
|
||||
|
||||
notebook.focus_cell(0)
|
||||
shift(notebook.browser, 'v') #Paste
|
||||
validate_dualmode_state(notebook, 'command', 0)
|
||||
assert notebook.get_cell_contents(0) == c #Cell 0 has the copied contents
|
||||
assert len(notebook.cells) == num_cells+3 #A cell was added
|
@ -0,0 +1,74 @@
|
||||
''' Test keyboard invoked execution '''
|
||||
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from .utils import shift, cmdtrl, alt, validate_dualmode_state
|
||||
|
||||
INITIAL_CELLS = ['', 'print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_dualmode_execute(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
for i in range(1, 4):
|
||||
notebook.execute_cell(i)
|
||||
|
||||
#shift-enter
|
||||
#last cell in notebook
|
||||
base_index = 3
|
||||
notebook.focus_cell(base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
|
||||
#Not last cell in notebook & starts in edit mode
|
||||
notebook.focus_cell(base_index)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'command', base_index + 1)
|
||||
|
||||
#Starts in command mode
|
||||
notebook.body.send_keys('k')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'command', base_index + 1)
|
||||
|
||||
|
||||
#Ctrl-enter
|
||||
#Last cell in notebook
|
||||
base_index += 1
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
|
||||
#Not last cell in notebook & stats in edit mode
|
||||
notebook.focus_cell(base_index - 1)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index - 1)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
|
||||
#Starts in command mode
|
||||
notebook.body.send_keys('j')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
|
||||
|
||||
#Alt-enter
|
||||
#Last cell in notebook
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
#Not last cell in notebook &starts in edit mode
|
||||
notebook.focus_cell(base_index)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index)
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
#starts in command mode
|
||||
notebook.body.send_keys(Keys.ESCAPE, 'k')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
|
||||
|
||||
#Notebook will now have 8 cells, the index of the last cell will be 7
|
||||
assert len(notebook) == 8 #Cells where added
|
||||
notebook.focus_cell(7)
|
||||
validate_dualmode_state(notebook, 'command', 7)
|
@ -0,0 +1,51 @@
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import shift
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_insert_cell(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
notebook.to_command_mode()
|
||||
notebook.focus_cell(2)
|
||||
notebook.convert_cell_type(2, "markdown")
|
||||
|
||||
# insert code cell above
|
||||
notebook.current_cell.send_keys("a")
|
||||
assert notebook.get_cell_contents(2) == ""
|
||||
assert notebook.get_cell_type(2) == "code"
|
||||
assert len(notebook.cells) == 4
|
||||
|
||||
# insert code cell below
|
||||
notebook.current_cell.send_keys("b")
|
||||
assert notebook.get_cell_contents(2) == ""
|
||||
assert notebook.get_cell_contents(3) == ""
|
||||
assert notebook.get_cell_type(3) == "code"
|
||||
assert len(notebook.cells) == 5
|
||||
|
||||
notebook.edit_cell(index=1, content="cell1")
|
||||
notebook.focus_cell(1)
|
||||
notebook.current_cell.send_keys("a")
|
||||
assert notebook.get_cell_contents(1) == ""
|
||||
assert notebook.get_cell_contents(2) == "cell1"
|
||||
|
||||
notebook.edit_cell(index=1, content='cell1')
|
||||
notebook.edit_cell(index=2, content='cell2')
|
||||
notebook.edit_cell(index=3, content='cell3')
|
||||
notebook.focus_cell(2)
|
||||
notebook.current_cell.send_keys("b")
|
||||
assert notebook.get_cell_contents(1) == "cell1"
|
||||
assert notebook.get_cell_contents(2) == "cell2"
|
||||
assert notebook.get_cell_contents(3) == ""
|
||||
assert notebook.get_cell_contents(4) == "cell3"
|
||||
|
||||
# insert above multiple selected cells
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('a')
|
||||
|
||||
# insert below multiple selected cells
|
||||
notebook.focus_cell(2)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('b')
|
||||
assert notebook.get_cells_contents()[1:5] == ["", "cell1", "cell2", ""]
|
@ -0,0 +1,53 @@
|
||||
'''Test'''
|
||||
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from .utils import cmdtrl, shift, validate_dualmode_state
|
||||
|
||||
def test_dualmode_markdown(notebook):
|
||||
def is_cell_rendered(index):
|
||||
JS = 'return !!IPython.notebook.get_cell(%s).rendered;'%index
|
||||
return notebook.browser.execute_script(JS)
|
||||
|
||||
|
||||
a = 'print("a")'
|
||||
index = 1
|
||||
notebook.append(a)
|
||||
|
||||
#Markdown rendering / unrendering
|
||||
notebook.focus_cell(index)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
notebook.body.send_keys("m")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
|
||||
notebook.body.send_keys(Keys.ENTER)#cell is unrendered
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
validate_dualmode_state(notebook, 'edit', index)
|
||||
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
assert is_cell_rendered(index) #cell is rendered with crtl+enter
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
|
||||
notebook.body.send_keys(Keys.ENTER)#cell is unrendered
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
|
||||
notebook.focus_cell(index - 1)
|
||||
assert not is_cell_rendered(index) #Select index-1; cell index is still not rendered
|
||||
validate_dualmode_state(notebook, 'command', index - 1)
|
||||
|
||||
notebook.focus_cell(index)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
||||
|
||||
notebook.focus_cell(index - 1)
|
||||
validate_dualmode_state(notebook, 'command', index - 1)
|
||||
|
||||
shift(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
||||
|
||||
shift(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', index + 1)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
@ -0,0 +1,66 @@
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import shift, cmdtrl
|
||||
|
||||
|
||||
def test_execute_code(notebook):
|
||||
browser = notebook.browser
|
||||
|
||||
def clear_outputs():
|
||||
return notebook.browser.execute_script(
|
||||
"Jupyter.notebook.clear_all_output();")
|
||||
|
||||
# Execute cell with Javascript API
|
||||
notebook.edit_cell(index=0, content='a=10; print(a)')
|
||||
browser.execute_script("Jupyter.notebook.get_cell(0).execute();")
|
||||
outputs = notebook.wait_for_cell_output(0)
|
||||
assert outputs[0].text == '10'
|
||||
|
||||
# Execute cell with Shift-Enter
|
||||
notebook.edit_cell(index=0, content='a=11; print(a)')
|
||||
clear_outputs()
|
||||
shift(notebook.browser, Keys.ENTER)
|
||||
outputs = notebook.wait_for_cell_output(0)
|
||||
assert outputs[0].text == '11'
|
||||
notebook.delete_cell(index=1)
|
||||
|
||||
# Execute cell with Ctrl-Enter
|
||||
notebook.edit_cell(index=0, content='a=12; print(a)')
|
||||
clear_outputs()
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
outputs = notebook.wait_for_cell_output(0)
|
||||
assert outputs[0].text == '12'
|
||||
|
||||
# Execute cell with toolbar button
|
||||
notebook.edit_cell(index=0, content='a=13; print(a)')
|
||||
clear_outputs()
|
||||
notebook.browser.find_element_by_css_selector(
|
||||
"button[data-jupyter-action='jupyter-notebook:run-cell-and-select-next']").click()
|
||||
outputs = notebook.wait_for_cell_output(0)
|
||||
assert outputs[0].text == '13'
|
||||
|
||||
# Set up two cells to test stopping on error
|
||||
notebook.edit_cell(index=0, content='raise IOError')
|
||||
notebook.edit_cell(index=1, content='a=14; print(a)')
|
||||
|
||||
# Default behaviour: stop on error
|
||||
clear_outputs()
|
||||
browser.execute_script("""
|
||||
var cell0 = Jupyter.notebook.get_cell(0);
|
||||
var cell1 = Jupyter.notebook.get_cell(1);
|
||||
cell0.execute();
|
||||
cell1.execute();
|
||||
""")
|
||||
outputs = notebook.wait_for_cell_output(0)
|
||||
assert notebook.get_cell_output(1) == []
|
||||
|
||||
# Execute a cell with stop_on_error=false
|
||||
clear_outputs()
|
||||
browser.execute_script("""
|
||||
var cell0 = Jupyter.notebook.get_cell(0);
|
||||
var cell1 = Jupyter.notebook.get_cell(1);
|
||||
cell0.execute(false);
|
||||
cell1.execute();
|
||||
""")
|
||||
outputs = notebook.wait_for_cell_output(1)
|
||||
assert outputs[0].text == '14'
|
||||
|
@ -0,0 +1,16 @@
|
||||
INITIAL_CELLS = ["hello", "hellohello", "abc", "ello"]
|
||||
|
||||
def test_find_and_replace(prefill_notebook):
|
||||
""" test find and replace on all the cells """
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
find_str = "ello"
|
||||
replace_str = "foo"
|
||||
|
||||
# replace the strings
|
||||
notebook.find_and_replace(index=0, find_txt=find_str, replace_txt=replace_str)
|
||||
|
||||
# check content of the cells
|
||||
assert notebook.get_cells_contents() == [
|
||||
s.replace(find_str, replace_str) for s in INITIAL_CELLS
|
||||
]
|
@ -0,0 +1,36 @@
|
||||
from .utils import wait_for_selector
|
||||
|
||||
def interrupt_from_menu(notebook):
|
||||
# Click interrupt button in kernel menu
|
||||
notebook.browser.find_element_by_id('kernellink').click()
|
||||
wait_for_selector(notebook.browser, '#int_kernel', single=True).click()
|
||||
|
||||
def interrupt_from_keyboard(notebook):
|
||||
notebook.body.send_keys("ii")
|
||||
|
||||
|
||||
def test_interrupt(notebook):
|
||||
""" Test the interrupt function using both the button in the Kernel menu and the keyboard shortcut "ii"
|
||||
|
||||
Having trouble accessing the Interrupt message when execution is halted. I am assuming that the
|
||||
message does not lie in the "outputs" field of the cell's JSON object. Using a timeout work-around for
|
||||
test with an infinite loop. We know the interrupt function is working if this test passes.
|
||||
Hope this is a good start.
|
||||
"""
|
||||
|
||||
text = ('import time\n'
|
||||
'for x in range(3):\n'
|
||||
' time.sleep(1)')
|
||||
|
||||
notebook.edit_cell(index=0, content=text)
|
||||
|
||||
for interrupt_method in (interrupt_from_menu, interrupt_from_keyboard):
|
||||
notebook.clear_cell_output(0)
|
||||
notebook.to_command_mode()
|
||||
notebook.execute_cell(0)
|
||||
|
||||
interrupt_method(notebook)
|
||||
|
||||
# Wait for an output to appear
|
||||
output = wait_for_selector(notebook.browser, '.output_subarea', single=True)
|
||||
assert 'KeyboardInterrupt' in output.text
|
@ -0,0 +1,59 @@
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
|
||||
restart_selectors = [
|
||||
'#restart_kernel', '#restart_clear_output', '#restart_run_all'
|
||||
]
|
||||
notify_interaction = '#notification_kernel > span'
|
||||
|
||||
shutdown_selector = '#shutdown_kernel'
|
||||
confirm_selector = '.btn-danger'
|
||||
cancel_selector = ".modal-footer button:first-of-type"
|
||||
|
||||
|
||||
def test_cancel_restart_or_shutdown(notebook):
|
||||
"""Click each of the restart options, then cancel the confirmation dialog"""
|
||||
browser = notebook.browser
|
||||
kernel_menu = browser.find_element_by_id('kernellink')
|
||||
|
||||
for menu_item in restart_selectors + [shutdown_selector]:
|
||||
kernel_menu.click()
|
||||
wait_for_selector(browser, menu_item, visible=True, single=True).click()
|
||||
wait_for_selector(browser, cancel_selector, visible=True, single=True).click()
|
||||
WebDriverWait(browser, 3).until(
|
||||
EC.invisibility_of_element((By.CSS_SELECTOR, '.modal-backdrop'))
|
||||
)
|
||||
assert notebook.is_kernel_running()
|
||||
|
||||
|
||||
def test_menu_items(notebook):
|
||||
browser = notebook.browser
|
||||
kernel_menu = browser.find_element_by_id('kernellink')
|
||||
|
||||
for menu_item in restart_selectors:
|
||||
# Shutdown
|
||||
kernel_menu.click()
|
||||
wait_for_selector(browser, shutdown_selector, visible=True, single=True).click()
|
||||
|
||||
# Confirm shutdown
|
||||
wait_for_selector(browser, confirm_selector, visible=True, single=True).click()
|
||||
|
||||
WebDriverWait(browser, 3).until(
|
||||
lambda b: not notebook.is_kernel_running(),
|
||||
message="Kernel did not shut down as expected"
|
||||
)
|
||||
|
||||
# Restart
|
||||
# Selenium can't click the menu while a modal dialog is fading out
|
||||
WebDriverWait(browser, 3).until(
|
||||
EC.invisibility_of_element((By.CSS_SELECTOR, '.modal-backdrop'))
|
||||
)
|
||||
kernel_menu.click()
|
||||
|
||||
wait_for_selector(browser, menu_item, visible=True, single=True).click()
|
||||
WebDriverWait(browser, 10).until(
|
||||
lambda b: notebook.is_kernel_running(),
|
||||
message=f"Restart ({menu_item!r}) after shutdown did not start kernel"
|
||||
)
|
@ -0,0 +1,41 @@
|
||||
from nbformat.v4 import new_markdown_cell
|
||||
|
||||
def get_rendered_contents(nb):
|
||||
cl = ["text_cell", "render"]
|
||||
rendered_cells = [cell.find_element_by_class_name("text_cell_render")
|
||||
for cell in nb.cells
|
||||
if all([c in cell.get_attribute("class") for c in cl])]
|
||||
return [x.get_attribute('innerHTML').strip()
|
||||
for x in rendered_cells
|
||||
if x is not None]
|
||||
|
||||
|
||||
def test_markdown_cell(prefill_notebook):
|
||||
nb = prefill_notebook([new_markdown_cell(md) for md in [
|
||||
'# Foo', '**Bar**', '*Baz*', '```\nx = 1\n```', '```aaaa\nx = 1\n```',
|
||||
'```python\ns = "$"\nt = "$"\n```'
|
||||
]])
|
||||
|
||||
assert get_rendered_contents(nb) == [
|
||||
'<h1 id="Foo">Foo<a class="anchor-link" href="#Foo">¶</a></h1>',
|
||||
'<p><strong>Bar</strong></p>',
|
||||
'<p><em>Baz</em></p>',
|
||||
'<pre><code>x = 1</code></pre>',
|
||||
'<pre><code class="cm-s-ipython language-aaaa">x = 1</code></pre>',
|
||||
'<pre><code class="cm-s-ipython language-python">' +
|
||||
'<span class="cm-variable">s</span> <span class="cm-operator">=</span> <span class="cm-string">"$"</span>\n' +
|
||||
'<span class="cm-variable">t</span> <span class="cm-operator">=</span> <span class="cm-string">"$"</span></code></pre>'
|
||||
]
|
||||
|
||||
def test_markdown_headings(notebook):
|
||||
lst = list([1, 2, 3, 4, 5, 6, 2, 1])
|
||||
for i in lst:
|
||||
notebook.add_markdown_cell()
|
||||
cell_text = notebook.browser.execute_script(f"""
|
||||
var cell = IPython.notebook.get_cell(1);
|
||||
cell.set_heading_level({i});
|
||||
cell.get_text();
|
||||
""")
|
||||
assert notebook.get_cell_contents(1) == "#" * i + " "
|
||||
notebook.delete_cell(1)
|
||||
|
@ -0,0 +1,36 @@
|
||||
"""Tests the merge cell api."""
|
||||
|
||||
INITIAL_CELLS = [
|
||||
"foo = 5",
|
||||
"bar = 10",
|
||||
"baz = 15",
|
||||
"print(foo)",
|
||||
"print(bar)",
|
||||
"print(baz)",
|
||||
]
|
||||
|
||||
def test_merge_cells(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
a, b, c, d, e, f = INITIAL_CELLS
|
||||
|
||||
# Before merging, there are 6 separate cells
|
||||
assert notebook.get_cells_contents() == [a, b, c, d, e, f]
|
||||
|
||||
# Focus on the second cell and merge it with the cell above
|
||||
notebook.focus_cell(1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_above();")
|
||||
merged_a_b = f"{a}\n\n{b}"
|
||||
assert notebook.get_cells_contents() == [merged_a_b, c, d, e, f]
|
||||
|
||||
# Focus on the second cell and merge it with the cell below
|
||||
notebook.focus_cell(1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_below();")
|
||||
merged_c_d = f"{c}\n\n{d}"
|
||||
assert notebook.get_cells_contents() == [merged_a_b, merged_c_d, e, f]
|
||||
|
||||
# Merge everything down to a single cell with selected cells
|
||||
notebook.select_cell_range(0,3)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_selected_cells();")
|
||||
merged_all = f"{merged_a_b}\n\n{merged_c_d}\n\n{e}\n\n{f}"
|
||||
assert notebook.get_cells_contents() == [merged_all]
|
||||
|
@ -0,0 +1,47 @@
|
||||
INITIAL_CELLS = ['1', '2', '3', '4', '5', '6']
|
||||
def test_move_multiselection(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
def assert_oder(pre_message, expected_state):
|
||||
for i in range(len(expected_state)):
|
||||
assert expected_state[i] == notebook.get_cell_contents(i), f"{pre_message}: Verify that cell {i} has for content: {expected_state[i]} found: {notebook.get_cell_contents(i)}"
|
||||
|
||||
# Select 3 first cells
|
||||
notebook.select_cell_range(0, 2)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
# Should not move up at top
|
||||
assert_oder('move up at top', ['1', '2', '3', '4', '5','6'])
|
||||
|
||||
# We do not need to reselect, move/up down should keep the selection.
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
|
||||
# 3 times down should move the 3 selected cells to the bottom
|
||||
assert_oder("move down to bottom", ['4', '5', '6', '1', '2', '3'])
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
|
||||
# They can't go any futher
|
||||
assert_oder("move down to bottom", ['4', '5', '6', '1', '2', '3'])
|
||||
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
|
||||
# Bring them back on top
|
||||
assert_oder('move up at top', ['1', '2', '3', '4', '5','6'])
|
@ -0,0 +1,63 @@
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_multiselect(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
def extend_selection_by(delta):
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.extend_selection_by(arguments[0]);", delta)
|
||||
|
||||
def n_selected_cells():
|
||||
return notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_selected_cells().length;")
|
||||
|
||||
notebook.focus_cell(0)
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Check that only one cell is selected according to CSS classes as well
|
||||
selected_css = notebook.browser.find_elements_by_css_selector(
|
||||
'.cell.jupyter-soft-selected, .cell.selected')
|
||||
assert len(selected_css) == 1
|
||||
|
||||
# Extend the selection down one
|
||||
extend_selection_by(1)
|
||||
assert n_selected_cells() == 2
|
||||
|
||||
# Contract the selection up one
|
||||
extend_selection_by(-1)
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Extend the selection up one
|
||||
notebook.focus_cell(1)
|
||||
extend_selection_by(-1)
|
||||
assert n_selected_cells() == 2
|
||||
|
||||
# Convert selected cells to Markdown
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_markdown();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['markdown', 'markdown', 'code']
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Convert selected cells to raw
|
||||
notebook.focus_cell(1)
|
||||
extend_selection_by(1)
|
||||
assert n_selected_cells() == 2
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_raw();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['markdown', 'raw', 'raw']
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Convert selected cells to code
|
||||
notebook.focus_cell(0)
|
||||
extend_selection_by(2)
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_code();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['code'] * 3
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
@ -0,0 +1,43 @@
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
def test_multiselect_toggle(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
def extend_selection_by(delta):
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.extend_selection_by(arguments[0]);", delta)
|
||||
|
||||
def n_selected_cells():
|
||||
return notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_selected_cells().length;")
|
||||
|
||||
def select_cells():
|
||||
notebook.focus_cell(0)
|
||||
extend_selection_by(2)
|
||||
|
||||
# Test that cells, which start off not collapsed, are collapsed after
|
||||
# calling the multiselected cell toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.execute_selected_cells();")
|
||||
select_cells()
|
||||
notebook.browser.execute_script("Jupyter.notebook.toggle_cells_outputs();")
|
||||
cell_output_states = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.collapsed)")
|
||||
assert cell_output_states == [False] * 3, "ensure that all cells are not collapsed"
|
||||
|
||||
# Test that cells, which start off not scrolled are scrolled after
|
||||
# calling the multiselected scroll toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.toggle_cells_outputs_scroll();")
|
||||
cell_scrolled_states = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.output_area.scroll_state)")
|
||||
assert all(cell_scrolled_states), "ensure that all have scrolling enabled"
|
||||
|
||||
# Test that cells, which start off not cleared are cleared after
|
||||
# calling the multiselected scroll toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.clear_cells_outputs();")
|
||||
cell_outputs_cleared = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.output_area.element.html())")
|
||||
assert cell_outputs_cleared == [""] * 3, "ensure that all cells are cleared"
|
@ -0,0 +1,103 @@
|
||||
"""
|
||||
Test the notification area and widgets
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from .utils import wait_for_selector, wait_for_script_to_return_true
|
||||
|
||||
|
||||
def get_widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.get_widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def new_notification_widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.new_notification_widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def widget_has_class(notebook, name, class_name):
|
||||
return notebook.browser.execute_script(
|
||||
f"""
|
||||
var w = IPython.notification_area.get_widget('{name}');
|
||||
return w.element.hasClass('{class_name}');
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def widget_message(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"""
|
||||
var w = IPython.notification_area.get_widget('{name}');
|
||||
return w.get_message();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_notification(notebook):
|
||||
# check that existing widgets are there
|
||||
assert get_widget(notebook, "kernel") and widget(notebook, "kernel"),\
|
||||
"The kernel notification widget exists"
|
||||
assert get_widget(notebook, "notebook") and widget(notebook, "notebook"),\
|
||||
"The notebook notification widget exists"
|
||||
|
||||
# try getting a non-existent widget
|
||||
with pytest.raises(Exception):
|
||||
get_widget(notebook, "foo")
|
||||
|
||||
# try creating a non-existent widget
|
||||
assert widget(notebook, "bar"), "widget: new widget is created"
|
||||
|
||||
# try creating a widget that already exists
|
||||
with pytest.raises(Exception):
|
||||
new_notification_widget(notebook, "kernel")
|
||||
|
||||
# test creating 'info', 'warning' and 'danger' messages
|
||||
for level in ("info", "warning", "danger"):
|
||||
notebook.browser.execute_script(f"""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw.{level}('test {level}');
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_has_class(notebook, "test", level), f"{level}: class is correct"
|
||||
assert widget_message(notebook, "test") == f"test {level}", f"{level}: message is correct"
|
||||
|
||||
# test message timeout
|
||||
notebook.browser.execute_script("""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw.set_message('test timeout', 1000);
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "test timeout", "timeout: message is correct"
|
||||
wait_for_selector(notebook.browser, "#notification_test", obscures=True)
|
||||
assert widget_message(notebook, "test") == "", "timeout: message was cleared"
|
||||
|
||||
# test click callback
|
||||
notebook.browser.execute_script("""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw._clicked = false;
|
||||
tnw.set_message('test click', undefined, function () {
|
||||
tnw._clicked = true;
|
||||
return true;
|
||||
});
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "test click", "callback: message is correct"
|
||||
|
||||
notebook.browser.find_element_by_id("notification_test").click()
|
||||
wait_for_script_to_return_true(notebook.browser,
|
||||
'return IPython.notification_area.widget("test")._clicked;')
|
||||
wait_for_selector(notebook.browser, "#notification_test", obscures=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "", "callback: message was cleared"
|
@ -0,0 +1,29 @@
|
||||
def test_prompt_numbers(prefill_notebook):
|
||||
notebook = prefill_notebook(['print("a")'])
|
||||
|
||||
def get_prompt():
|
||||
return (
|
||||
notebook.cells[0].find_element_by_class_name('input')
|
||||
.find_element_by_class_name('input_prompt')
|
||||
.get_attribute('innerHTML').strip()
|
||||
)
|
||||
|
||||
def set_prompt(value):
|
||||
notebook.set_cell_input_prompt(0, value)
|
||||
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
||||
|
||||
set_prompt(2)
|
||||
assert get_prompt() == "<bdi>In</bdi> [2]:"
|
||||
|
||||
set_prompt(0)
|
||||
assert get_prompt() == "<bdi>In</bdi> [0]:"
|
||||
|
||||
set_prompt("'*'")
|
||||
assert get_prompt() == "<bdi>In</bdi> [*]:"
|
||||
|
||||
set_prompt("undefined")
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
||||
|
||||
set_prompt("null")
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
64
.venv/Lib/site-packages/notebook/tests/selenium/test_save.py
Normal file
64
.venv/Lib/site-packages/notebook/tests/selenium/test_save.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""Test saving a notebook with escaped characters
|
||||
"""
|
||||
|
||||
from urllib.parse import quote
|
||||
from .utils import wait_for_selector
|
||||
|
||||
promise_js = """
|
||||
var done = arguments[arguments.length - 1];
|
||||
%s.then(
|
||||
data => { done(["success", data]); },
|
||||
error => { done(["error", error]); }
|
||||
);
|
||||
"""
|
||||
|
||||
def execute_promise(js, browser):
|
||||
state, data = browser.execute_async_script(promise_js % js)
|
||||
if state == 'success':
|
||||
return data
|
||||
raise Exception(data)
|
||||
|
||||
|
||||
def test_save(notebook):
|
||||
# don't use unicode with ambiguous composed/decomposed normalization
|
||||
# because the filesystem may use a different normalization than literals.
|
||||
# This causes no actual problems, but will break string comparison.
|
||||
nbname = "has#hash and space and unicø∂e.ipynb"
|
||||
escaped_name = quote(nbname)
|
||||
|
||||
notebook.edit_cell(index=0, content="s = '??'")
|
||||
|
||||
notebook.browser.execute_script("Jupyter.notebook.set_notebook_name(arguments[0])", nbname)
|
||||
|
||||
model = execute_promise("Jupyter.notebook.save_notebook()", notebook.browser)
|
||||
assert model['name'] == nbname
|
||||
|
||||
current_name = notebook.browser.execute_script("return Jupyter.notebook.notebook_name")
|
||||
assert current_name == nbname
|
||||
|
||||
current_path = notebook.browser.execute_script("return Jupyter.notebook.notebook_path")
|
||||
assert current_path == nbname
|
||||
|
||||
displayed_name = notebook.browser.find_element_by_id('notebook_name').text
|
||||
assert displayed_name + '.ipynb' == nbname
|
||||
|
||||
execute_promise("Jupyter.notebook.save_checkpoint()", notebook.browser)
|
||||
|
||||
checkpoints = notebook.browser.execute_script("return Jupyter.notebook.checkpoints")
|
||||
assert len(checkpoints) == 1
|
||||
|
||||
notebook.browser.find_element_by_css_selector('#ipython_notebook a').click()
|
||||
hrefs_nonmatch = []
|
||||
for link in wait_for_selector(notebook.browser, 'a.item_link'):
|
||||
href = link.get_attribute('href')
|
||||
if escaped_name in href:
|
||||
print("Opening", href)
|
||||
notebook.browser.get(href)
|
||||
wait_for_selector(notebook.browser, '.cell')
|
||||
break
|
||||
hrefs_nonmatch.append(href)
|
||||
else:
|
||||
raise AssertionError(f"{escaped_name!r} not found in {hrefs_nonmatch!r}")
|
||||
|
||||
current_name = notebook.browser.execute_script("return Jupyter.notebook.notebook_name")
|
||||
assert current_name == nbname
|
@ -0,0 +1,40 @@
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
def wait_for_rename(browser, nbname, timeout=10):
|
||||
wait = WebDriverWait(browser, timeout)
|
||||
def notebook_renamed(browser):
|
||||
elem = browser.find_element_by_id('notebook_name')
|
||||
current_name = browser.execute_script('return arguments[0].innerText', elem)
|
||||
return current_name == nbname
|
||||
return wait.until(notebook_renamed)
|
||||
|
||||
def save_as(nb):
|
||||
JS = 'Jupyter.notebook.save_notebook_as()'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def get_notebook_name(nb):
|
||||
JS = 'return Jupyter.notebook.notebook_name'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def set_notebook_name(nb, name):
|
||||
JS = f'Jupyter.notebook.rename("{name}")'
|
||||
nb.browser.execute_script(JS)
|
||||
|
||||
def test_save_notebook_as(notebook):
|
||||
# Set a name for comparison later
|
||||
set_notebook_name(notebook, name="nb1.ipynb")
|
||||
wait_for_rename(notebook.browser, "nb1")
|
||||
assert get_notebook_name(notebook) == "nb1.ipynb"
|
||||
# Wait for Save As modal, save
|
||||
save_as(notebook)
|
||||
wait_for_selector(notebook.browser, '.save-message')
|
||||
inp = notebook.browser.find_element_by_xpath('//input[@data-testid="save-as"]')
|
||||
inp.send_keys('new_notebook.ipynb')
|
||||
inp.send_keys(Keys.RETURN)
|
||||
wait_for_rename(notebook.browser, "new_notebook")
|
||||
# Test that the name changed
|
||||
assert get_notebook_name(notebook) == "new_notebook.ipynb"
|
||||
# Test that address bar was updated (TODO: get the base url)
|
||||
assert "new_notebook.ipynb" in notebook.browser.current_url
|
@ -0,0 +1,80 @@
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
promise_js = """
|
||||
var done = arguments[arguments.length - 1];
|
||||
(%s).then(
|
||||
data => { done(["success", data]); },
|
||||
error => { done(["error", error]); }
|
||||
);
|
||||
"""
|
||||
|
||||
def execute_promise(js, browser):
|
||||
state, data = browser.execute_async_script(promise_js % js)
|
||||
if state == 'success':
|
||||
return data
|
||||
raise Exception(data)
|
||||
|
||||
def wait_for_rename(browser, nbname, timeout=10):
|
||||
wait = WebDriverWait(browser, timeout)
|
||||
def notebook_renamed(browser):
|
||||
elem = browser.find_element_by_id('notebook_name')
|
||||
current_name = browser.execute_script('return arguments[0].innerText', elem)
|
||||
return current_name == nbname
|
||||
return wait.until(notebook_renamed)
|
||||
|
||||
def save_as(nb):
|
||||
JS = 'Jupyter.notebook.save_notebook_as()'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def get_notebook_name(nb):
|
||||
JS = 'return Jupyter.notebook.notebook_name'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def refresh_notebook(nb):
|
||||
nb.browser.refresh()
|
||||
nb.__init__(nb.browser)
|
||||
|
||||
def test_save_readonly_notebook_as(notebook):
|
||||
# Make notebook read-only
|
||||
notebook.edit_cell(index=0, content='import os\nimport stat\nos.chmod("'
|
||||
+ notebook.browser.current_url.split('?')[0].split('/')[-1] + '", stat.S_IREAD)\nprint(0)')
|
||||
notebook.browser.execute_script("Jupyter.notebook.get_cell(0).execute();")
|
||||
notebook.wait_for_cell_output(0)
|
||||
refresh_notebook(notebook)
|
||||
# Test that the notebook is read-only
|
||||
assert notebook.browser.execute_script('return Jupyter.notebook.writable') == False
|
||||
|
||||
# Add some content
|
||||
test_content_0 = "print('a simple')\nprint('test script')"
|
||||
notebook.edit_cell(index=0, content=test_content_0)
|
||||
|
||||
# Wait for Save As modal, save
|
||||
save_as(notebook)
|
||||
wait_for_selector(notebook.browser, '.save-message')
|
||||
inp = notebook.browser.find_element_by_xpath('//input[@data-testid="save-as"]')
|
||||
inp.send_keys('writable_notebook.ipynb')
|
||||
inp.send_keys(Keys.RETURN)
|
||||
wait_for_rename(notebook.browser, "writable_notebook")
|
||||
# Test that the name changed
|
||||
assert get_notebook_name(notebook) == "writable_notebook.ipynb"
|
||||
# Test that address bar was updated (TODO: get the base url)
|
||||
assert "writable_notebook.ipynb" in notebook.browser.current_url
|
||||
# Test that it is no longer read-only
|
||||
assert notebook.browser.execute_script('return Jupyter.notebook.writable') == True
|
||||
|
||||
# Add some more content
|
||||
test_content_1 = "print('a second simple')\nprint('script to test save feature')"
|
||||
notebook.add_and_execute_cell(content=test_content_1)
|
||||
# and save the notebook
|
||||
execute_promise("Jupyter.notebook.save_notebook()", notebook.browser)
|
||||
|
||||
# Test that it still contains the content
|
||||
assert notebook.get_cell_contents(index=0) == test_content_0
|
||||
assert notebook.get_cell_contents(index=1) == test_content_1
|
||||
# even after a refresh
|
||||
refresh_notebook(notebook)
|
||||
assert notebook.get_cell_contents(index=0) == test_content_0
|
||||
assert notebook.get_cell_contents(index=1) == test_content_1
|
@ -0,0 +1,15 @@
|
||||
"""Tests shutdown of the Kernel."""
|
||||
from .utils import wait_for_selector, wait_for_xpath
|
||||
|
||||
def test_shutdown(notebook):
|
||||
notebook.edit_cell(content="print(21)")
|
||||
wait_for_xpath(notebook.browser, '//a[text()="Kernel"]', single=True).click()
|
||||
wait_for_selector(notebook.browser, '#shutdown_kernel', single=True).click()
|
||||
wait_for_selector(notebook.browser, '.btn.btn-default.btn-sm.btn-danger', single=True).click()
|
||||
|
||||
#Wait until all shutdown modal elements disappear before trying to execute the cell
|
||||
wait_for_xpath(notebook.browser, "//div[contains(@class,'modal')]", obscures=True)
|
||||
notebook.execute_cell(0)
|
||||
|
||||
assert not notebook.is_kernel_running()
|
||||
assert len(notebook.get_cell_output()) == 0
|
@ -0,0 +1,92 @@
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import shift
|
||||
|
||||
def undelete(nb):
|
||||
nb.browser.execute_script('Jupyter.notebook.undelete_cell();')
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")', 'print("d")']
|
||||
|
||||
def test_undelete_cells(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
a, b, c, d = INITIAL_CELLS
|
||||
|
||||
# Verify initial state
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete cells [1, 2]
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a, d]
|
||||
|
||||
# Delete new cell 1 (which contains d)
|
||||
notebook.focus_cell(1)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a]
|
||||
|
||||
# Undelete d
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, d]
|
||||
|
||||
# Undelete b, c
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Nothing more to undelete
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete first two cells and restore
|
||||
notebook.focus_cell(0)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [c, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete last two cells and restore
|
||||
notebook.focus_cell(-1)
|
||||
shift(notebook.browser, Keys.UP)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a, b]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Merge cells [1, 2], restore the deleted one
|
||||
bc = b + "\n\n" + c
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, 'j')
|
||||
shift(notebook.browser, 'm')
|
||||
assert notebook.get_cells_contents() == [a, bc, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, bc, c, d]
|
||||
|
||||
# Merge cells [2, 3], restore the deleted one
|
||||
cd = c + "\n\n" + d
|
||||
notebook.focus_cell(-1)
|
||||
shift(notebook.browser, 'k')
|
||||
shift(notebook.browser, 'm')
|
||||
assert notebook.get_cells_contents() == [a, bc, cd]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, bc, cd, d]
|
||||
|
||||
# Reset contents to [a, b, c, d] --------------------------------------
|
||||
notebook.edit_cell(index=1, content=b)
|
||||
notebook.edit_cell(index=2, content=c)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Merge cell below, restore the deleted one
|
||||
ab = a + "\n\n" + b
|
||||
notebook.focus_cell(0)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_below();")
|
||||
assert notebook.get_cells_contents() == [ab, c, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [ab, b, c, d]
|
||||
|
||||
# Merge cell above, restore the deleted one
|
||||
cd = c + "\n\n" + d
|
||||
notebook.focus_cell(-1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_above();")
|
||||
assert notebook.get_cells_contents() == [ab, b, cd]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [ab, b, c, cd]
|
468
.venv/Lib/site-packages/notebook/tests/selenium/utils.py
Normal file
468
.venv/Lib/site-packages/notebook/tests/selenium/utils.py
Normal file
@ -0,0 +1,468 @@
|
||||
import os
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
def wait_for_selector(driver, selector, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.CSS_SELECTOR, selector, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.CSS_SELECTOR, selector, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_tag(driver, tag, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.TAG_NAME, tag, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.TAG_NAME, tag, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_xpath(driver, xpath, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.XPATH, xpath, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.XPATH, xpath, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_script_to_return_true(driver, script, timeout=10):
|
||||
WebDriverWait(driver, timeout).until(lambda d: d.execute_script(script))
|
||||
|
||||
|
||||
def _wait_for(driver, locator_type, locator, timeout=10, visible=False, single=False, obscures=False):
|
||||
"""Waits `timeout` seconds for the specified condition to be met. Condition is
|
||||
met if any matching element is found. Returns located element(s) when found.
|
||||
|
||||
Args:
|
||||
driver: Selenium web driver instance
|
||||
locator_type: type of locator (e.g. By.CSS_SELECTOR or By.TAG_NAME)
|
||||
locator: name of tag, class, etc. to wait for
|
||||
timeout: how long to wait for presence/visibility of element
|
||||
visible: if True, require that element is not only present, but visible
|
||||
single: if True, return a single element, otherwise return a list of matching
|
||||
elements
|
||||
obscures: if True, waits until the element becomes invisible
|
||||
"""
|
||||
wait = WebDriverWait(driver, timeout)
|
||||
if obscures:
|
||||
conditional = EC.invisibility_of_element_located
|
||||
elif single:
|
||||
if visible:
|
||||
conditional = EC.visibility_of_element_located
|
||||
else:
|
||||
conditional = EC.presence_of_element_located
|
||||
else:
|
||||
if visible:
|
||||
conditional = EC.visibility_of_all_elements_located
|
||||
else:
|
||||
conditional = EC.presence_of_all_elements_located
|
||||
return wait.until(conditional((locator_type, locator)))
|
||||
|
||||
|
||||
def _wait_for_multiple(driver, locator_type, locator, timeout, wait_for_n, visible=False):
|
||||
"""Waits until `wait_for_n` matching elements to be present (or visible).
|
||||
Returns located elements when found.
|
||||
|
||||
Args:
|
||||
driver: Selenium web driver instance
|
||||
locator_type: type of locator (e.g. By.CSS_SELECTOR or By.TAG_NAME)
|
||||
locator: name of tag, class, etc. to wait for
|
||||
timeout: how long to wait for presence/visibility of element
|
||||
wait_for_n: wait until this number of matching elements are present/visible
|
||||
visible: if True, require that elements are not only present, but visible
|
||||
"""
|
||||
wait = WebDriverWait(driver, timeout)
|
||||
|
||||
def multiple_found(driver):
|
||||
elements = driver.find_elements(locator_type, locator)
|
||||
if visible:
|
||||
elements = [e for e in elements if e.is_displayed()]
|
||||
if len(elements) < wait_for_n:
|
||||
return False
|
||||
return elements
|
||||
|
||||
return wait.until(multiple_found)
|
||||
|
||||
|
||||
class CellTypeError(ValueError):
|
||||
|
||||
def __init__(self, message=""):
|
||||
self.message = message
|
||||
|
||||
|
||||
class Notebook:
|
||||
|
||||
def __init__(self, browser):
|
||||
self.browser = browser
|
||||
self._wait_for_start()
|
||||
self.disable_autosave_and_onbeforeunload()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.cells)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.cells[key]
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
if isinstance(key, int):
|
||||
self.edit_cell(index=key, content=item, render=False)
|
||||
# TODO: re-add slicing support, handle general python slicing behaviour
|
||||
# includes: overwriting the entire self.cells object if you do
|
||||
# self[:] = []
|
||||
# elif isinstance(key, slice):
|
||||
# indices = (self.index(cell) for cell in self[key])
|
||||
# for k, v in zip(indices, item):
|
||||
# self.edit_cell(index=k, content=v, render=False)
|
||||
|
||||
def __iter__(self):
|
||||
return (cell for cell in self.cells)
|
||||
|
||||
def _wait_for_start(self):
|
||||
"""Wait until the notebook interface is loaded and the kernel started"""
|
||||
wait_for_selector(self.browser, '.cell')
|
||||
WebDriverWait(self.browser, 10).until(
|
||||
lambda drvr: self.is_kernel_running()
|
||||
)
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return self.browser.find_element_by_tag_name("body")
|
||||
|
||||
@property
|
||||
def cells(self):
|
||||
"""Gets all cells once they are visible.
|
||||
|
||||
"""
|
||||
return self.browser.find_elements_by_class_name("cell")
|
||||
|
||||
@property
|
||||
def current_index(self):
|
||||
return self.index(self.current_cell)
|
||||
|
||||
def index(self, cell):
|
||||
return self.cells.index(cell)
|
||||
|
||||
def disable_autosave_and_onbeforeunload(self):
|
||||
"""Disable request to save before closing window and autosave.
|
||||
|
||||
This is most easily done by using js directly.
|
||||
"""
|
||||
self.browser.execute_script("window.onbeforeunload = null;")
|
||||
self.browser.execute_script("Jupyter.notebook.set_autosave_interval(0)")
|
||||
|
||||
def to_command_mode(self):
|
||||
"""Changes us into command mode on currently focused cell
|
||||
|
||||
"""
|
||||
self.body.send_keys(Keys.ESCAPE)
|
||||
self.browser.execute_script("return Jupyter.notebook.handle_command_mode("
|
||||
"Jupyter.notebook.get_cell("
|
||||
"Jupyter.notebook.get_edit_index()))")
|
||||
|
||||
def focus_cell(self, index=0):
|
||||
cell = self.cells[index]
|
||||
cell.click()
|
||||
self.to_command_mode()
|
||||
self.current_cell = cell
|
||||
|
||||
def select_cell_range(self, initial_index=0, final_index=0):
|
||||
self.focus_cell(initial_index)
|
||||
self.to_command_mode()
|
||||
for i in range(final_index - initial_index):
|
||||
shift(self.browser, 'j')
|
||||
|
||||
def find_and_replace(self, index=0, find_txt='', replace_txt=''):
|
||||
self.focus_cell(index)
|
||||
self.to_command_mode()
|
||||
self.body.send_keys('f')
|
||||
wait_for_selector(self.browser, "#find-and-replace", single=True)
|
||||
self.browser.find_element_by_id("findreplace_allcells_btn").click()
|
||||
self.browser.find_element_by_id("findreplace_find_inp").send_keys(find_txt)
|
||||
self.browser.find_element_by_id("findreplace_replace_inp").send_keys(replace_txt)
|
||||
self.browser.find_element_by_id("findreplace_replaceall_btn").click()
|
||||
|
||||
def convert_cell_type(self, index=0, cell_type="code"):
|
||||
# TODO add check to see if it is already present
|
||||
self.focus_cell(index)
|
||||
cell = self.cells[index]
|
||||
if cell_type == "markdown":
|
||||
self.current_cell.send_keys("m")
|
||||
elif cell_type == "raw":
|
||||
self.current_cell.send_keys("r")
|
||||
elif cell_type == "code":
|
||||
self.current_cell.send_keys("y")
|
||||
else:
|
||||
raise CellTypeError(f"{cell_type} is not a valid cell type,use 'code', 'markdown', or 'raw'")
|
||||
|
||||
self.wait_for_stale_cell(cell)
|
||||
self.focus_cell(index)
|
||||
return self.current_cell
|
||||
|
||||
def wait_for_stale_cell(self, cell):
|
||||
""" This is needed to switch a cell's mode and refocus it, or to render it.
|
||||
|
||||
Warning: there is currently no way to do this when changing between
|
||||
markdown and raw cells.
|
||||
"""
|
||||
wait = WebDriverWait(self.browser, 10)
|
||||
element = wait.until(EC.staleness_of(cell))
|
||||
|
||||
def wait_for_element_availability(self, element):
|
||||
_wait_for(self.browser, By.CLASS_NAME, element, visible=True)
|
||||
|
||||
def get_cells_contents(self):
|
||||
JS = 'return Jupyter.notebook.get_cells().map(function(c) {return c.get_text();})'
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def get_cell_contents(self, index=0, selector='div .CodeMirror-code'):
|
||||
return self.cells[index].find_element_by_css_selector(selector).text
|
||||
|
||||
def get_cell_output(self, index=0, output='output_subarea'):
|
||||
return self.cells[index].find_elements_by_class_name(output)
|
||||
|
||||
def wait_for_cell_output(self, index=0, timeout=10):
|
||||
return WebDriverWait(self.browser, timeout).until(
|
||||
lambda b: self.get_cell_output(index)
|
||||
)
|
||||
|
||||
def set_cell_metadata(self, index, key, value):
|
||||
JS = f'Jupyter.notebook.get_cell({index}).metadata.{key} = {value}'
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def get_cell_type(self, index=0):
|
||||
JS = f'return Jupyter.notebook.get_cell({index}).cell_type'
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def set_cell_input_prompt(self, index, prmpt_val):
|
||||
JS = f'Jupyter.notebook.get_cell({index}).set_input_prompt({prmpt_val})'
|
||||
self.browser.execute_script(JS)
|
||||
|
||||
def edit_cell(self, cell=None, index=0, content="", render=False):
|
||||
"""Set the contents of a cell to *content*, by cell object or by index
|
||||
"""
|
||||
if cell is not None:
|
||||
index = self.index(cell)
|
||||
self.focus_cell(index)
|
||||
|
||||
# Select & delete anything already in the cell
|
||||
self.current_cell.send_keys(Keys.ENTER)
|
||||
cmdtrl(self.browser, 'a')
|
||||
self.current_cell.send_keys(Keys.DELETE)
|
||||
|
||||
for line_no, line in enumerate(content.splitlines()):
|
||||
if line_no != 0:
|
||||
self.current_cell.send_keys(Keys.ENTER, "\n")
|
||||
self.current_cell.send_keys(Keys.ENTER, line)
|
||||
if render:
|
||||
self.execute_cell(self.current_index)
|
||||
|
||||
def execute_cell(self, cell_or_index=None):
|
||||
if isinstance(cell_or_index, int):
|
||||
index = cell_or_index
|
||||
elif isinstance(cell_or_index, WebElement):
|
||||
index = self.index(cell_or_index)
|
||||
else:
|
||||
raise TypeError("execute_cell only accepts a WebElement or an int")
|
||||
self.focus_cell(index)
|
||||
self.current_cell.send_keys(Keys.CONTROL, Keys.ENTER)
|
||||
|
||||
def add_cell(self, index=-1, cell_type="code", content=""):
|
||||
self.focus_cell(index)
|
||||
self.current_cell.send_keys("b")
|
||||
new_index = index + 1 if index >= 0 else index
|
||||
if content:
|
||||
self.edit_cell(index=index, content=content)
|
||||
if cell_type != 'code':
|
||||
self.convert_cell_type(index=new_index, cell_type=cell_type)
|
||||
|
||||
def add_and_execute_cell(self, index=-1, cell_type="code", content=""):
|
||||
self.add_cell(index=index, cell_type=cell_type, content=content)
|
||||
self.execute_cell(index)
|
||||
|
||||
def delete_cell(self, index):
|
||||
self.focus_cell(index)
|
||||
self.to_command_mode()
|
||||
self.current_cell.send_keys('dd')
|
||||
|
||||
def add_markdown_cell(self, index=-1, content="", render=True):
|
||||
self.add_cell(index, cell_type="markdown")
|
||||
self.edit_cell(index=index, content=content, render=render)
|
||||
|
||||
def append(self, *values, cell_type="code"):
|
||||
for i, value in enumerate(values):
|
||||
if isinstance(value, str):
|
||||
self.add_cell(cell_type=cell_type,
|
||||
content=value)
|
||||
else:
|
||||
raise TypeError(f"Don't know how to add cell from {value!r}")
|
||||
|
||||
def extend(self, values):
|
||||
self.append(*values)
|
||||
|
||||
def run_all(self):
|
||||
for cell in self:
|
||||
self.execute_cell(cell)
|
||||
|
||||
def trigger_keydown(self, keys):
|
||||
trigger_keystrokes(self.body, keys)
|
||||
|
||||
def is_kernel_running(self):
|
||||
return self.browser.execute_script(
|
||||
"return Jupyter.notebook.kernel && Jupyter.notebook.kernel.is_connected()"
|
||||
)
|
||||
|
||||
def clear_cell_output(self, index):
|
||||
JS = f'Jupyter.notebook.clear_output({index})'
|
||||
self.browser.execute_script(JS)
|
||||
|
||||
@classmethod
|
||||
def new_notebook(cls, browser, kernel_name='kernel-python3'):
|
||||
with new_window(browser):
|
||||
select_kernel(browser, kernel_name=kernel_name)
|
||||
return cls(browser)
|
||||
|
||||
|
||||
def select_kernel(browser, kernel_name='kernel-python3'):
|
||||
"""Clicks the "new" button and selects a kernel from the options.
|
||||
"""
|
||||
wait = WebDriverWait(browser, 10)
|
||||
new_button = wait.until(EC.element_to_be_clickable((By.ID, "new-dropdown-button")))
|
||||
new_button.click()
|
||||
kernel_selector = f'#{kernel_name} a'
|
||||
kernel = wait_for_selector(browser, kernel_selector, single=True)
|
||||
kernel.click()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def new_window(browser):
|
||||
"""Contextmanager for switching to & waiting for a window created.
|
||||
|
||||
This context manager gives you the ability to create a new window inside
|
||||
the created context and it will switch you to that new window.
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.utils import new_window, Notebook
|
||||
|
||||
⋮ # something that creates a browser object
|
||||
|
||||
with new_window(browser):
|
||||
select_kernel(browser, kernel_name=kernel_name)
|
||||
nb = Notebook(browser)
|
||||
|
||||
"""
|
||||
initial_window_handles = browser.window_handles
|
||||
yield
|
||||
new_window_handles = [window for window in browser.window_handles
|
||||
if window not in initial_window_handles]
|
||||
if not new_window_handles:
|
||||
raise Exception("No new windows opened during context")
|
||||
browser.switch_to.window(new_window_handles[0])
|
||||
|
||||
def shift(browser, k):
|
||||
"""Send key combination Shift+(k)"""
|
||||
trigger_keystrokes(browser, "shift-%s"%k)
|
||||
|
||||
def cmdtrl(browser, k):
|
||||
"""Send key combination Ctrl+(k) or Command+(k) for MacOS"""
|
||||
trigger_keystrokes(browser, "command-%s"%k) if os.uname()[0] == "Darwin" else trigger_keystrokes(browser, "control-%s"%k)
|
||||
|
||||
def alt(browser, k):
|
||||
"""Send key combination Alt+(k)"""
|
||||
trigger_keystrokes(browser, 'alt-%s'%k)
|
||||
|
||||
def trigger_keystrokes(browser, *keys):
|
||||
""" Send the keys in sequence to the browser.
|
||||
Handles following key combinations
|
||||
1. with modifiers eg. 'control-alt-a', 'shift-c'
|
||||
2. just modifiers eg. 'alt', 'esc'
|
||||
3. non-modifiers eg. 'abc'
|
||||
Modifiers : http://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html
|
||||
"""
|
||||
for each_key_combination in keys:
|
||||
keys = each_key_combination.split('-')
|
||||
if len(keys) > 1: # key has modifiers eg. control, alt, shift
|
||||
modifiers_keys = [getattr(Keys, x.upper()) for x in keys[:-1]]
|
||||
ac = ActionChains(browser)
|
||||
for i in modifiers_keys: ac = ac.key_down(i)
|
||||
ac.send_keys(keys[-1])
|
||||
for i in modifiers_keys[::-1]: ac = ac.key_up(i)
|
||||
ac.perform()
|
||||
else: # single key stroke. Check if modifier eg. "up"
|
||||
browser.send_keys(getattr(Keys, keys[0].upper(), keys[0]))
|
||||
|
||||
def validate_dualmode_state(notebook, mode, index):
|
||||
'''Validate the entire dual mode state of the notebook.
|
||||
Checks if the specified cell is selected, and the mode and keyboard mode are the same.
|
||||
Depending on the mode given:
|
||||
Command: Checks that no cells are in focus or in edit mode.
|
||||
Edit: Checks that only the specified cell is in focus and in edit mode.
|
||||
'''
|
||||
def is_only_cell_edit(index):
|
||||
JS = 'return Jupyter.notebook.get_cells().map(function(c) {return c.mode;})'
|
||||
cells_mode = notebook.browser.execute_script(JS)
|
||||
#None of the cells are in edit mode
|
||||
if index is None:
|
||||
for mode in cells_mode:
|
||||
if mode == 'edit':
|
||||
return False
|
||||
return True
|
||||
#Only the index cell is on edit mode
|
||||
for i, mode in enumerate(cells_mode):
|
||||
if i == index:
|
||||
if mode != 'edit':
|
||||
return False
|
||||
else:
|
||||
if mode == 'edit':
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_focused_on(index):
|
||||
JS = "return $('#notebook .CodeMirror-focused textarea').length;"
|
||||
focused_cells = notebook.browser.execute_script(JS)
|
||||
if index is None:
|
||||
return focused_cells == 0
|
||||
|
||||
if focused_cells != 1: #only one cell is focused
|
||||
return False
|
||||
|
||||
JS = "return $('#notebook .CodeMirror-focused textarea')[0];"
|
||||
focused_cell = notebook.browser.execute_script(JS)
|
||||
JS = "return IPython.notebook.get_cell(%s).code_mirror.getInputField()"%index
|
||||
cell = notebook.browser.execute_script(JS)
|
||||
return focused_cell == cell
|
||||
|
||||
|
||||
#general test
|
||||
JS = "return IPython.keyboard_manager.mode;"
|
||||
keyboard_mode = notebook.browser.execute_script(JS)
|
||||
JS = "return IPython.notebook.mode;"
|
||||
notebook_mode = notebook.browser.execute_script(JS)
|
||||
|
||||
#validate selected cell
|
||||
JS = "return Jupyter.notebook.get_selected_cells_indices();"
|
||||
cell_index = notebook.browser.execute_script(JS)
|
||||
assert cell_index == [index] #only the index cell is selected
|
||||
|
||||
if mode != 'command' and mode != 'edit':
|
||||
raise Exception('An unknown mode was send: mode = "%s"'%mode) #An unknown mode is send
|
||||
|
||||
#validate mode
|
||||
assert mode == keyboard_mode #keyboard mode is correct
|
||||
|
||||
if mode == 'command':
|
||||
assert is_focused_on(None) #no focused cells
|
||||
|
||||
assert is_only_cell_edit(None) #no cells in edit mode
|
||||
|
||||
elif mode == 'edit':
|
||||
assert is_focused_on(index) #The specified cell is focused
|
||||
|
||||
assert is_only_cell_edit(index) #The specified cell is the only one in edit mode
|
325
.venv/Lib/site-packages/notebook/tests/services/kernel.js
Normal file
325
.venv/Lib/site-packages/notebook/tests/services/kernel.js
Normal file
@ -0,0 +1,325 @@
|
||||
|
||||
//
|
||||
// Kernel tests
|
||||
//
|
||||
casper.notebook_test(function () {
|
||||
// test that the kernel is running
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'kernel is running');
|
||||
});
|
||||
|
||||
// test list
|
||||
this.thenEvaluate(function () {
|
||||
IPython._kernels = null;
|
||||
IPython.notebook.kernel.list(function (data) {
|
||||
IPython._kernels = data;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._kernels !== null;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var num_kernels = this.evaluate(function () {
|
||||
return IPython._kernels.length;
|
||||
});
|
||||
this.test.assertEquals(num_kernels, 1, 'one kernel running');
|
||||
});
|
||||
|
||||
// test get_info
|
||||
var kernel_info = this.evaluate(function () {
|
||||
return {
|
||||
name: IPython.notebook.kernel.name,
|
||||
id: IPython.notebook.kernel.id
|
||||
};
|
||||
});
|
||||
this.thenEvaluate(function () {
|
||||
IPython._kernel_info = null;
|
||||
IPython.notebook.kernel.get_info(function (data) {
|
||||
IPython._kernel_info = data;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._kernel_info !== null;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var new_kernel_info = this.evaluate(function () {
|
||||
return IPython._kernel_info;
|
||||
});
|
||||
this.test.assertEquals(kernel_info.name, new_kernel_info.name, 'kernel: name correct');
|
||||
this.test.assertEquals(kernel_info.id, new_kernel_info.id, 'kernel: id correct');
|
||||
});
|
||||
|
||||
// test interrupt
|
||||
this.thenEvaluate(function () {
|
||||
IPython._interrupted = false;
|
||||
IPython.notebook.kernel.interrupt(function () {
|
||||
IPython._interrupted = true;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._interrupted;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var interrupted = this.evaluate(function () {
|
||||
return IPython._interrupted;
|
||||
});
|
||||
this.test.assert(interrupted, 'kernel was interrupted');
|
||||
});
|
||||
|
||||
// test restart
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.restart();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.wait_for_kernel_ready();
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'kernel restarted');
|
||||
});
|
||||
|
||||
// test reconnect
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.stop_channels();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.reconnect();
|
||||
});
|
||||
this.wait_for_kernel_ready();
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'kernel reconnected');
|
||||
});
|
||||
|
||||
// test kernel_info_request
|
||||
this.evaluate(function () {
|
||||
IPython.notebook.kernel.kernel_info(
|
||||
function(msg){
|
||||
IPython._kernel_info_response = msg;
|
||||
});
|
||||
});
|
||||
this.waitFor(
|
||||
function () {
|
||||
return this.evaluate(function(){
|
||||
return IPython._kernel_info_response;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var kernel_info_response = this.evaluate(function(){
|
||||
return IPython._kernel_info_response;
|
||||
});
|
||||
this.test.assertTrue( kernel_info_response.msg_type === 'kernel_info_reply', 'Kernel info request return kernel_info_reply');
|
||||
this.test.assertTrue( kernel_info_response.content !== undefined, 'Kernel_info_reply is not undefined');
|
||||
});
|
||||
|
||||
// test kill
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.kill();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.then(function () {
|
||||
this.test.assert(!this.kernel_running(), 'kernel is not running');
|
||||
});
|
||||
|
||||
// test start
|
||||
var url, base_url;
|
||||
this.then(function () {
|
||||
base_url = this.evaluate(function () {
|
||||
return IPython.notebook.base_url;
|
||||
});
|
||||
url = this.evaluate(function () {
|
||||
return IPython.notebook.kernel.start();
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
this.test.assertEquals(url, base_url + "api/kernels", "start url is correct");
|
||||
});
|
||||
this.wait_for_kernel_ready();
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'kernel is running');
|
||||
});
|
||||
|
||||
// test start with parameters
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.kill();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.then(function () {
|
||||
url = this.evaluate(function () {
|
||||
return IPython.notebook.kernel.start({foo: "bar"});
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
this.test.assertEquals(url, base_url + "api/kernels?foo=bar", "start url with params is correct");
|
||||
});
|
||||
this.wait_for_kernel_ready();
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'kernel is running');
|
||||
});
|
||||
|
||||
// check for events in kill/start cycle
|
||||
this.event_test(
|
||||
'kill/start',
|
||||
[
|
||||
'kernel_killed.Kernel',
|
||||
'kernel_starting.Kernel',
|
||||
'kernel_created.Kernel',
|
||||
'kernel_connected.Kernel',
|
||||
'kernel_ready.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.kill();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.start();
|
||||
});
|
||||
}
|
||||
);
|
||||
// wait for any last idle/busy messages to be handled
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events in disconnect/connect cycle
|
||||
this.event_test(
|
||||
'reconnect',
|
||||
[
|
||||
'kernel_reconnecting.Kernel',
|
||||
'kernel_connected.Kernel',
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.stop_channels();
|
||||
IPython.notebook.kernel.reconnect(1);
|
||||
});
|
||||
}
|
||||
);
|
||||
// wait for any last idle/busy messages to be handled
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events in the restart cycle
|
||||
this.event_test(
|
||||
'restart',
|
||||
[
|
||||
'kernel_restarting.Kernel',
|
||||
'kernel_created.Kernel',
|
||||
'kernel_connected.Kernel',
|
||||
'kernel_ready.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.restart();
|
||||
});
|
||||
}
|
||||
);
|
||||
// wait for any last idle/busy messages to be handled
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events in the interrupt cycle
|
||||
this.event_test(
|
||||
'interrupt',
|
||||
[
|
||||
'kernel_interrupting.Kernel',
|
||||
'kernel_busy.Kernel',
|
||||
'kernel_idle.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.interrupt();
|
||||
});
|
||||
}
|
||||
);
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events after ws close
|
||||
this.event_test(
|
||||
'ws_closed_ok',
|
||||
[
|
||||
'kernel_disconnected.Kernel',
|
||||
'kernel_reconnecting.Kernel',
|
||||
'kernel_connected.Kernel',
|
||||
// note: there will be a 'busy' in here, too,
|
||||
// but we can't guarantee which will come first
|
||||
'kernel_idle.Kernel',
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel._ws_closed("", false);
|
||||
});
|
||||
}
|
||||
);
|
||||
// wait for any last idle/busy messages to be handled
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events after ws close (error)
|
||||
this.event_test(
|
||||
'ws_closed_error',
|
||||
[
|
||||
'kernel_disconnected.Kernel',
|
||||
'kernel_connection_failed.Kernel',
|
||||
'kernel_reconnecting.Kernel',
|
||||
'kernel_connected.Kernel',
|
||||
'kernel_idle.Kernel',
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel._ws_closed("", true);
|
||||
});
|
||||
}
|
||||
);
|
||||
// wait for any last idle/busy messages to be handled
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// start the kernel back up
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.restart();
|
||||
});
|
||||
this.waitFor(this.kernel_running);
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// test handling of autorestarting messages
|
||||
this.event_test(
|
||||
'autorestarting',
|
||||
[
|
||||
'kernel_restarting.Kernel',
|
||||
'kernel_autorestarting.Kernel',
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text('import os\n' + 'os._exit(1)');
|
||||
cell.execute();
|
||||
});
|
||||
}
|
||||
);
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// test handling of failed restart
|
||||
this.event_test(
|
||||
'failed_restart',
|
||||
[
|
||||
'kernel_restarting.Kernel',
|
||||
'kernel_autorestarting.Kernel',
|
||||
'kernel_dead.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text("import os\n" +
|
||||
"from ipykernel.connect import get_connection_file\n" +
|
||||
"with open(get_connection_file(), 'w') as f:\n" +
|
||||
" f.write('garbage')\n" +
|
||||
"os._exit(1)");
|
||||
cell.execute();
|
||||
});
|
||||
},
|
||||
|
||||
// need an extra-long timeout, because it needs to try
|
||||
// restarting the kernel 5 times!
|
||||
20000
|
||||
);
|
||||
});
|
123
.venv/Lib/site-packages/notebook/tests/services/serialize.js
Normal file
123
.venv/Lib/site-packages/notebook/tests/services/serialize.js
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Test binary messages on websockets.
|
||||
// Only works on slimer for now, due to old websocket impl in phantomjs.
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
// create EchoBuffers target on js-side.
|
||||
// it just captures and echos comm messages.
|
||||
this.then(function () {
|
||||
var success = this.evaluate(function () {
|
||||
IPython._msgs = [];
|
||||
|
||||
var EchoBuffers = function(comm) {
|
||||
this.comm = comm;
|
||||
this.comm.on_msg($.proxy(this.on_msg, this));
|
||||
};
|
||||
|
||||
EchoBuffers.prototype.on_msg = function (msg) {
|
||||
IPython._msgs.push(msg);
|
||||
this.comm.send(msg.content.data, {}, {}, msg.buffers);
|
||||
};
|
||||
|
||||
IPython.notebook.kernel.comm_manager.register_target("echo", function (comm) {
|
||||
return new EchoBuffers(comm);
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
this.test.assertEquals(success, true, "Created echo comm target");
|
||||
});
|
||||
|
||||
// Create a similar comm that captures messages Python-side
|
||||
this.then(function () {
|
||||
var index = this.append_cell([
|
||||
"import os",
|
||||
"from ipykernel.comm.comm import Comm",
|
||||
"comm = Comm(target_name='echo')",
|
||||
"msgs = []",
|
||||
"def on_msg(msg):",
|
||||
" msgs.append(msg)",
|
||||
"comm.on_msg(on_msg)"
|
||||
].join('\n'), 'code');
|
||||
this.execute_cell(index);
|
||||
});
|
||||
|
||||
// send a message with binary data
|
||||
this.then(function () {
|
||||
var index = this.append_cell([
|
||||
"buffers = [b'\\xFF\\x00', b'\\x00\\x01\\x02']",
|
||||
"comm.send(data='message 0', buffers=buffers)",
|
||||
"comm.send(data='message 1')",
|
||||
"comm.send(data='message 2', buffers=buffers)",
|
||||
].join('\n'), 'code');
|
||||
this.execute_cell(index);
|
||||
});
|
||||
|
||||
// wait for capture
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._msgs.length >= 3;
|
||||
});
|
||||
});
|
||||
|
||||
// validate captured buffers js-side
|
||||
this.then(function () {
|
||||
var msgs = this.evaluate(function () {
|
||||
return IPython._msgs;
|
||||
});
|
||||
this.test.assertEquals(msgs.length, 3, "Captured three comm messages");
|
||||
|
||||
|
||||
|
||||
// check the messages came in the right order
|
||||
this.test.assertEquals(msgs[0].content.data, "message 0", "message 0 processed first");
|
||||
this.test.assertEquals(msgs[0].buffers.length, 2, "comm message 0 has two buffers");
|
||||
this.test.assertEquals(msgs[1].content.data, "message 1", "message 1 processed second");
|
||||
this.test.assertEquals(msgs[1].buffers.length, 0, "comm message 1 has no buffers");
|
||||
this.test.assertEquals(msgs[2].content.data, "message 2", "message 2 processed third");
|
||||
this.test.assertEquals(msgs[2].buffers.length, 2, "comm message 2 has two buffers");
|
||||
|
||||
// extract attributes to test in evaluate,
|
||||
// because the raw DataViews can't be passed across
|
||||
var buf_info = function (message, index) {
|
||||
var buf = IPython._msgs[message].buffers[index];
|
||||
var data = {};
|
||||
data.byteLength = buf.byteLength;
|
||||
data.bytes = [];
|
||||
for (var i = 0; i < data.byteLength; i++) {
|
||||
data.bytes.push(buf.getUint8(i));
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
var msgs_with_buffers = [0, 2];
|
||||
for (var i = 0; i < msgs_with_buffers.length; i++) {
|
||||
msg_index = msgs_with_buffers[i];
|
||||
buf0 = this.evaluate(buf_info, msg_index, 0);
|
||||
buf1 = this.evaluate(buf_info, msg_index, 1);
|
||||
this.test.assertEquals(buf0.byteLength, 2, 'buf[0] has correct size in message '+msg_index);
|
||||
this.test.assertEquals(buf0.bytes, [255, 0], 'buf[0] has correct bytes in message '+msg_index);
|
||||
this.test.assertEquals(buf1.byteLength, 3, 'buf[1] has correct size in message '+msg_index);
|
||||
this.test.assertEquals(buf1.bytes, [0, 1, 2], 'buf[1] has correct bytes in message '+msg_index);
|
||||
}
|
||||
});
|
||||
|
||||
// validate captured buffers Python-side
|
||||
this.then(function () {
|
||||
var index = this.append_cell([
|
||||
"assert len(msgs) == 3, len(msgs)",
|
||||
"bufs = msgs[0]['buffers']",
|
||||
"assert len(bufs) == len(buffers), bufs",
|
||||
"assert bufs[0].tobytes() == buffers[0], bufs[0]",
|
||||
"assert bufs[1].tobytes() == buffers[1], bufs[1]",
|
||||
"1",
|
||||
].join('\n'), 'code');
|
||||
this.execute_cell(index);
|
||||
this.wait_for_output(index);
|
||||
this.then(function () {
|
||||
var out = this.get_output_cell(index);
|
||||
this.test.assertEquals(out.data['text/plain'], '1', "Python received buffers");
|
||||
});
|
||||
});
|
||||
});
|
186
.venv/Lib/site-packages/notebook/tests/services/session.js
Normal file
186
.venv/Lib/site-packages/notebook/tests/services/session.js
Normal file
@ -0,0 +1,186 @@
|
||||
|
||||
//
|
||||
// Tests for the Session object
|
||||
//
|
||||
|
||||
casper.notebook_test(function () {
|
||||
var that = this;
|
||||
var get_info = function () {
|
||||
return that.evaluate(function () {
|
||||
return JSON.parse(JSON.stringify(IPython.notebook.session._get_model()));
|
||||
});
|
||||
};
|
||||
|
||||
// test that the kernel is running
|
||||
this.then(function () {
|
||||
this.test.assert(this.kernel_running(), 'session: kernel is running');
|
||||
});
|
||||
|
||||
// test list
|
||||
this.thenEvaluate(function () {
|
||||
IPython._sessions = null;
|
||||
IPython.notebook.session.list(function (data) {
|
||||
IPython._sessions = data;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._sessions !== null;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var num_sessions = this.evaluate(function () {
|
||||
return IPython._sessions.length;
|
||||
});
|
||||
this.test.assertEquals(num_sessions, 1, 'one session running');
|
||||
});
|
||||
|
||||
// test get_info
|
||||
var session_info = get_info();
|
||||
this.thenEvaluate(function () {
|
||||
IPython._session_info = null;
|
||||
IPython.notebook.session.get_info(function (data) {
|
||||
IPython._session_info = data;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._session_info !== null;
|
||||
});
|
||||
});
|
||||
this.then(function () {
|
||||
var new_session_info = this.evaluate(function () {
|
||||
return IPython._session_info;
|
||||
});
|
||||
this.test.assertEquals(session_info.name, new_session_info.name, 'session: notebook name correct');
|
||||
this.test.assertEquals(session_info.path, new_session_info.path, 'session: notebook path correct');
|
||||
this.test.assertEquals(session_info.kernel.name, new_session_info.kernel.name, 'session: kernel name correct');
|
||||
this.test.assertEquals(session_info.kernel.id, new_session_info.kernel.id, 'session: kernel id correct');
|
||||
});
|
||||
|
||||
// test rename_notebook
|
||||
//
|
||||
// TODO: the PATCH request isn't supported by phantom, so this test always
|
||||
// fails, see https://github.com/ariya/phantomjs/issues/11384
|
||||
// when this is fixed we can properly run this test
|
||||
//
|
||||
// this.thenEvaluate(function () {
|
||||
// IPython._renamed = false;
|
||||
// IPython.notebook.session.rename_notebook(
|
||||
// "foo",
|
||||
// "bar",
|
||||
// function (data) {
|
||||
// IPython._renamed = true;
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
// this.waitFor(function () {
|
||||
// return this.evaluate(function () {
|
||||
// return IPython._renamed;
|
||||
// });
|
||||
// });
|
||||
// this.then(function () {
|
||||
// var info = get_info();
|
||||
// this.test.assertEquals(info.notebook.name, "foo", "notebook was renamed");
|
||||
// this.test.assertEquals(info.notebook.path, "bar", "notebook path was changed");
|
||||
// });
|
||||
|
||||
// test delete
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.session.delete();
|
||||
});
|
||||
this.waitFor(this.kernel_disconnected);
|
||||
this.then(function () {
|
||||
this.test.assert(!this.kernel_running(), 'session deletes kernel');
|
||||
});
|
||||
|
||||
// check for events when starting the session
|
||||
this.event_test(
|
||||
'start_session',
|
||||
[
|
||||
'kernel_created.Session',
|
||||
'kernel_connected.Kernel',
|
||||
'kernel_ready.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.session.start();
|
||||
});
|
||||
}
|
||||
);
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events when killing the session
|
||||
this.event_test(
|
||||
'delete_session',
|
||||
['kernel_killed.Session'],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.session.delete();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.thenEvaluate( function() {IPython.notebook.session.start()});
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events when restarting the session
|
||||
this.event_test(
|
||||
'restart_session',
|
||||
[
|
||||
'kernel_killed.Session',
|
||||
'kernel_created.Session',
|
||||
'kernel_connected.Kernel',
|
||||
'kernel_ready.Kernel'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.session.restart();
|
||||
});
|
||||
}
|
||||
);
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// test handling of failed restart
|
||||
this.event_test(
|
||||
'failed_restart',
|
||||
[
|
||||
'kernel_restarting.Kernel',
|
||||
'kernel_autorestarting.Kernel',
|
||||
'kernel_killed.Session',
|
||||
'kernel_dead.Kernel',
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text("import os\n" +
|
||||
"from ipykernel.connect import get_connection_file\n" +
|
||||
"with open(get_connection_file(), 'w') as f:\n" +
|
||||
" f.write('garbage')\n" +
|
||||
"os._exit(1)");
|
||||
cell.execute();
|
||||
});
|
||||
},
|
||||
|
||||
// need an extra-long timeout, because it needs to try
|
||||
// restarting the kernel 5 times!
|
||||
20000
|
||||
);
|
||||
|
||||
this.thenEvaluate( function() {IPython.notebook.session.start()});
|
||||
this.wait_for_kernel_ready();
|
||||
|
||||
// check for events when starting a nonexistent kernel
|
||||
this.event_test(
|
||||
'bad_start_session',
|
||||
[
|
||||
'kernel_killed.Session',
|
||||
'kernel_dead.Session'
|
||||
],
|
||||
function () {
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.session.restart({kernel_name: 'foo'});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
@ -0,0 +1,57 @@
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from notebook.config_manager import BaseJSONConfigManager
|
||||
|
||||
|
||||
def test_json():
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
try:
|
||||
root_data = dict(a=1, x=2, nest={'a':1, 'x':2})
|
||||
with open(os.path.join(tmpdir, 'foo.json'), 'w') as f:
|
||||
json.dump(root_data, f)
|
||||
# also make a foo.d/ directory with multiple json files
|
||||
os.makedirs(os.path.join(tmpdir, 'foo.d'))
|
||||
with open(os.path.join(tmpdir, 'foo.d', 'a.json'), 'w') as f:
|
||||
json.dump(dict(a=2, b=1, nest={'a':2, 'b':1}), f)
|
||||
with open(os.path.join(tmpdir, 'foo.d', 'b.json'), 'w') as f:
|
||||
json.dump(dict(a=3, b=2, c=3, nest={'a':3, 'b':2, 'c':3}, only_in_b={'x':1}), f)
|
||||
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=False)
|
||||
data = manager.get('foo')
|
||||
assert 'a' in data
|
||||
assert 'x' in data
|
||||
assert 'b' not in data
|
||||
assert 'c' not in data
|
||||
assert data['a'] == 1
|
||||
assert 'x' in data['nest']
|
||||
# if we write it out, it also shouldn't pick up the subdirectory
|
||||
manager.set('foo', data)
|
||||
data = manager.get('foo')
|
||||
assert data == root_data
|
||||
|
||||
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=True)
|
||||
data = manager.get('foo')
|
||||
assert 'a' in data
|
||||
assert 'b' in data
|
||||
assert 'c' in data
|
||||
# files should be read in order foo.d/a.json foo.d/b.json foo.json
|
||||
assert data['a'] == 1
|
||||
assert data['b'] == 2
|
||||
assert data['c'] == 3
|
||||
assert data['nest']['a'] == 1
|
||||
assert data['nest']['b'] == 2
|
||||
assert data['nest']['c'] == 3
|
||||
assert data['nest']['x'] == 2
|
||||
|
||||
# when writing out, we don't want foo.d/*.json data to be included in the root foo.json
|
||||
manager.set('foo', data)
|
||||
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=False)
|
||||
data = manager.get('foo')
|
||||
assert data == root_data
|
||||
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
172
.venv/Lib/site-packages/notebook/tests/test_files.py
Normal file
172
.venv/Lib/site-packages/notebook/tests/test_files.py
Normal file
@ -0,0 +1,172 @@
|
||||
"""Test the /files/ handler."""
|
||||
|
||||
import os
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
import json
|
||||
|
||||
from nbformat import write
|
||||
from nbformat.v4 import (new_notebook,
|
||||
new_markdown_cell, new_code_cell,
|
||||
new_output)
|
||||
|
||||
from notebook.utils import url_path_join
|
||||
from .launchnotebook import NotebookTestBase
|
||||
|
||||
|
||||
class FilesTest(NotebookTestBase):
|
||||
def test_hidden_files(self):
|
||||
not_hidden = [
|
||||
'å b',
|
||||
'å b/ç. d',
|
||||
]
|
||||
hidden = [
|
||||
'.å b',
|
||||
'å b/.ç d',
|
||||
]
|
||||
dirs = not_hidden + hidden
|
||||
|
||||
nbdir = self.notebook_dir
|
||||
for d in dirs:
|
||||
path = pjoin(nbdir, d.replace('/', os.sep))
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
with open(pjoin(path, 'foo'), 'w') as f:
|
||||
f.write('foo')
|
||||
with open(pjoin(path, '.foo'), 'w') as f:
|
||||
f.write('.foo')
|
||||
|
||||
for d in not_hidden:
|
||||
path = pjoin(nbdir, d.replace('/', os.sep))
|
||||
r = self.request('GET', url_path_join('files', d, 'foo'))
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.text, 'foo')
|
||||
r = self.request('GET', url_path_join('files', d, '.foo'))
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
for d in hidden:
|
||||
path = pjoin(nbdir, d.replace('/', os.sep))
|
||||
for foo in ('foo', '.foo'):
|
||||
r = self.request('GET', url_path_join('files', d, foo))
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
self.notebook.contents_manager.allow_hidden = True
|
||||
try:
|
||||
for d in not_hidden:
|
||||
path = pjoin(nbdir, d.replace('/', os.sep))
|
||||
r = self.request('GET', url_path_join('files', d, 'foo'))
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.text, 'foo')
|
||||
r = self.request('GET', url_path_join('files', d, '.foo'))
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.text, '.foo')
|
||||
|
||||
for d in hidden:
|
||||
path = pjoin(nbdir, d.replace('/', os.sep))
|
||||
for foo in ('foo', '.foo'):
|
||||
r = self.request('GET', url_path_join('files', d, foo))
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.text, foo)
|
||||
finally:
|
||||
self.notebook.contents_manager.allow_hidden = False
|
||||
|
||||
def test_contents_manager(self):
|
||||
"make sure ContentsManager returns right files (ipynb, bin, txt)."
|
||||
|
||||
nbdir = self.notebook_dir
|
||||
|
||||
nb = new_notebook(
|
||||
cells=[
|
||||
new_markdown_cell('Created by test ³'),
|
||||
new_code_cell("print(2*6)", outputs=[
|
||||
new_output("stream", text="12"),
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
with open(pjoin(nbdir, 'testnb.ipynb'), 'w',
|
||||
encoding='utf-8') as f:
|
||||
write(nb, f, version=4)
|
||||
|
||||
with open(pjoin(nbdir, 'test.bin'), 'wb') as f:
|
||||
f.write(b'\xff' + os.urandom(5))
|
||||
f.close()
|
||||
|
||||
with open(pjoin(nbdir, 'test.txt'), 'w') as f:
|
||||
f.write('foobar')
|
||||
f.close()
|
||||
|
||||
r = self.request('GET', 'files/testnb.ipynb')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn('print(2*6)', r.text)
|
||||
json.loads(r.text)
|
||||
|
||||
r = self.request('GET', 'files/test.bin')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.headers['content-type'], 'application/octet-stream')
|
||||
self.assertEqual(r.content[:1], b'\xff')
|
||||
self.assertEqual(len(r.content), 6)
|
||||
|
||||
r = self.request('GET', 'files/test.txt')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.headers['content-type'], 'text/plain; charset=UTF-8')
|
||||
self.assertEqual(r.text, 'foobar')
|
||||
|
||||
def test_download(self):
|
||||
nbdir = self.notebook_dir
|
||||
|
||||
text = 'hello'
|
||||
with open(pjoin(nbdir, 'test.txt'), 'w') as f:
|
||||
f.write(text)
|
||||
|
||||
r = self.request('GET', 'files/test.txt')
|
||||
disposition = r.headers.get('Content-Disposition', '')
|
||||
self.assertNotIn('attachment', disposition)
|
||||
|
||||
r = self.request('GET', 'files/test.txt?download=1')
|
||||
disposition = r.headers.get('Content-Disposition', '')
|
||||
self.assertIn('attachment', disposition)
|
||||
self.assertIn("filename*=utf-8''test.txt", disposition)
|
||||
|
||||
def test_view_html(self):
|
||||
nbdir = self.notebook_dir
|
||||
|
||||
html = '<div>Test test</div>'
|
||||
with open(pjoin(nbdir, 'test.html'), 'w') as f:
|
||||
f.write(html)
|
||||
|
||||
r = self.request('GET', 'view/test.html')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
def test_old_files_redirect(self):
|
||||
"""pre-2.0 'files/' prefixed links are properly redirected"""
|
||||
nbdir = self.notebook_dir
|
||||
|
||||
os.mkdir(pjoin(nbdir, 'files'))
|
||||
os.makedirs(pjoin(nbdir, 'sub', 'files'))
|
||||
|
||||
for prefix in ('', 'sub'):
|
||||
with open(pjoin(nbdir, prefix, 'files', 'f1.txt'), 'w') as f:
|
||||
f.write(prefix + '/files/f1')
|
||||
with open(pjoin(nbdir, prefix, 'files', 'f2.txt'), 'w') as f:
|
||||
f.write(prefix + '/files/f2')
|
||||
with open(pjoin(nbdir, prefix, 'f2.txt'), 'w') as f:
|
||||
f.write(prefix + '/f2')
|
||||
with open(pjoin(nbdir, prefix, 'f3.txt'), 'w') as f:
|
||||
f.write(prefix + '/f3')
|
||||
|
||||
url = url_path_join('notebooks', prefix, 'files', 'f1.txt')
|
||||
r = self.request('GET', url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.text, prefix + '/files/f1')
|
||||
|
||||
url = url_path_join('notebooks', prefix, 'files', 'f2.txt')
|
||||
r = self.request('GET', url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.text, prefix + '/files/f2')
|
||||
|
||||
url = url_path_join('notebooks', prefix, 'files', 'f3.txt')
|
||||
r = self.request('GET', url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.text, prefix + '/f3')
|
351
.venv/Lib/site-packages/notebook/tests/test_gateway.py
Normal file
351
.venv/Lib/site-packages/notebook/tests/test_gateway.py
Normal file
@ -0,0 +1,351 @@
|
||||
"""Test GatewayClient"""
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from unittest.mock import patch
|
||||
|
||||
from tornado.web import HTTPError
|
||||
from tornado.httpclient import HTTPRequest, HTTPResponse
|
||||
|
||||
from notebook.gateway.managers import GatewayClient
|
||||
from notebook.utils import maybe_future
|
||||
from .launchnotebook import NotebookTestBase
|
||||
|
||||
|
||||
def generate_kernelspec(name):
|
||||
argv_stanza = ['python', '-m', 'ipykernel_launcher', '-f', '{connection_file}']
|
||||
spec_stanza = {'spec': {'argv': argv_stanza, 'env': {}, 'display_name': name, 'language': 'python', 'interrupt_mode': 'signal', 'metadata': {}}}
|
||||
kernelspec_stanza = {'name': name, 'spec': spec_stanza, 'resources': {}}
|
||||
return kernelspec_stanza
|
||||
|
||||
|
||||
# We'll mock up two kernelspecs - kspec_foo and kspec_bar
|
||||
kernelspecs = {'default': 'kspec_foo', 'kernelspecs': {'kspec_foo': generate_kernelspec('kspec_foo'), 'kspec_bar': generate_kernelspec('kspec_bar')}}
|
||||
|
||||
|
||||
# maintain a dictionary of expected running kernels. Key = kernel_id, Value = model.
|
||||
running_kernels = dict()
|
||||
|
||||
|
||||
def generate_model(name):
|
||||
"""Generate a mocked kernel model. Caller is responsible for adding model to running_kernels dictionary."""
|
||||
dt = datetime.utcnow().isoformat() + 'Z'
|
||||
kernel_id = str(uuid.uuid4())
|
||||
model = {'id': kernel_id, 'name': name, 'last_activity': str(dt), 'execution_state': 'idle', 'connections': 1}
|
||||
return model
|
||||
|
||||
|
||||
async def mock_gateway_request(url, **kwargs):
|
||||
method = 'GET'
|
||||
if kwargs['method']:
|
||||
method = kwargs['method']
|
||||
|
||||
request = HTTPRequest(url=url, **kwargs)
|
||||
|
||||
endpoint = str(url)
|
||||
|
||||
# Fetch all kernelspecs
|
||||
if endpoint.endswith('/api/kernelspecs') and method == 'GET':
|
||||
response_buf = StringIO(json.dumps(kernelspecs))
|
||||
response = await maybe_future(HTTPResponse(request, 200, buffer=response_buf))
|
||||
return response
|
||||
|
||||
# Fetch named kernelspec
|
||||
if endpoint.rfind('/api/kernelspecs/') >= 0 and method == 'GET':
|
||||
requested_kernelspec = endpoint.rpartition('/')[2]
|
||||
kspecs = kernelspecs.get('kernelspecs')
|
||||
if requested_kernelspec in kspecs:
|
||||
response_buf = StringIO(json.dumps(kspecs.get(requested_kernelspec)))
|
||||
response = await maybe_future(HTTPResponse(request, 200, buffer=response_buf))
|
||||
return response
|
||||
else:
|
||||
raise HTTPError(404, message=f'Kernelspec does not exist: {requested_kernelspec}')
|
||||
|
||||
# Create kernel
|
||||
if endpoint.endswith('/api/kernels') and method == 'POST':
|
||||
json_body = json.loads(kwargs['body'])
|
||||
name = json_body.get('name')
|
||||
env = json_body.get('env')
|
||||
kspec_name = env.get('KERNEL_KSPEC_NAME')
|
||||
assert name == kspec_name # Ensure that KERNEL_ env values get propagated
|
||||
model = generate_model(name)
|
||||
running_kernels[model.get('id')] = model # Register model as a running kernel
|
||||
response_buf = StringIO(json.dumps(model))
|
||||
response = await maybe_future(HTTPResponse(request, 201, buffer=response_buf))
|
||||
return response
|
||||
|
||||
# Fetch list of running kernels
|
||||
if endpoint.endswith('/api/kernels') and method == 'GET':
|
||||
kernels = []
|
||||
for kernel_id in running_kernels.keys():
|
||||
model = running_kernels.get(kernel_id)
|
||||
kernels.append(model)
|
||||
response_buf = StringIO(json.dumps(kernels))
|
||||
response = await maybe_future(HTTPResponse(request, 200, buffer=response_buf))
|
||||
return response
|
||||
|
||||
# Interrupt or restart existing kernel
|
||||
if endpoint.rfind('/api/kernels/') >= 0 and method == 'POST':
|
||||
requested_kernel_id, sep, action = endpoint.rpartition('/api/kernels/')[2].rpartition('/')
|
||||
|
||||
if action == 'interrupt':
|
||||
if requested_kernel_id in running_kernels:
|
||||
response = await maybe_future(HTTPResponse(request, 204))
|
||||
return response
|
||||
else:
|
||||
raise HTTPError(404, message=f'Kernel does not exist: {requested_kernel_id}')
|
||||
elif action == 'restart':
|
||||
if requested_kernel_id in running_kernels:
|
||||
response_buf = StringIO(json.dumps(running_kernels.get(requested_kernel_id)))
|
||||
response = await maybe_future(HTTPResponse(request, 204, buffer=response_buf))
|
||||
return response
|
||||
else:
|
||||
raise HTTPError(404, message=f'Kernel does not exist: {requested_kernel_id}')
|
||||
else:
|
||||
raise HTTPError(404, message=f'Bad action detected: {action}')
|
||||
|
||||
# Shutdown existing kernel
|
||||
if endpoint.rfind('/api/kernels/') >= 0 and method == 'DELETE':
|
||||
requested_kernel_id = endpoint.rpartition('/')[2]
|
||||
running_kernels.pop(requested_kernel_id) # Simulate shutdown by removing kernel from running set
|
||||
response = await maybe_future(HTTPResponse(request, 204))
|
||||
return response
|
||||
|
||||
# Fetch existing kernel
|
||||
if endpoint.rfind('/api/kernels/') >= 0 and method == 'GET':
|
||||
requested_kernel_id = endpoint.rpartition('/')[2]
|
||||
if requested_kernel_id in running_kernels:
|
||||
response_buf = StringIO(json.dumps(running_kernels.get(requested_kernel_id)))
|
||||
response = await maybe_future(HTTPResponse(request, 200, buffer=response_buf))
|
||||
return response
|
||||
else:
|
||||
raise HTTPError(404, message=f'Kernel does not exist: {requested_kernel_id}')
|
||||
|
||||
|
||||
mocked_gateway = patch('notebook.gateway.managers.gateway_request', mock_gateway_request)
|
||||
|
||||
|
||||
class TestGateway(NotebookTestBase):
|
||||
|
||||
mock_gateway_url = 'http://mock-gateway-server:8889'
|
||||
mock_http_user = 'alice'
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
GatewayClient.clear_instance()
|
||||
super().setup_class()
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
GatewayClient.clear_instance()
|
||||
super().teardown_class()
|
||||
|
||||
@classmethod
|
||||
def get_patch_env(cls):
|
||||
test_env = super().get_patch_env()
|
||||
test_env.update({'JUPYTER_GATEWAY_URL': TestGateway.mock_gateway_url,
|
||||
'JUPYTER_GATEWAY_CONNECT_TIMEOUT': '44.4'})
|
||||
return test_env
|
||||
|
||||
@classmethod
|
||||
def get_argv(cls):
|
||||
argv = super().get_argv()
|
||||
argv.extend(['--GatewayClient.request_timeout=96.0', '--GatewayClient.http_user=' + TestGateway.mock_http_user])
|
||||
return argv
|
||||
|
||||
def setUp(self):
|
||||
kwargs = dict()
|
||||
GatewayClient.instance().load_connection_args(**kwargs)
|
||||
super().setUp()
|
||||
|
||||
def test_gateway_options(self):
|
||||
assert self.notebook.gateway_config.gateway_enabled == True
|
||||
assert self.notebook.gateway_config.url == TestGateway.mock_gateway_url
|
||||
assert self.notebook.gateway_config.http_user == TestGateway.mock_http_user
|
||||
assert self.notebook.gateway_config.connect_timeout == self.notebook.gateway_config.connect_timeout
|
||||
assert self.notebook.gateway_config.connect_timeout == 44.4
|
||||
assert self.notebook.gateway_config.request_timeout == 96.0
|
||||
assert os.environ['KERNEL_LAUNCH_TIMEOUT'] == str(96) # Ensure KLT gets set from request-timeout
|
||||
|
||||
def test_gateway_class_mappings(self):
|
||||
# Ensure appropriate class mappings are in place.
|
||||
assert self.notebook.kernel_manager_class.__name__ == 'GatewayKernelManager'
|
||||
assert self.notebook.session_manager_class.__name__ == 'GatewaySessionManager'
|
||||
assert self.notebook.kernel_spec_manager_class.__name__ == 'GatewayKernelSpecManager'
|
||||
|
||||
def test_gateway_get_kernelspecs(self):
|
||||
# Validate that kernelspecs come from gateway.
|
||||
with mocked_gateway:
|
||||
response = self.request('GET', '/api/kernelspecs')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
kspecs = content.get('kernelspecs')
|
||||
assert len(kspecs) == 2
|
||||
assert kspecs.get('kspec_bar').get('name') == 'kspec_bar'
|
||||
|
||||
def test_gateway_get_named_kernelspec(self):
|
||||
# Validate that a specific kernelspec can be retrieved from gateway.
|
||||
with mocked_gateway:
|
||||
response = self.request('GET', '/api/kernelspecs/kspec_foo')
|
||||
assert response.status_code == 200
|
||||
kspec_foo = json.loads(response.content.decode('utf-8'))
|
||||
assert kspec_foo.get('name') == 'kspec_foo'
|
||||
|
||||
response = self.request('GET', '/api/kernelspecs/no_such_spec')
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_gateway_session_lifecycle(self):
|
||||
# Validate session lifecycle functions; create and delete.
|
||||
|
||||
# create
|
||||
session_id, kernel_id = self.create_session('kspec_foo')
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# interrupt
|
||||
self.interrupt_kernel(kernel_id)
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# restart
|
||||
self.restart_kernel(kernel_id)
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# delete
|
||||
self.delete_session(session_id)
|
||||
self.assertFalse(self.is_kernel_running(kernel_id))
|
||||
|
||||
def test_gateway_kernel_lifecycle(self):
|
||||
# Validate kernel lifecycle functions; create, interrupt, restart and delete.
|
||||
|
||||
# create
|
||||
kernel_id = self.create_kernel('kspec_bar')
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# interrupt
|
||||
self.interrupt_kernel(kernel_id)
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# restart
|
||||
self.restart_kernel(kernel_id)
|
||||
|
||||
# ensure kernel still considered running
|
||||
self.assertTrue(self.is_kernel_running(kernel_id))
|
||||
|
||||
# delete
|
||||
self.delete_kernel(kernel_id)
|
||||
self.assertFalse(self.is_kernel_running(kernel_id))
|
||||
|
||||
def create_session(self, kernel_name):
|
||||
"""Creates a session for a kernel. The session is created against the notebook server
|
||||
which then uses the gateway for kernel management.
|
||||
"""
|
||||
with mocked_gateway:
|
||||
nb_path = os.path.join(self.notebook_dir, 'testgw.ipynb')
|
||||
kwargs = dict()
|
||||
kwargs['json'] = {'path': nb_path, 'type': 'notebook', 'kernel': {'name': kernel_name}}
|
||||
|
||||
# add a KERNEL_ value to the current env and we'll ensure that that value exists in the mocked method
|
||||
os.environ['KERNEL_KSPEC_NAME'] = kernel_name
|
||||
|
||||
# Create the kernel... (also tests get_kernel)
|
||||
response = self.request('POST', '/api/sessions', **kwargs)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
model = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(model.get('path'), nb_path)
|
||||
kernel_id = model.get('kernel').get('id')
|
||||
# ensure its in the running_kernels and name matches.
|
||||
running_kernel = running_kernels.get(kernel_id)
|
||||
self.assertEqual(kernel_id, running_kernel.get('id'))
|
||||
self.assertEqual(model.get('kernel').get('name'), running_kernel.get('name'))
|
||||
session_id = model.get('id')
|
||||
|
||||
# restore env
|
||||
os.environ.pop('KERNEL_KSPEC_NAME')
|
||||
return session_id, kernel_id
|
||||
|
||||
def delete_session(self, session_id):
|
||||
"""Deletes a session corresponding to the given session id.
|
||||
"""
|
||||
with mocked_gateway:
|
||||
# Delete the session (and kernel)
|
||||
response = self.request('DELETE', '/api/sessions/' + session_id)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(response.reason, 'No Content')
|
||||
|
||||
def is_kernel_running(self, kernel_id):
|
||||
"""Issues request to get the set of running kernels
|
||||
"""
|
||||
with mocked_gateway:
|
||||
# Get list of running kernels
|
||||
response = self.request('GET', '/api/kernels')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
kernels = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(len(kernels), len(running_kernels))
|
||||
for model in kernels:
|
||||
if model.get('id') == kernel_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_kernel(self, kernel_name):
|
||||
"""Issues request to restart the given kernel
|
||||
"""
|
||||
with mocked_gateway:
|
||||
kwargs = dict()
|
||||
kwargs['json'] = {'name': kernel_name}
|
||||
|
||||
# add a KERNEL_ value to the current env and we'll ensure that that value exists in the mocked method
|
||||
os.environ['KERNEL_KSPEC_NAME'] = kernel_name
|
||||
|
||||
response = self.request('POST', '/api/kernels', **kwargs)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
model = json.loads(response.content.decode('utf-8'))
|
||||
kernel_id = model.get('id')
|
||||
# ensure its in the running_kernels and name matches.
|
||||
running_kernel = running_kernels.get(kernel_id)
|
||||
self.assertEqual(kernel_id, running_kernel.get('id'))
|
||||
self.assertEqual(model.get('name'), kernel_name)
|
||||
|
||||
# restore env
|
||||
os.environ.pop('KERNEL_KSPEC_NAME')
|
||||
return kernel_id
|
||||
|
||||
def interrupt_kernel(self, kernel_id):
|
||||
"""Issues request to interrupt the given kernel
|
||||
"""
|
||||
with mocked_gateway:
|
||||
response = self.request('POST', '/api/kernels/' + kernel_id + '/interrupt')
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(response.reason, 'No Content')
|
||||
|
||||
def restart_kernel(self, kernel_id):
|
||||
"""Issues request to restart the given kernel
|
||||
"""
|
||||
with mocked_gateway:
|
||||
response = self.request('POST', '/api/kernels/' + kernel_id + '/restart')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
model = json.loads(response.content.decode('utf-8'))
|
||||
restarted_kernel_id = model.get('id')
|
||||
# ensure its in the running_kernels and name matches.
|
||||
running_kernel = running_kernels.get(restarted_kernel_id)
|
||||
self.assertEqual(restarted_kernel_id, running_kernel.get('id'))
|
||||
self.assertEqual(model.get('name'), running_kernel.get('name'))
|
||||
|
||||
def delete_kernel(self, kernel_id):
|
||||
"""Deletes kernel corresponding to the given kernel id.
|
||||
"""
|
||||
with mocked_gateway:
|
||||
# Delete the session (and kernel)
|
||||
response = self.request('DELETE', '/api/kernels/' + kernel_id)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(response.reason, 'No Content')
|
7
.venv/Lib/site-packages/notebook/tests/test_i18n.py
Normal file
7
.venv/Lib/site-packages/notebook/tests/test_i18n.py
Normal file
@ -0,0 +1,7 @@
|
||||
from notebook import i18n
|
||||
|
||||
def test_parse_accept_lang_header():
|
||||
palh = i18n.parse_accept_lang_header
|
||||
assert palh('') == []
|
||||
assert palh('zh-CN,en-GB;q=0.7,en;q=0.3') == ['en', 'en_GB', 'zh', 'zh_CN']
|
||||
assert palh('nl,fr;q=0') == ['nl']
|
40
.venv/Lib/site-packages/notebook/tests/test_log.py
Normal file
40
.venv/Lib/site-packages/notebook/tests/test_log.py
Normal file
@ -0,0 +1,40 @@
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from notebook import log
|
||||
|
||||
|
||||
class TestLogRequest(unittest.TestCase):
|
||||
|
||||
@mock.patch('notebook.log.prometheus_log_method')
|
||||
def test_log_request_json(self, mock_prometheus):
|
||||
headers = {'Referer': 'test'}
|
||||
request = mock.Mock(
|
||||
request_time=mock.Mock(return_value=1),
|
||||
headers=headers,
|
||||
method='GET',
|
||||
remote_ip='1.2.3.4',
|
||||
uri='/notebooks/foo/bar'
|
||||
)
|
||||
handler = mock.MagicMock(
|
||||
request=request,
|
||||
get_status=mock.Mock(return_value=500)
|
||||
)
|
||||
logger = mock.MagicMock()
|
||||
log.log_request(handler, log=logger, log_json=True)
|
||||
# Since the status was 500 there should be two calls to log.error,
|
||||
# one with the request headers and another with the other request
|
||||
# parameters.
|
||||
self.assertEqual(2, logger.error.call_count)
|
||||
logger.error.assert_has_calls([
|
||||
mock.call("", extra=dict(props=dict(headers))),
|
||||
mock.call("", extra=dict(props={
|
||||
'status': handler.get_status(),
|
||||
'method': request.method,
|
||||
'ip': request.remote_ip,
|
||||
'uri': request.uri,
|
||||
'request_time': 1000.0,
|
||||
'referer': headers['Referer']
|
||||
}))
|
||||
])
|
||||
mock_prometheus.assert_called_once_with(handler)
|
514
.venv/Lib/site-packages/notebook/tests/test_nbextensions.py
Normal file
514
.venv/Lib/site-packages/notebook/tests/test_nbextensions.py
Normal file
@ -0,0 +1,514 @@
|
||||
"""Test installation of notebook extensions"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import glob
|
||||
import os
|
||||
import pytest
|
||||
import sys
|
||||
import tarfile
|
||||
import zipfile
|
||||
from io import BytesIO, StringIO
|
||||
from os.path import basename, join as pjoin
|
||||
from traitlets.tests.utils import check_help_all_output
|
||||
from unittest import TestCase
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from ipython_genutils import py3compat
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
from notebook import nbextensions
|
||||
from notebook.nbextensions import (install_nbextension, check_nbextension,
|
||||
enable_nbextension, disable_nbextension,
|
||||
install_nbextension_python, uninstall_nbextension_python,
|
||||
enable_nbextension_python, disable_nbextension_python, _get_config_dir,
|
||||
validate_nbextension, validate_nbextension_python
|
||||
)
|
||||
|
||||
from notebook.config_manager import BaseJSONConfigManager
|
||||
|
||||
|
||||
def touch(file_name, mtime=None):
|
||||
"""ensure a file exists, and set its modification time
|
||||
|
||||
returns the modification time of the file
|
||||
"""
|
||||
open(file_name, 'a').close()
|
||||
# set explicit mtime
|
||||
if mtime:
|
||||
atime = os.stat(file_name).st_atime
|
||||
os.utime(file_name, (atime, mtime))
|
||||
return os.stat(file_name).st_mtime
|
||||
|
||||
|
||||
def test_help_output():
|
||||
check_help_all_output('notebook.nbextensions')
|
||||
check_help_all_output('notebook.nbextensions', ['enable'])
|
||||
check_help_all_output('notebook.nbextensions', ['disable'])
|
||||
check_help_all_output('notebook.nbextensions', ['install'])
|
||||
check_help_all_output('notebook.nbextensions', ['uninstall'])
|
||||
|
||||
|
||||
class TestInstallNBExtension(TestCase):
|
||||
|
||||
def tempdir(self):
|
||||
td = TemporaryDirectory()
|
||||
self.tempdirs.append(td)
|
||||
return py3compat.cast_unicode(td.name)
|
||||
|
||||
def setUp(self):
|
||||
# Any TemporaryDirectory objects appended to this list will be cleaned
|
||||
# up at the end of the test run.
|
||||
self.tempdirs = []
|
||||
|
||||
@self.addCleanup
|
||||
def cleanup_tempdirs():
|
||||
for d in self.tempdirs:
|
||||
d.cleanup()
|
||||
|
||||
self.src = self.tempdir()
|
||||
self.files = files = [
|
||||
pjoin('ƒile'),
|
||||
pjoin('∂ir', 'ƒile1'),
|
||||
pjoin('∂ir', '∂ir2', 'ƒile2'),
|
||||
]
|
||||
for file_name in files:
|
||||
fullpath = os.path.join(self.src, file_name)
|
||||
parent = os.path.dirname(fullpath)
|
||||
if not os.path.exists(parent):
|
||||
os.makedirs(parent)
|
||||
touch(fullpath)
|
||||
|
||||
self.test_dir = self.tempdir()
|
||||
self.data_dir = os.path.join(self.test_dir, 'data')
|
||||
self.config_dir = os.path.join(self.test_dir, 'config')
|
||||
self.system_data_dir = os.path.join(self.test_dir, 'system_data')
|
||||
self.system_path = [self.system_data_dir]
|
||||
self.system_nbext = os.path.join(self.system_data_dir, 'nbextensions')
|
||||
|
||||
# Patch out os.environ so that tests are isolated from the real OS
|
||||
# environment.
|
||||
self.patch_env = patch.dict('os.environ', {
|
||||
'JUPYTER_CONFIG_DIR': self.config_dir,
|
||||
'JUPYTER_DATA_DIR': self.data_dir,
|
||||
})
|
||||
self.patch_env.start()
|
||||
self.addCleanup(self.patch_env.stop)
|
||||
|
||||
# Patch out the system path os that we consistently use our own
|
||||
# temporary directory instead.
|
||||
self.patch_system_path = patch.object(
|
||||
nbextensions, 'SYSTEM_JUPYTER_PATH', self.system_path
|
||||
)
|
||||
self.patch_system_path.start()
|
||||
self.addCleanup(self.patch_system_path.stop)
|
||||
|
||||
def assert_dir_exists(self, path):
|
||||
if not os.path.exists(path):
|
||||
do_exist = os.listdir(os.path.dirname(path))
|
||||
self.fail(f"{path} should exist (found {do_exist})")
|
||||
|
||||
def assert_not_dir_exists(self, path):
|
||||
if os.path.exists(path):
|
||||
self.fail(f"{path} should not exist")
|
||||
|
||||
def assert_installed(self, relative_path, user=False):
|
||||
if user:
|
||||
nbext = pjoin(self.data_dir, 'nbextensions')
|
||||
else:
|
||||
nbext = self.system_nbext
|
||||
self.assert_dir_exists(
|
||||
pjoin(nbext, relative_path)
|
||||
)
|
||||
|
||||
def assert_not_installed(self, relative_path, user=False):
|
||||
if user:
|
||||
nbext = pjoin(self.data_dir, 'nbextensions')
|
||||
else:
|
||||
nbext = self.system_nbext
|
||||
self.assert_not_dir_exists(
|
||||
pjoin(nbext, relative_path)
|
||||
)
|
||||
|
||||
def test_create_data_dir(self):
|
||||
"""install_nbextension when data_dir doesn't exist"""
|
||||
with TemporaryDirectory() as td:
|
||||
data_dir = os.path.join(td, self.data_dir)
|
||||
with patch.dict('os.environ', {
|
||||
'JUPYTER_DATA_DIR': data_dir,
|
||||
}):
|
||||
install_nbextension(self.src, user=True)
|
||||
self.assert_dir_exists(data_dir)
|
||||
for file_name in self.files:
|
||||
self.assert_installed(
|
||||
pjoin(basename(self.src), file_name),
|
||||
user=True,
|
||||
)
|
||||
|
||||
def test_create_nbextensions_user(self):
|
||||
with TemporaryDirectory() as td:
|
||||
install_nbextension(self.src, user=True)
|
||||
self.assert_installed(
|
||||
pjoin(basename(self.src), 'ƒile'),
|
||||
user=True
|
||||
)
|
||||
|
||||
def test_create_nbextensions_system(self):
|
||||
with TemporaryDirectory() as td:
|
||||
self.system_nbext = pjoin(td, 'nbextensions')
|
||||
with patch.object(nbextensions, 'SYSTEM_JUPYTER_PATH', [td]):
|
||||
install_nbextension(self.src, user=False)
|
||||
self.assert_installed(
|
||||
pjoin(basename(self.src), 'ƒile'),
|
||||
user=False
|
||||
)
|
||||
|
||||
def test_single_file(self):
|
||||
file_name = self.files[0]
|
||||
install_nbextension(pjoin(self.src, file_name))
|
||||
self.assert_installed(file_name)
|
||||
|
||||
def test_single_dir(self):
|
||||
d = '∂ir'
|
||||
install_nbextension(pjoin(self.src, d))
|
||||
self.assert_installed(self.files[-1])
|
||||
|
||||
def test_single_dir_trailing_slash(self):
|
||||
d = '∂ir/'
|
||||
install_nbextension(pjoin(self.src, d))
|
||||
self.assert_installed(self.files[-1])
|
||||
if os.name == 'nt':
|
||||
d = '∂ir\\'
|
||||
install_nbextension(pjoin(self.src, d))
|
||||
self.assert_installed(self.files[-1])
|
||||
|
||||
def test_destination_file(self):
|
||||
file_name = self.files[0]
|
||||
install_nbextension(pjoin(self.src, file_name), destination = 'ƒiledest')
|
||||
self.assert_installed('ƒiledest')
|
||||
|
||||
def test_destination_dir(self):
|
||||
d = '∂ir'
|
||||
install_nbextension(pjoin(self.src, d), destination = 'ƒiledest2')
|
||||
self.assert_installed(pjoin('ƒiledest2', '∂ir2', 'ƒile2'))
|
||||
|
||||
def test_install_nbextension(self):
|
||||
with self.assertRaises(TypeError):
|
||||
install_nbextension(glob.glob(pjoin(self.src, '*')))
|
||||
|
||||
def test_overwrite_file(self):
|
||||
with TemporaryDirectory() as d:
|
||||
fname = 'ƒ.js'
|
||||
src = pjoin(d, fname)
|
||||
with open(src, 'w') as f:
|
||||
f.write('first')
|
||||
mtime = touch(src)
|
||||
dest = pjoin(self.system_nbext, fname)
|
||||
install_nbextension(src)
|
||||
with open(src, 'w') as f:
|
||||
f.write('overwrite')
|
||||
mtime = touch(src, mtime - 100)
|
||||
install_nbextension(src, overwrite=True)
|
||||
with open(dest) as f:
|
||||
self.assertEqual(f.read(), 'overwrite')
|
||||
|
||||
def test_overwrite_dir(self):
|
||||
with TemporaryDirectory() as src:
|
||||
base = basename(src)
|
||||
fname = 'ƒ.js'
|
||||
touch(pjoin(src, fname))
|
||||
install_nbextension(src)
|
||||
self.assert_installed(pjoin(base, fname))
|
||||
os.remove(pjoin(src, fname))
|
||||
fname2 = '∂.js'
|
||||
touch(pjoin(src, fname2))
|
||||
install_nbextension(src, overwrite=True)
|
||||
self.assert_installed(pjoin(base, fname2))
|
||||
self.assert_not_installed(pjoin(base, fname))
|
||||
|
||||
def test_update_file(self):
|
||||
with TemporaryDirectory() as d:
|
||||
fname = 'ƒ.js'
|
||||
src = pjoin(d, fname)
|
||||
with open(src, 'w') as f:
|
||||
f.write('first')
|
||||
mtime = touch(src)
|
||||
install_nbextension(src)
|
||||
self.assert_installed(fname)
|
||||
dest = pjoin(self.system_nbext, fname)
|
||||
os.stat(dest).st_mtime
|
||||
with open(src, 'w') as f:
|
||||
f.write('overwrite')
|
||||
touch(src, mtime + 10)
|
||||
install_nbextension(src)
|
||||
with open(dest) as f:
|
||||
self.assertEqual(f.read(), 'overwrite')
|
||||
|
||||
def test_skip_old_file(self):
|
||||
with TemporaryDirectory() as d:
|
||||
fname = 'ƒ.js'
|
||||
src = pjoin(d, fname)
|
||||
mtime = touch(src)
|
||||
install_nbextension(src)
|
||||
self.assert_installed(fname)
|
||||
dest = pjoin(self.system_nbext, fname)
|
||||
old_mtime = os.stat(dest).st_mtime
|
||||
|
||||
mtime = touch(src, mtime - 100)
|
||||
install_nbextension(src)
|
||||
new_mtime = os.stat(dest).st_mtime
|
||||
self.assertEqual(new_mtime, old_mtime)
|
||||
|
||||
def test_quiet(self):
|
||||
stdout = StringIO()
|
||||
stderr = StringIO()
|
||||
with patch.object(sys, 'stdout', stdout), \
|
||||
patch.object(sys, 'stderr', stderr):
|
||||
install_nbextension(self.src)
|
||||
self.assertEqual(stdout.getvalue(), '')
|
||||
self.assertEqual(stderr.getvalue(), '')
|
||||
|
||||
def test_install_zip(self):
|
||||
path = pjoin(self.src, "myjsext.zip")
|
||||
with zipfile.ZipFile(path, 'w') as f:
|
||||
f.writestr("a.js", b"b();")
|
||||
f.writestr("foo/a.js", b"foo();")
|
||||
install_nbextension(path)
|
||||
self.assert_installed("a.js")
|
||||
self.assert_installed(pjoin("foo", "a.js"))
|
||||
|
||||
def test_install_tar(self):
|
||||
def _add_file(f, fname, buf):
|
||||
info = tarfile.TarInfo(fname)
|
||||
info.size = len(buf)
|
||||
f.addfile(info, BytesIO(buf))
|
||||
|
||||
for i,ext in enumerate((".tar.gz", ".tgz", ".tar.bz2")):
|
||||
path = pjoin(self.src, "myjsext" + ext)
|
||||
with tarfile.open(path, 'w') as f:
|
||||
_add_file(f, f"b{i}.js", b"b();")
|
||||
_add_file(f, f"foo/b{i}.js", b"foo();")
|
||||
install_nbextension(path)
|
||||
self.assert_installed(f"b{i}.js")
|
||||
self.assert_installed(pjoin("foo", f"b{i}.js"))
|
||||
|
||||
def test_install_url(self):
|
||||
def fake_urlretrieve(url, dest):
|
||||
touch(dest)
|
||||
save_urlretrieve = nbextensions.urlretrieve
|
||||
nbextensions.urlretrieve = fake_urlretrieve
|
||||
try:
|
||||
install_nbextension("http://example.com/path/to/foo.js")
|
||||
self.assert_installed("foo.js")
|
||||
install_nbextension("https://example.com/path/to/another/bar.js")
|
||||
self.assert_installed("bar.js")
|
||||
install_nbextension("https://example.com/path/to/another/bar.js",
|
||||
destination = 'foobar.js')
|
||||
self.assert_installed("foobar.js")
|
||||
finally:
|
||||
nbextensions.urlretrieve = save_urlretrieve
|
||||
|
||||
def test_check_nbextension(self):
|
||||
with TemporaryDirectory() as d:
|
||||
f = 'ƒ.js'
|
||||
src = pjoin(d, f)
|
||||
touch(src)
|
||||
install_nbextension(src, user=True)
|
||||
|
||||
assert check_nbextension(f, user=True)
|
||||
assert check_nbextension([f], user=True)
|
||||
assert not check_nbextension([f, pjoin('dne', f)], user=True)
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_install_symlink(self):
|
||||
with TemporaryDirectory() as d:
|
||||
f = 'ƒ.js'
|
||||
src = pjoin(d, f)
|
||||
touch(src)
|
||||
install_nbextension(src, symlink=True)
|
||||
dest = pjoin(self.system_nbext, f)
|
||||
assert os.path.islink(dest)
|
||||
link = os.readlink(dest)
|
||||
self.assertEqual(link, src)
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_overwrite_broken_symlink(self):
|
||||
with TemporaryDirectory() as d:
|
||||
f = 'ƒ.js'
|
||||
f2 = 'ƒ2.js'
|
||||
src = pjoin(d, f)
|
||||
src2 = pjoin(d, f2)
|
||||
touch(src)
|
||||
install_nbextension(src, symlink=True)
|
||||
os.rename(src, src2)
|
||||
install_nbextension(src2, symlink=True, overwrite=True, destination=f)
|
||||
dest = pjoin(self.system_nbext, f)
|
||||
assert os.path.islink(dest)
|
||||
link = os.readlink(dest)
|
||||
self.assertEqual(link, src2)
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_install_symlink_destination(self):
|
||||
with TemporaryDirectory() as d:
|
||||
f = 'ƒ.js'
|
||||
flink = 'ƒlink.js'
|
||||
src = pjoin(d, f)
|
||||
touch(src)
|
||||
install_nbextension(src, symlink=True, destination=flink)
|
||||
dest = pjoin(self.system_nbext, flink)
|
||||
assert os.path.islink(dest)
|
||||
link = os.readlink(dest)
|
||||
self.assertEqual(link, src)
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_install_symlink_bad(self):
|
||||
with self.assertRaises(ValueError):
|
||||
install_nbextension("http://example.com/foo.js", symlink=True)
|
||||
|
||||
with TemporaryDirectory() as d:
|
||||
zf = 'ƒ.zip'
|
||||
zsrc = pjoin(d, zf)
|
||||
with zipfile.ZipFile(zsrc, 'w') as z:
|
||||
z.writestr("a.js", b"b();")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
install_nbextension(zsrc, symlink=True)
|
||||
|
||||
def test_install_destination_bad(self):
|
||||
with TemporaryDirectory() as d:
|
||||
zf = 'ƒ.zip'
|
||||
zsrc = pjoin(d, zf)
|
||||
with zipfile.ZipFile(zsrc, 'w') as z:
|
||||
z.writestr("a.js", b"b();")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
install_nbextension(zsrc, destination='foo')
|
||||
|
||||
def test_nbextension_enable(self):
|
||||
with TemporaryDirectory() as d:
|
||||
f = 'ƒ.js'
|
||||
src = pjoin(d, f)
|
||||
touch(src)
|
||||
install_nbextension(src, user=True)
|
||||
enable_nbextension(section='notebook', require='ƒ')
|
||||
|
||||
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
|
||||
cm = BaseJSONConfigManager(config_dir=config_dir)
|
||||
enabled = cm.get('notebook').get('load_extensions', {}).get('ƒ', False)
|
||||
assert enabled
|
||||
|
||||
def test_nbextension_disable(self):
|
||||
self.test_nbextension_enable()
|
||||
disable_nbextension(section='notebook', require='ƒ')
|
||||
|
||||
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
|
||||
cm = BaseJSONConfigManager(config_dir=config_dir)
|
||||
enabled = cm.get('notebook').get('load_extensions', {}).get('ƒ', False)
|
||||
assert not enabled
|
||||
|
||||
|
||||
def _mock_extension_spec_meta(self, section='notebook'):
|
||||
return {
|
||||
'section': section,
|
||||
'src': 'mockextension',
|
||||
'dest': '_mockdestination',
|
||||
'require': '_mockdestination/index'
|
||||
}
|
||||
|
||||
def _inject_mock_extension(self, section='notebook'):
|
||||
outer_file = __file__
|
||||
|
||||
meta = self._mock_extension_spec_meta(section)
|
||||
|
||||
class mock():
|
||||
__file__ = outer_file
|
||||
|
||||
@staticmethod
|
||||
def _jupyter_nbextension_paths():
|
||||
return [meta]
|
||||
|
||||
import sys
|
||||
sys.modules['mockextension'] = mock
|
||||
|
||||
def test_nbextensionpy_files(self):
|
||||
self._inject_mock_extension()
|
||||
install_nbextension_python('mockextension')
|
||||
|
||||
assert check_nbextension('_mockdestination/index.js')
|
||||
assert check_nbextension(['_mockdestination/index.js'])
|
||||
|
||||
def test_nbextensionpy_user_files(self):
|
||||
self._inject_mock_extension()
|
||||
install_nbextension_python('mockextension', user=True)
|
||||
|
||||
assert check_nbextension('_mockdestination/index.js', user=True)
|
||||
assert check_nbextension(['_mockdestination/index.js'], user=True)
|
||||
|
||||
def test_nbextensionpy_uninstall_files(self):
|
||||
self._inject_mock_extension()
|
||||
install_nbextension_python('mockextension', user=True)
|
||||
uninstall_nbextension_python('mockextension', user=True)
|
||||
|
||||
assert not check_nbextension('_mockdestination/index.js')
|
||||
assert not check_nbextension(['_mockdestination/index.js'])
|
||||
|
||||
def test_nbextensionpy_enable(self):
|
||||
self._inject_mock_extension('notebook')
|
||||
install_nbextension_python('mockextension', user=True)
|
||||
enable_nbextension_python('mockextension')
|
||||
|
||||
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
|
||||
cm = BaseJSONConfigManager(config_dir=config_dir)
|
||||
enabled = cm.get('notebook').get('load_extensions', {}).get('_mockdestination/index', False)
|
||||
assert enabled
|
||||
|
||||
def test_nbextensionpy_disable(self):
|
||||
self._inject_mock_extension('notebook')
|
||||
install_nbextension_python('mockextension', user=True)
|
||||
enable_nbextension_python('mockextension')
|
||||
disable_nbextension_python('mockextension', user=True)
|
||||
|
||||
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
|
||||
cm = BaseJSONConfigManager(config_dir=config_dir)
|
||||
enabled = cm.get('notebook').get('load_extensions', {}).get('_mockdestination/index', False)
|
||||
assert not enabled
|
||||
|
||||
def test_nbextensionpy_validate(self):
|
||||
self._inject_mock_extension('notebook')
|
||||
|
||||
paths = install_nbextension_python('mockextension', user=True)
|
||||
enable_nbextension_python('mockextension')
|
||||
|
||||
meta = self._mock_extension_spec_meta()
|
||||
warnings = validate_nbextension_python(meta, paths[0])
|
||||
self.assertEqual([], warnings, warnings)
|
||||
|
||||
def test_nbextensionpy_validate_bad(self):
|
||||
# Break the metadata (correct file will still be copied)
|
||||
self._inject_mock_extension('notebook')
|
||||
|
||||
paths = install_nbextension_python('mockextension', user=True)
|
||||
|
||||
enable_nbextension_python('mockextension')
|
||||
|
||||
meta = self._mock_extension_spec_meta()
|
||||
meta.update(require="bad-require")
|
||||
|
||||
warnings = validate_nbextension_python(meta, paths[0])
|
||||
self.assertNotEqual([], warnings, warnings)
|
||||
|
||||
def test_nbextension_validate(self):
|
||||
# Break the metadata (correct file will still be copied)
|
||||
self._inject_mock_extension('notebook')
|
||||
|
||||
install_nbextension_python('mockextension', user=True)
|
||||
enable_nbextension_python('mockextension')
|
||||
|
||||
warnings = validate_nbextension("_mockdestination/index")
|
||||
self.assertEqual([], warnings, warnings)
|
||||
|
||||
def test_nbextension_validate_bad(self):
|
||||
warnings = validate_nbextension("this-doesn't-exist")
|
||||
self.assertNotEqual([], warnings, warnings)
|
||||
|
231
.venv/Lib/site-packages/notebook/tests/test_notebookapp.py
Normal file
231
.venv/Lib/site-packages/notebook/tests/test_notebookapp.py
Normal file
@ -0,0 +1,231 @@
|
||||
"""Test NotebookApp"""
|
||||
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from traitlets.tests.utils import check_help_all_output
|
||||
|
||||
from jupyter_core.application import NoStart
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
from traitlets import TraitError
|
||||
from notebook import notebookapp, __version__
|
||||
from notebook.auth.security import passwd_check
|
||||
NotebookApp = notebookapp.NotebookApp
|
||||
|
||||
from .launchnotebook import NotebookTestBase, UNIXSocketNotebookTestBase
|
||||
|
||||
|
||||
def test_help_output():
|
||||
"""ipython notebook --help-all works"""
|
||||
check_help_all_output('notebook')
|
||||
|
||||
def test_server_info_file():
|
||||
td = TemporaryDirectory()
|
||||
nbapp = NotebookApp(runtime_dir=td.name, log=logging.getLogger())
|
||||
def get_servers():
|
||||
return list(notebookapp.list_running_servers(nbapp.runtime_dir))
|
||||
nbapp.initialize(argv=[])
|
||||
nbapp.write_server_info_file()
|
||||
servers = get_servers()
|
||||
assert len(servers) == 1
|
||||
assert servers[0]['port'] == nbapp.port
|
||||
assert servers[0]['url'] == nbapp.connection_url
|
||||
nbapp.remove_server_info_file()
|
||||
assert get_servers() == []
|
||||
|
||||
# The ENOENT error should be silenced.
|
||||
nbapp.remove_server_info_file()
|
||||
|
||||
def test_nb_dir():
|
||||
with TemporaryDirectory() as td:
|
||||
app = NotebookApp(notebook_dir=td)
|
||||
assert app.notebook_dir == td
|
||||
|
||||
def test_no_create_nb_dir():
|
||||
with TemporaryDirectory() as td:
|
||||
nbdir = os.path.join(td, 'notebooks')
|
||||
app = NotebookApp()
|
||||
with pytest.raises(TraitError):
|
||||
app.notebook_dir = nbdir
|
||||
|
||||
def test_missing_nb_dir():
|
||||
with TemporaryDirectory() as td:
|
||||
nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
|
||||
app = NotebookApp()
|
||||
with pytest.raises(TraitError):
|
||||
app.notebook_dir = nbdir
|
||||
|
||||
def test_invalid_nb_dir():
|
||||
with NamedTemporaryFile() as tf:
|
||||
app = NotebookApp()
|
||||
with pytest.raises(TraitError):
|
||||
app.notebook_dir = tf
|
||||
|
||||
def test_nb_dir_with_slash():
|
||||
with TemporaryDirectory(suffix="_slash" + os.sep) as td:
|
||||
app = NotebookApp(notebook_dir=td)
|
||||
assert not app.notebook_dir.endswith(os.sep)
|
||||
|
||||
def test_nb_dir_root():
|
||||
root = os.path.abspath(os.sep) # gets the right value on Windows, Posix
|
||||
app = NotebookApp(notebook_dir=root)
|
||||
assert app.notebook_dir == root
|
||||
|
||||
def test_generate_config():
|
||||
with TemporaryDirectory() as td:
|
||||
app = NotebookApp(config_dir=td)
|
||||
app.initialize(['--generate-config', '--allow-root'])
|
||||
with pytest.raises(NoStart):
|
||||
app.start()
|
||||
assert os.path.exists(os.path.join(td, 'jupyter_notebook_config.py'))
|
||||
|
||||
#test if the version testin function works
|
||||
@pytest.mark.parametrize(
|
||||
'version', [
|
||||
'4.1.0.b1',
|
||||
'4.1.b1',
|
||||
'4.2',
|
||||
'X.y.z',
|
||||
'1.2.3.dev1.post2',
|
||||
]
|
||||
)
|
||||
def test_pep440_bad_version(version):
|
||||
with pytest.raises(ValueError):
|
||||
raise_on_bad_version(version)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'version', [
|
||||
'4.1.1',
|
||||
'4.2.1b3',
|
||||
]
|
||||
)
|
||||
def test_pep440_good_version(version):
|
||||
raise_on_bad_version(version)
|
||||
|
||||
pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
|
||||
|
||||
def raise_on_bad_version(version):
|
||||
if not pep440re.match(version):
|
||||
raise ValueError("Versions String does apparently not match Pep 440 specification, "
|
||||
"which might lead to sdist and wheel being seen as 2 different release. "
|
||||
"E.g: do not use dots for beta/alpha/rc markers.")
|
||||
|
||||
def test_current_version():
|
||||
raise_on_bad_version(__version__)
|
||||
|
||||
def test_notebook_password():
|
||||
password = 'secret'
|
||||
with TemporaryDirectory() as td:
|
||||
with patch.dict('os.environ', {
|
||||
'JUPYTER_CONFIG_DIR': td,
|
||||
}), patch.object(getpass, 'getpass', return_value=password):
|
||||
app = notebookapp.NotebookPasswordApp(log_level=logging.ERROR)
|
||||
app.initialize([])
|
||||
app.start()
|
||||
nb = NotebookApp()
|
||||
nb.load_config_file()
|
||||
assert nb.password != ''
|
||||
passwd_check(nb.password, password)
|
||||
|
||||
class StopAppTest(notebookapp.NbserverStopApp):
|
||||
"""For testing the logic of NbserverStopApp."""
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.servers_shut_down = []
|
||||
|
||||
def shutdown_server(self, server):
|
||||
self.servers_shut_down.append(server)
|
||||
return True
|
||||
|
||||
def test_notebook_stop():
|
||||
def list_running_servers(runtime_dir):
|
||||
for port in range(100, 110):
|
||||
yield {
|
||||
'pid': 1000 + port,
|
||||
'port': port,
|
||||
'base_url': '/',
|
||||
'hostname': 'localhost',
|
||||
'notebook_dir': '/',
|
||||
'secure': False,
|
||||
'token': '',
|
||||
'password': False,
|
||||
'url': f'http://localhost:{port}',
|
||||
}
|
||||
|
||||
mock_servers = patch('notebook.notebookapp.list_running_servers', list_running_servers)
|
||||
|
||||
# test stop with a match
|
||||
with mock_servers:
|
||||
app = StopAppTest()
|
||||
app.initialize(['105'])
|
||||
app.start()
|
||||
assert len(app.servers_shut_down) == 1
|
||||
assert app.servers_shut_down[0]['port'] == 105
|
||||
|
||||
# test no match
|
||||
with mock_servers, patch('os.kill') as os_kill:
|
||||
app = StopAppTest()
|
||||
app.initialize(['999'])
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
app.start()
|
||||
assert exc.value.code == 1
|
||||
assert len(app.servers_shut_down) == 0
|
||||
|
||||
|
||||
class NotebookAppTests(NotebookTestBase):
|
||||
def test_list_running_servers(self):
|
||||
servers = list(notebookapp.list_running_servers())
|
||||
assert len(servers) >= 1
|
||||
assert self.port in {info['port'] for info in servers}
|
||||
|
||||
def test_log_json_default(self):
|
||||
self.assertFalse(self.notebook.log_json)
|
||||
|
||||
def test_validate_log_json(self):
|
||||
self.assertFalse(self.notebook._validate_log_json(dict(value=False)))
|
||||
|
||||
|
||||
# UNIX sockets aren't available on Windows.
|
||||
if not sys.platform.startswith('win'):
|
||||
class NotebookUnixSocketTests(UNIXSocketNotebookTestBase):
|
||||
def test_run(self):
|
||||
self.fetch_url(self.base_url() + 'api/contents')
|
||||
|
||||
def test_list_running_sock_servers(self):
|
||||
servers = list(notebookapp.list_running_servers())
|
||||
assert len(servers) >= 1
|
||||
assert self.sock in {info['sock'] for info in servers}
|
||||
|
||||
|
||||
class NotebookAppJSONLoggingTests(NotebookTestBase):
|
||||
"""Tests for when json logging is enabled."""
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
super().setup_class()
|
||||
try:
|
||||
import json_logging
|
||||
cls.json_logging_available = True
|
||||
except ImportError:
|
||||
cls.json_logging_available = False
|
||||
|
||||
@classmethod
|
||||
def get_patch_env(cls):
|
||||
test_env = super().get_patch_env()
|
||||
test_env.update({'JUPYTER_ENABLE_JSON_LOGGING': 'true'})
|
||||
return test_env
|
||||
|
||||
def test_log_json_enabled(self):
|
||||
self.assertTrue(self.notebook._default_log_json())
|
||||
|
||||
def test_validate_log_json(self):
|
||||
self.assertEqual(
|
||||
self.notebook._validate_log_json(dict(value=True)),
|
||||
self.json_logging_available)
|
@ -0,0 +1,170 @@
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from notebook import DEFAULT_NOTEBOOK_PORT
|
||||
|
||||
from .launchnotebook import UNIXSocketNotebookTestBase
|
||||
from ..utils import urlencode_unix_socket, urlencode_unix_socket_path
|
||||
|
||||
|
||||
pytestmark = pytest.mark.integration_tests
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_shutdown_sock_server_integration():
|
||||
sock = UNIXSocketNotebookTestBase.sock
|
||||
url = urlencode_unix_socket(sock).encode()
|
||||
encoded_sock_path = urlencode_unix_socket_path(sock)
|
||||
|
||||
p = subprocess.Popen(
|
||||
['jupyter-notebook', f'--sock={sock}', '--sock-mode=0700'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
complete = False
|
||||
for line in iter(p.stderr.readline, b''):
|
||||
print(line.decode())
|
||||
if url in line:
|
||||
complete = True
|
||||
break
|
||||
|
||||
assert complete, 'did not find socket URL in stdout when launching notebook'
|
||||
|
||||
assert encoded_sock_path.encode() in subprocess.check_output(['jupyter-notebook', 'list'])
|
||||
|
||||
# Ensure umask is properly applied.
|
||||
assert stat.S_IMODE(os.lstat(sock).st_mode) == 0o700
|
||||
|
||||
try:
|
||||
subprocess.check_output(['jupyter-notebook', 'stop'], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert 'There is currently no server running on' in e.output.decode()
|
||||
else:
|
||||
raise AssertionError('expected stop command to fail due to target mis-match')
|
||||
|
||||
assert encoded_sock_path.encode() in subprocess.check_output(['jupyter-notebook', 'list'])
|
||||
|
||||
subprocess.check_output(['jupyter-notebook', 'stop', sock])
|
||||
|
||||
assert encoded_sock_path.encode() not in subprocess.check_output(['jupyter-notebook', 'list'])
|
||||
|
||||
p.wait()
|
||||
|
||||
|
||||
def test_sock_server_validate_sockmode_type():
|
||||
try:
|
||||
subprocess.check_output(
|
||||
['jupyter-notebook', '--sock=/tmp/nonexistent', '--sock-mode=badbadbad'],
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert 'badbadbad' in e.output.decode()
|
||||
else:
|
||||
raise AssertionError('expected execution to fail due to validation of --sock-mode param')
|
||||
|
||||
|
||||
def test_sock_server_validate_sockmode_accessible():
|
||||
try:
|
||||
subprocess.check_output(
|
||||
['jupyter-notebook', '--sock=/tmp/nonexistent', '--sock-mode=0444'],
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert '0444' in e.output.decode()
|
||||
else:
|
||||
raise AssertionError('expected execution to fail due to validation of --sock-mode param')
|
||||
|
||||
|
||||
def _ensure_stopped(check_msg='There are no running servers'):
|
||||
try:
|
||||
subprocess.check_output(
|
||||
['jupyter-notebook', 'stop'],
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert check_msg in e.output.decode()
|
||||
else:
|
||||
raise AssertionError('expected all servers to be stopped')
|
||||
|
||||
|
||||
@pytest.mark.skipif(not bool(os.environ.get('RUN_NB_INTEGRATION_TESTS', False)), reason="for local testing")
|
||||
def test_stop_multi_integration():
|
||||
"""Tests lifecycle behavior for mixed-mode server types w/ default ports.
|
||||
|
||||
Mostly suitable for local dev testing due to reliance on default port binding.
|
||||
"""
|
||||
TEST_PORT = '9797'
|
||||
MSG_TMPL = 'Shutting down server on {}...'
|
||||
|
||||
_ensure_stopped()
|
||||
|
||||
# Default port.
|
||||
p1 = subprocess.Popen(
|
||||
['jupyter-notebook', '--no-browser']
|
||||
)
|
||||
|
||||
# Unix socket.
|
||||
sock = UNIXSocketNotebookTestBase.sock
|
||||
p2 = subprocess.Popen(
|
||||
['jupyter-notebook', f'--sock={sock}']
|
||||
)
|
||||
|
||||
# Specified port
|
||||
p3 = subprocess.Popen(
|
||||
['jupyter-notebook', '--no-browser', f'--port={TEST_PORT}']
|
||||
)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
assert MSG_TMPL.format(DEFAULT_NOTEBOOK_PORT) in subprocess.check_output(
|
||||
['jupyter-notebook', 'stop']
|
||||
).decode()
|
||||
|
||||
_ensure_stopped('There is currently no server running on 8888')
|
||||
|
||||
assert MSG_TMPL.format(sock) in subprocess.check_output(
|
||||
['jupyter-notebook', 'stop', sock]
|
||||
).decode()
|
||||
|
||||
assert MSG_TMPL.format(TEST_PORT) in subprocess.check_output(
|
||||
['jupyter-notebook', 'stop', TEST_PORT]
|
||||
).decode()
|
||||
|
||||
_ensure_stopped()
|
||||
|
||||
p1.wait()
|
||||
p2.wait()
|
||||
p3.wait()
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
|
||||
def test_launch_socket_collision():
|
||||
"""Tests UNIX socket in-use detection for lifecycle correctness."""
|
||||
sock = UNIXSocketNotebookTestBase.sock
|
||||
check_msg = f'socket {sock} is already in use'
|
||||
|
||||
_ensure_stopped()
|
||||
|
||||
# Start a server.
|
||||
cmd = ['jupyter-notebook', f'--sock={sock}']
|
||||
p1 = subprocess.Popen(cmd)
|
||||
time.sleep(3)
|
||||
|
||||
# Try to start a server bound to the same UNIX socket.
|
||||
try:
|
||||
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert check_msg in e.output.decode()
|
||||
else:
|
||||
raise AssertionError(f'expected error, instead got {e.output.decode()}')
|
||||
|
||||
# Stop the background server, ensure it's stopped and wait on the process to exit.
|
||||
subprocess.check_call(['jupyter-notebook', 'stop', sock])
|
||||
|
||||
_ensure_stopped()
|
||||
|
||||
p1.wait()
|
48
.venv/Lib/site-packages/notebook/tests/test_paths.py
Normal file
48
.venv/Lib/site-packages/notebook/tests/test_paths.py
Normal file
@ -0,0 +1,48 @@
|
||||
import re
|
||||
|
||||
from notebook.base.handlers import path_regex
|
||||
from notebook.utils import url_path_join
|
||||
from .launchnotebook import NotebookTestBase
|
||||
|
||||
# build regexps that tornado uses:
|
||||
path_pat = re.compile(f'^/x{path_regex}$')
|
||||
|
||||
|
||||
def test_path_regex():
|
||||
for path in (
|
||||
'/x',
|
||||
'/x/',
|
||||
'/x/foo',
|
||||
'/x/foo.ipynb',
|
||||
'/x/foo/bar',
|
||||
'/x/foo/bar.txt',
|
||||
):
|
||||
assert re.match(path_pat, path)
|
||||
|
||||
def test_path_regex_bad():
|
||||
for path in (
|
||||
'/xfoo',
|
||||
'/xfoo/',
|
||||
'/xfoo/bar',
|
||||
'/xfoo/bar/',
|
||||
'/x/foo/bar/',
|
||||
'/x//foo',
|
||||
'/y',
|
||||
'/y/x/foo',
|
||||
):
|
||||
assert not re.match(path_pat, path)
|
||||
|
||||
|
||||
class RedirectTestCase(NotebookTestBase):
|
||||
def test_trailing_slash(self):
|
||||
for uri, expected in (
|
||||
("/notebooks/mynotebook/", "/notebooks/mynotebook"),
|
||||
("////foo///", "/foo"),
|
||||
("//example.com/", "/example.com"),
|
||||
("/has/param/?hasparam=true", "/has/param?hasparam=true"),
|
||||
):
|
||||
r = self.request("GET", uri, allow_redirects=False)
|
||||
print(uri, expected)
|
||||
assert r.status_code == 302
|
||||
assert "Location" in r.headers
|
||||
assert r.headers["Location"] == url_path_join(self.url_prefix, expected)
|
24
.venv/Lib/site-packages/notebook/tests/test_serialize.py
Normal file
24
.venv/Lib/site-packages/notebook/tests/test_serialize.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""Test serialize/deserialize messages with buffers"""
|
||||
|
||||
import os
|
||||
|
||||
from jupyter_client.session import Session
|
||||
from ..base.zmqhandlers import (
|
||||
serialize_binary_message,
|
||||
deserialize_binary_message,
|
||||
)
|
||||
|
||||
def test_serialize_binary():
|
||||
s = Session()
|
||||
msg = s.msg('data_pub', content={'a': 'b'})
|
||||
msg['buffers'] = [ memoryview(os.urandom(3)) for i in range(3) ]
|
||||
bmsg = serialize_binary_message(msg)
|
||||
assert isinstance(bmsg, bytes)
|
||||
|
||||
def test_deserialize_binary():
|
||||
s = Session()
|
||||
msg = s.msg('data_pub', content={'a': 'b'})
|
||||
msg['buffers'] = [ memoryview(os.urandom(2)) for i in range(3) ]
|
||||
bmsg = serialize_binary_message(msg)
|
||||
msg2 = deserialize_binary_message(bmsg)
|
||||
assert msg2 == msg
|
196
.venv/Lib/site-packages/notebook/tests/test_serverextensions.py
Normal file
196
.venv/Lib/site-packages/notebook/tests/test_serverextensions.py
Normal file
@ -0,0 +1,196 @@
|
||||
import os
|
||||
import site
|
||||
import sys
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
from ipython_genutils import py3compat
|
||||
|
||||
from notebook.config_manager import BaseJSONConfigManager
|
||||
from traitlets.tests.utils import check_help_all_output
|
||||
from jupyter_core import paths
|
||||
|
||||
from notebook.serverextensions import toggle_serverextension_python
|
||||
from notebook import nbextensions, extensions
|
||||
from notebook.notebookapp import NotebookApp
|
||||
from notebook.nbextensions import _get_config_dir
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
def test_help_output():
|
||||
check_help_all_output('notebook.serverextensions')
|
||||
check_help_all_output('notebook.serverextensions', ['enable'])
|
||||
check_help_all_output('notebook.serverextensions', ['disable'])
|
||||
check_help_all_output('notebook.serverextensions', ['install'])
|
||||
check_help_all_output('notebook.serverextensions', ['uninstall'])
|
||||
|
||||
outer_file = __file__
|
||||
|
||||
class MockExtensionModule:
|
||||
__file__ = outer_file
|
||||
|
||||
@staticmethod
|
||||
def _jupyter_server_extension_paths():
|
||||
return [{
|
||||
'module': '_mockdestination/index'
|
||||
}]
|
||||
|
||||
loaded = False
|
||||
|
||||
def load_jupyter_server_extension(self, app):
|
||||
self.loaded = True
|
||||
|
||||
|
||||
class MockEnvTestCase(TestCase):
|
||||
|
||||
def tempdir(self):
|
||||
td = TemporaryDirectory()
|
||||
self.tempdirs.append(td)
|
||||
return py3compat.cast_unicode(td.name)
|
||||
|
||||
def setUp(self):
|
||||
self.tempdirs = []
|
||||
self._mock_extensions = []
|
||||
|
||||
self.test_dir = self.tempdir()
|
||||
self.data_dir = os.path.join(self.test_dir, 'data')
|
||||
self.config_dir = os.path.join(self.test_dir, 'config')
|
||||
self.system_data_dir = os.path.join(self.test_dir, 'system_data')
|
||||
self.system_config_dir = os.path.join(self.test_dir, 'system_config')
|
||||
self.system_path = [self.system_data_dir]
|
||||
self.system_config_path = [self.system_config_dir]
|
||||
|
||||
self.patches = []
|
||||
p = patch.dict('os.environ', {
|
||||
'JUPYTER_CONFIG_DIR': self.config_dir,
|
||||
'JUPYTER_DATA_DIR': self.data_dir,
|
||||
})
|
||||
self.patches.append(p)
|
||||
for mod in (paths, nbextensions):
|
||||
p = patch.object(mod,
|
||||
'SYSTEM_JUPYTER_PATH', self.system_path)
|
||||
self.patches.append(p)
|
||||
p = patch.object(mod,
|
||||
'ENV_JUPYTER_PATH', [])
|
||||
self.patches.append(p)
|
||||
for mod in (paths, extensions):
|
||||
p = patch.object(mod,
|
||||
'SYSTEM_CONFIG_PATH', self.system_config_path)
|
||||
self.patches.append(p)
|
||||
p = patch.object(mod,
|
||||
'ENV_CONFIG_PATH', [])
|
||||
self.patches.append(p)
|
||||
# avoid adding the user site to the config paths with jupyter-core >= 4.9
|
||||
# https://github.com/jupyter/jupyter_core/pull/242
|
||||
p = patch.object(site,
|
||||
'ENABLE_USER_SITE', False)
|
||||
self.patches.append(p)
|
||||
for p in self.patches:
|
||||
p.start()
|
||||
self.addCleanup(p.stop)
|
||||
# verify our patches
|
||||
self.assertEqual(paths.jupyter_config_path(), [self.config_dir] + self.system_config_path)
|
||||
self.assertEqual(extensions._get_config_dir(user=False), self.system_config_dir)
|
||||
self.assertEqual(paths.jupyter_path(), [self.data_dir] + self.system_path)
|
||||
|
||||
def tearDown(self):
|
||||
for modulename in self._mock_extensions:
|
||||
sys.modules.pop(modulename)
|
||||
|
||||
def _inject_mock_extension(self, modulename='mockextension'):
|
||||
|
||||
sys.modules[modulename] = ext = MockExtensionModule()
|
||||
self._mock_extensions.append(modulename)
|
||||
return ext
|
||||
|
||||
class TestInstallServerExtension(MockEnvTestCase):
|
||||
|
||||
def _get_config(self, user=True):
|
||||
cm = BaseJSONConfigManager(config_dir=_get_config_dir(user))
|
||||
data = cm.get("jupyter_notebook_config")
|
||||
return data.get("NotebookApp", {}).get("nbserver_extensions", {})
|
||||
|
||||
def test_enable(self):
|
||||
self._inject_mock_extension()
|
||||
toggle_serverextension_python('mockextension', True)
|
||||
|
||||
config = self._get_config()
|
||||
assert config['mockextension']
|
||||
|
||||
def test_disable(self):
|
||||
self._inject_mock_extension()
|
||||
toggle_serverextension_python('mockextension', True)
|
||||
toggle_serverextension_python('mockextension', False)
|
||||
|
||||
config = self._get_config()
|
||||
assert not config['mockextension']
|
||||
|
||||
def test_merge_config(self):
|
||||
# enabled at sys level
|
||||
mock_sys = self._inject_mock_extension('mockext_sys')
|
||||
# enabled at sys, disabled at user
|
||||
mock_both = self._inject_mock_extension('mockext_both')
|
||||
# enabled at user
|
||||
mock_user = self._inject_mock_extension('mockext_user')
|
||||
# enabled at Python
|
||||
mock_py = self._inject_mock_extension('mockext_py')
|
||||
|
||||
toggle_serverextension_python('mockext_sys', enabled=True, user=False)
|
||||
toggle_serverextension_python('mockext_user', enabled=True, user=True)
|
||||
toggle_serverextension_python('mockext_both', enabled=True, user=False)
|
||||
toggle_serverextension_python('mockext_both', enabled=False, user=True)
|
||||
|
||||
app = NotebookApp(nbserver_extensions={'mockext_py': True})
|
||||
app.init_server_extension_config()
|
||||
app.init_server_extensions()
|
||||
|
||||
assert mock_user.loaded
|
||||
assert mock_sys.loaded
|
||||
assert mock_py.loaded
|
||||
assert not mock_both.loaded
|
||||
|
||||
|
||||
class TestOrderedServerExtension(MockEnvTestCase):
|
||||
"""
|
||||
Test that Server Extensions are loaded _in order_
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
mockextension1 = SimpleNamespace()
|
||||
mockextension2 = SimpleNamespace()
|
||||
|
||||
def load_jupyter_server_extension(obj):
|
||||
obj.mockI = True
|
||||
obj.mock_shared = 'I'
|
||||
|
||||
mockextension1.load_jupyter_server_extension = load_jupyter_server_extension
|
||||
|
||||
def load_jupyter_server_extension(obj):
|
||||
obj.mockII = True
|
||||
obj.mock_shared = 'II'
|
||||
|
||||
mockextension2.load_jupyter_server_extension = load_jupyter_server_extension
|
||||
|
||||
sys.modules['mockextension2'] = mockextension2
|
||||
sys.modules['mockextension1'] = mockextension1
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
del sys.modules['mockextension2']
|
||||
del sys.modules['mockextension1']
|
||||
|
||||
|
||||
def test_load_ordered(self):
|
||||
app = NotebookApp()
|
||||
app.nbserver_extensions = OrderedDict([('mockextension2',True),('mockextension1',True)])
|
||||
|
||||
app.init_server_extensions()
|
||||
|
||||
assert app.mockII is True, "Mock II should have been loaded"
|
||||
assert app.mockI is True, "Mock I should have been loaded"
|
||||
assert app.mock_shared == 'II', "Mock II should be loaded after Mock I"
|
80
.venv/Lib/site-packages/notebook/tests/test_traittypes.py
Normal file
80
.venv/Lib/site-packages/notebook/tests/test_traittypes.py
Normal file
@ -0,0 +1,80 @@
|
||||
import pytest
|
||||
from traitlets import HasTraits, TraitError
|
||||
from traitlets.utils.importstring import import_item
|
||||
|
||||
from notebook.traittypes import (
|
||||
InstanceFromClasses,
|
||||
TypeFromClasses
|
||||
)
|
||||
from notebook.services.contents.largefilemanager import LargeFileManager
|
||||
|
||||
|
||||
class DummyClass:
|
||||
"""Dummy class for testing Instance"""
|
||||
|
||||
|
||||
class DummyInt(int):
|
||||
"""Dummy class for testing types."""
|
||||
|
||||
|
||||
class Thing(HasTraits):
|
||||
|
||||
a = InstanceFromClasses(
|
||||
default_value=2,
|
||||
klasses=[
|
||||
int,
|
||||
str,
|
||||
DummyClass,
|
||||
]
|
||||
)
|
||||
|
||||
b = TypeFromClasses(
|
||||
default_value=None,
|
||||
allow_none=True,
|
||||
klasses=[
|
||||
DummyClass,
|
||||
int,
|
||||
'notebook.services.contents.manager.ContentsManager'
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class TestInstanceFromClasses:
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value',
|
||||
[1, 'test', DummyClass()]
|
||||
)
|
||||
def test_good_values(self, value):
|
||||
thing = Thing(a=value)
|
||||
assert thing.a == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value',
|
||||
[2.4, object()]
|
||||
)
|
||||
def test_bad_values(self, value):
|
||||
with pytest.raises(TraitError) as e:
|
||||
thing = Thing(a=value)
|
||||
|
||||
|
||||
class TestTypeFromClasses:
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value',
|
||||
[DummyClass, DummyInt, LargeFileManager,
|
||||
'notebook.services.contents.manager.ContentsManager']
|
||||
)
|
||||
def test_good_values(self, value):
|
||||
thing = Thing(b=value)
|
||||
if isinstance(value, str):
|
||||
value = import_item(value)
|
||||
assert thing.b == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'value',
|
||||
[float, object]
|
||||
)
|
||||
def test_bad_values(self, value):
|
||||
with pytest.raises(TraitError) as e:
|
||||
thing = Thing(b=value)
|
94
.venv/Lib/site-packages/notebook/tests/test_utils.py
Normal file
94
.venv/Lib/site-packages/notebook/tests/test_utils.py
Normal file
@ -0,0 +1,94 @@
|
||||
"""Test HTML utils"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from traitlets.tests.utils import check_help_all_output
|
||||
from notebook.utils import url_escape, url_unescape, is_hidden, is_file_hidden
|
||||
from ipython_genutils.py3compat import cast_unicode
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
|
||||
|
||||
def test_help_output():
|
||||
"""jupyter notebook --help-all works"""
|
||||
# FIXME: will be notebook
|
||||
check_help_all_output('notebook')
|
||||
|
||||
|
||||
def test_url_escape():
|
||||
|
||||
# changes path or notebook name with special characters to url encoding
|
||||
# these tests specifically encode paths with spaces
|
||||
path = url_escape('/this is a test/for spaces/')
|
||||
assert path == '/this%20is%20a%20test/for%20spaces/'
|
||||
|
||||
path = url_escape('notebook with space.ipynb')
|
||||
assert path == 'notebook%20with%20space.ipynb'
|
||||
|
||||
path = url_escape('/path with a/notebook and space.ipynb')
|
||||
assert path == '/path%20with%20a/notebook%20and%20space.ipynb'
|
||||
|
||||
path = url_escape('/ !@$#%^&* / test %^ notebook @#$ name.ipynb')
|
||||
assert path == '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb'
|
||||
|
||||
def test_url_unescape():
|
||||
|
||||
# decodes a url string to a plain string
|
||||
# these tests decode paths with spaces
|
||||
path = url_unescape('/this%20is%20a%20test/for%20spaces/')
|
||||
assert path == '/this is a test/for spaces/'
|
||||
|
||||
path = url_unescape('notebook%20with%20space.ipynb')
|
||||
assert path == 'notebook with space.ipynb'
|
||||
|
||||
path = url_unescape('/path%20with%20a/notebook%20and%20space.ipynb')
|
||||
assert path == '/path with a/notebook and space.ipynb'
|
||||
|
||||
path = url_unescape(
|
||||
'/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb')
|
||||
assert path == '/ !@$#%^&* / test %^ notebook @#$ name.ipynb'
|
||||
|
||||
def test_is_hidden():
|
||||
with TemporaryDirectory() as root:
|
||||
subdir1 = os.path.join(root, 'subdir')
|
||||
os.makedirs(subdir1)
|
||||
assert is_hidden(subdir1, root) == False
|
||||
assert is_file_hidden(subdir1) == False
|
||||
|
||||
subdir2 = os.path.join(root, '.subdir2')
|
||||
os.makedirs(subdir2)
|
||||
assert is_hidden(subdir2, root) == True
|
||||
assert is_file_hidden(subdir2) == True
|
||||
# root dir is always visible
|
||||
assert is_hidden(subdir2, subdir2) == False
|
||||
|
||||
subdir34 = os.path.join(root, 'subdir3', '.subdir4')
|
||||
os.makedirs(subdir34)
|
||||
assert is_hidden(subdir34, root) == True
|
||||
assert is_hidden(subdir34) == True
|
||||
|
||||
subdir56 = os.path.join(root, '.subdir5', 'subdir6')
|
||||
os.makedirs(subdir56)
|
||||
assert is_hidden(subdir56, root) == True
|
||||
assert is_hidden(subdir56) == True
|
||||
assert is_file_hidden(subdir56) == False
|
||||
assert is_file_hidden(subdir56, os.stat(subdir56)) == False
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "win32", reason="run on windows only")
|
||||
def test_is_hidden_win32():
|
||||
with TemporaryDirectory() as root:
|
||||
root = cast_unicode(root)
|
||||
subdir1 = os.path.join(root, 'subdir')
|
||||
os.makedirs(subdir1)
|
||||
assert not is_hidden(subdir1, root)
|
||||
r = ctypes.windll.kernel32.SetFileAttributesW(subdir1, 0x02)
|
||||
print(r)
|
||||
assert is_hidden(subdir1, root)
|
||||
assert is_file_hidden(subdir1)
|
||||
|
889
.venv/Lib/site-packages/notebook/tests/util.js
Normal file
889
.venv/Lib/site-packages/notebook/tests/util.js
Normal file
@ -0,0 +1,889 @@
|
||||
//
|
||||
// Utility functions for the HTML notebook's CasperJS tests.
|
||||
//
|
||||
casper.get_notebook_server = function () {
|
||||
// Get the URL of a notebook server on which to run tests.
|
||||
var port = casper.cli.get("port");
|
||||
port = (typeof port === 'undefined') ? '8888' : port;
|
||||
return casper.cli.get("url") || ('http://127.0.0.1:' + port);
|
||||
};
|
||||
|
||||
// casper.thenClick doesn't seem to trigger click events properly
|
||||
casper.thenClick = function (selector) {
|
||||
return this.thenEvaluate(function(selector) {
|
||||
var el = $(selector);
|
||||
if (el.length === 0) {
|
||||
console.error("Missing element!", selector)
|
||||
}
|
||||
el.click();
|
||||
}, {selector: selector})
|
||||
}
|
||||
|
||||
casper.open_new_notebook = function () {
|
||||
// Create and open a new notebook.
|
||||
var baseUrl = this.get_notebook_server();
|
||||
this.start(baseUrl);
|
||||
this.waitFor(this.page_loaded);
|
||||
this.waitForSelector('#kernel-python2 a, #kernel-python3 a');
|
||||
this.thenClick('#kernel-python2 a, #kernel-python3 a');
|
||||
|
||||
this.waitForPopup('');
|
||||
|
||||
this.withPopup('', function () {this.waitForSelector('.CodeMirror-code');});
|
||||
this.then(function () {
|
||||
this.open(this.popups[0].url);
|
||||
});
|
||||
this.waitFor(this.page_loaded);
|
||||
|
||||
// Hook the log and error methods of the console, forcing them to
|
||||
// serialize their arguments before printing. This allows the
|
||||
// Objects to cross into the phantom/slimer regime for display.
|
||||
this.thenEvaluate(function(){
|
||||
var serialize_arguments = function(f, context) {
|
||||
return function() {
|
||||
var pretty_arguments = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var value = arguments[i];
|
||||
if (value instanceof Object) {
|
||||
var name = value.name || 'Object';
|
||||
// Print a JSON string representation of the object.
|
||||
// If we don't do this, [Object object] gets printed
|
||||
// by casper, which is useless. The long regular
|
||||
// expression reduces the verbosity of the JSON.
|
||||
pretty_arguments.push(name + ' {' + JSON.stringify(value, null, ' ')
|
||||
.replace(/(\s+)?({)?(\s+)?(}(\s+)?,?)?(\s+)?(\s+)?\n/g, '\n')
|
||||
.replace(/\n(\s+)?\n/g, '\n'));
|
||||
} else {
|
||||
pretty_arguments.push(value);
|
||||
}
|
||||
}
|
||||
f.apply(context, pretty_arguments);
|
||||
};
|
||||
};
|
||||
console.log = serialize_arguments(console.log, console);
|
||||
console.error = serialize_arguments(console.error, console);
|
||||
});
|
||||
|
||||
// Make sure the kernel has started
|
||||
this.waitFor(this.kernel_running);
|
||||
// track the IPython busy/idle state
|
||||
this.thenEvaluate(function () {
|
||||
require(['base/js/namespace', 'base/js/events'], function (IPython, events) {
|
||||
|
||||
events.on('kernel_idle.Kernel',function () {
|
||||
IPython._status = 'idle';
|
||||
});
|
||||
events.on('kernel_busy.Kernel',function () {
|
||||
IPython._status = 'busy';
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
casper.page_loaded = function() {
|
||||
// Return whether or not the page has been loaded.
|
||||
return this.evaluate(function() {
|
||||
return typeof IPython !== "undefined" &&
|
||||
IPython.page !== undefined;
|
||||
});
|
||||
};
|
||||
|
||||
casper.kernel_running = function() {
|
||||
// Return whether or not the kernel is running.
|
||||
return this.evaluate(function() {
|
||||
return IPython &&
|
||||
IPython.notebook &&
|
||||
IPython.notebook.kernel &&
|
||||
IPython.notebook.kernel.is_connected();
|
||||
});
|
||||
};
|
||||
|
||||
casper.kernel_disconnected = function() {
|
||||
return this.evaluate(function() {
|
||||
return IPython.notebook.kernel.is_fully_disconnected();
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_kernel_ready = function () {
|
||||
this.waitFor(this.kernel_running);
|
||||
this.thenEvaluate(function () {
|
||||
IPython._kernel_ready = false;
|
||||
IPython.notebook.kernel.kernel_info(
|
||||
function () {
|
||||
IPython._kernel_ready = true;
|
||||
});
|
||||
});
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._kernel_ready;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
casper.shutdown_current_kernel = function () {
|
||||
// Shut down the current notebook's kernel.
|
||||
this.thenEvaluate(function() {
|
||||
IPython.notebook.session.delete();
|
||||
});
|
||||
// We close the page right after this so we need to give it time to complete.
|
||||
this.wait(1000);
|
||||
};
|
||||
|
||||
casper.delete_current_notebook = function () {
|
||||
// Delete created notebook.
|
||||
|
||||
// For some unknown reason, this doesn't work?!?
|
||||
this.thenEvaluate(function() {
|
||||
IPython.notebook.delete();
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_busy = function () {
|
||||
// Waits for the notebook to enter a busy state.
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._status == 'busy';
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_idle = function () {
|
||||
// Waits for the notebook to idle.
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function () {
|
||||
return IPython._status == 'idle';
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_output = function (cell_num, out_num) {
|
||||
// wait for the nth output in a given cell
|
||||
this.wait_for_idle();
|
||||
out_num = out_num || 0;
|
||||
this.then(function() {
|
||||
this.waitFor(function (c, o) {
|
||||
return this.evaluate(function get_output(c, o) {
|
||||
var cell = IPython.notebook.get_cell(c);
|
||||
return cell.output_area.outputs.length > o;
|
||||
},
|
||||
// pass parameter from the test suite js to the browser code js
|
||||
{c : cell_num, o : out_num});
|
||||
},
|
||||
function then() { },
|
||||
function timeout() {
|
||||
this.echo("wait_for_output timed out on cell "+cell_num+", waiting for "+out_num+" outputs .");
|
||||
var pn = this.evaluate(function get_prompt(c) {
|
||||
return (IPython.notebook.get_cell(c)|| {'input_prompt_number':'no cell'}).input_prompt_number;
|
||||
});
|
||||
this.echo("cell prompt was :'"+pn+"'.");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_widget = function (widget_info) {
|
||||
// wait for a widget msg que to reach 0
|
||||
//
|
||||
// Parameters
|
||||
// ----------
|
||||
// widget_info : object
|
||||
// Object which contains info related to the widget. The model_id property
|
||||
// is used to identify the widget.
|
||||
|
||||
// Clear the results of a previous query, if they exist. Make sure a
|
||||
// dictionary exists to store the async results in.
|
||||
this.thenEvaluate(function(model_id) {
|
||||
if (window.pending_msgs === undefined) {
|
||||
window.pending_msgs = {};
|
||||
} else {
|
||||
window.pending_msgs[model_id] = -1;
|
||||
}
|
||||
}, {model_id: widget_info.model_id});
|
||||
|
||||
// Wait for the pending messages to be 0.
|
||||
this.waitFor(function () {
|
||||
var pending = this.evaluate(function (model_id) {
|
||||
|
||||
// Get the model. Once the model is had, store it's pending_msgs
|
||||
// count in the window's dictionary.
|
||||
IPython.notebook.kernel.widget_manager.get_model(model_id)
|
||||
.then(function(model) {
|
||||
window.pending_msgs[model_id] = model.pending_msgs;
|
||||
});
|
||||
|
||||
// Return the pending_msgs result.
|
||||
return window.pending_msgs[model_id];
|
||||
}, {model_id: widget_info.model_id});
|
||||
|
||||
if (pending === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
casper.cell_has_outputs = function (cell_num) {
|
||||
var result = casper.evaluate(function (c) {
|
||||
var cell = IPython.notebook.get_cell(c);
|
||||
return cell.output_area.outputs.length;
|
||||
},
|
||||
{c : cell_num});
|
||||
return result > 0;
|
||||
};
|
||||
|
||||
casper.get_output_cell = function (cell_num, out_num, message) {
|
||||
messsge = message+': ' ||'no category :'
|
||||
// return an output of a given cell
|
||||
out_num = out_num || 0;
|
||||
var result = casper.evaluate(function (c, o) {
|
||||
var cell = IPython.notebook.get_cell(c);
|
||||
return cell.output_area.outputs[o];
|
||||
},
|
||||
{c : cell_num, o : out_num});
|
||||
if (!result) {
|
||||
var num_outputs = casper.evaluate(function (c) {
|
||||
var cell = IPython.notebook.get_cell(c);
|
||||
return cell.output_area.outputs.length;
|
||||
},
|
||||
{c : cell_num});
|
||||
this.test.assertTrue(false,
|
||||
message+"Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
|
||||
);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
casper.get_cells_length = function () {
|
||||
// return the number of cells in the notebook
|
||||
var result = casper.evaluate(function () {
|
||||
return IPython.notebook.get_cells().length;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
casper.set_cell_text = function(index, text){
|
||||
// Set the text content of a cell.
|
||||
this.evaluate(function (index, text) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
cell.set_text(text);
|
||||
}, index, text);
|
||||
};
|
||||
|
||||
casper.get_cell_text = function(index){
|
||||
// Get the text content of a cell.
|
||||
return this.evaluate(function (index) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
return cell.get_text();
|
||||
}, index);
|
||||
};
|
||||
|
||||
casper.insert_cell_at_bottom = function(cell_type){
|
||||
// Inserts a cell at the bottom of the notebook
|
||||
// Returns the new cell's index.
|
||||
return this.evaluate(function (cell_type) {
|
||||
var cell = IPython.notebook.insert_cell_at_bottom(cell_type);
|
||||
return IPython.notebook.find_cell_index(cell);
|
||||
}, cell_type);
|
||||
};
|
||||
|
||||
casper.append_cell = function(text, cell_type) {
|
||||
// Insert a cell at the bottom of the notebook and set the cells text.
|
||||
// Returns the new cell's index.
|
||||
var index = this.insert_cell_at_bottom(cell_type);
|
||||
if (text !== undefined) {
|
||||
this.set_cell_text(index, text);
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
casper.execute_cell = function(index, expect_failure){
|
||||
// Asynchronously executes a cell by index.
|
||||
// Returns the cell's index.
|
||||
|
||||
if (expect_failure === undefined) expect_failure = false;
|
||||
var that = this;
|
||||
this.then(function(){
|
||||
that.evaluate(function (index) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
cell.execute();
|
||||
}, index);
|
||||
});
|
||||
this.wait_for_idle();
|
||||
|
||||
this.then(function () {
|
||||
var error = that.evaluate(function (index) {
|
||||
var cell = IPython.notebook.get_cell(index);
|
||||
var outputs = cell.output_area.outputs;
|
||||
for (var i = 0; i < outputs.length; i++) {
|
||||
if (outputs[i].output_type == 'error') {
|
||||
return outputs[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, index);
|
||||
if (error === null) {
|
||||
this.test.fail("Failed to check for error output");
|
||||
}
|
||||
if (expect_failure && error === false) {
|
||||
this.test.fail("Expected error while running cell");
|
||||
} else if (!expect_failure && error !== false) {
|
||||
this.test.fail("Error running cell:\n" + error.traceback.join('\n'));
|
||||
}
|
||||
});
|
||||
return index;
|
||||
};
|
||||
|
||||
casper.execute_cell_then = function(index, then_callback, expect_failure) {
|
||||
// Synchronously executes a cell by index.
|
||||
// Optionally accepts a then_callback parameter. then_callback will get called
|
||||
// when the cell has finished executing.
|
||||
// Returns the cell's index.
|
||||
var return_val = this.execute_cell(index, expect_failure);
|
||||
|
||||
this.wait_for_idle();
|
||||
|
||||
var that = this;
|
||||
this.then(function(){
|
||||
if (then_callback!==undefined) {
|
||||
then_callback.apply(that, [index]);
|
||||
}
|
||||
});
|
||||
|
||||
return return_val;
|
||||
};
|
||||
|
||||
casper.append_cell_execute_then = function(text, then_callback, expect_failure) {
|
||||
// Append a code cell and execute it, optionally calling a then_callback
|
||||
var c = this.append_cell(text);
|
||||
return this.execute_cell_then(c, then_callback, expect_failure);
|
||||
};
|
||||
|
||||
casper.assert_output_equals = function(text, output_text, message) {
|
||||
// Append a code cell with the text, then assert the output is equal to output_text
|
||||
this.append_cell_execute_then(text, function(index) {
|
||||
this.test.assertEquals(this.get_output_cell(index).text.trim(), output_text, message);
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_element = function(index, selector){
|
||||
// Utility function that allows us to easily wait for an element
|
||||
// within a cell. Uses JQuery selector to look for the element.
|
||||
var that = this;
|
||||
this.waitFor(function() {
|
||||
return that.cell_element_exists(index, selector);
|
||||
});
|
||||
};
|
||||
|
||||
casper.cell_element_exists = function(index, selector){
|
||||
// Utility function that allows us to easily check if an element exists
|
||||
// within a cell. Uses JQuery selector to look for the element.
|
||||
return casper.evaluate(function (index, selector) {
|
||||
var $cell = IPython.notebook.get_cell(index).element;
|
||||
return $cell.find(selector).length > 0;
|
||||
}, index, selector);
|
||||
};
|
||||
|
||||
casper.cell_element_function = function(index, selector, function_name, function_args){
|
||||
// Utility function that allows us to execute a jQuery function on an
|
||||
// element within a cell.
|
||||
return casper.evaluate(function (index, selector, function_name, function_args) {
|
||||
var $cell = IPython.notebook.get_cell(index).element;
|
||||
var $el = $cell.find(selector);
|
||||
return $el[function_name].apply($el, function_args);
|
||||
}, index, selector, function_name, function_args);
|
||||
};
|
||||
|
||||
casper.validate_notebook_state = function(message, mode, cell_index) {
|
||||
// Validate the entire dual mode state of the notebook. Make sure no more than
|
||||
// one cell is selected, focused, in edit mode, etc...
|
||||
// General tests.
|
||||
this.test.assertEquals(this.get_keyboard_mode(), this.get_notebook_mode(),
|
||||
message + '; keyboard and notebook modes match');
|
||||
// Is the selected cell the only cell that is selected?
|
||||
if (cell_index!==undefined) {
|
||||
this.test.assert(this.is_only_cell_selected(cell_index),
|
||||
message + '; expecting cell ' + cell_index + ' to be the only cell selected. Got selected cell(s):'+
|
||||
(function(){
|
||||
return casper.evaluate(function(){
|
||||
return IPython.notebook.get_selected_cells_indices();
|
||||
})
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
||||
// Mode specific tests.
|
||||
if (mode==='command') {
|
||||
// Are the notebook and keyboard manager in command mode?
|
||||
this.test.assertEquals(this.get_keyboard_mode(), 'command',
|
||||
message + '; in command mode');
|
||||
// Make sure there isn't a single cell in edit mode.
|
||||
this.test.assert(this.is_only_cell_edit(null),
|
||||
message + '; all cells in command mode');
|
||||
this.test.assert(this.is_cell_editor_focused(null),
|
||||
message + '; no cell editors are focused while in command mode');
|
||||
|
||||
} else if (mode==='edit') {
|
||||
// Are the notebook and keyboard manager in edit mode?
|
||||
this.test.assertEquals(this.get_keyboard_mode(), 'edit',
|
||||
message + '; in edit mode');
|
||||
if (cell_index!==undefined) {
|
||||
// Is the specified cell the only cell in edit mode?
|
||||
this.test.assert(this.is_only_cell_edit(cell_index),
|
||||
message + '; cell ' + cell_index + ' is the only cell in edit mode '+ this.cells_modes());
|
||||
// Is the specified cell the only cell with a focused code mirror?
|
||||
this.test.assert(this.is_cell_editor_focused(cell_index),
|
||||
message + '; cell ' + cell_index + '\'s editor is appropriately focused');
|
||||
}
|
||||
|
||||
} else {
|
||||
this.test.assert(false, message + '; ' + mode + ' is an unknown mode');
|
||||
}
|
||||
};
|
||||
|
||||
casper.select_cell = function(index, moveanchor) {
|
||||
// Select a cell in the notebook.
|
||||
this.evaluate(function (i, moveanchor) {
|
||||
IPython.notebook.select(i, moveanchor);
|
||||
}, {i: index, moveanchor: moveanchor});
|
||||
};
|
||||
|
||||
casper.select_cells = function(index, bound, moveanchor) {
|
||||
// Select a block of cells in the notebook.
|
||||
// like Python range, selects [index,bound)
|
||||
this.evaluate(function (i, n, moveanchor) {
|
||||
Jupyter.notebook.select(i, moveanchor);
|
||||
Jupyter.notebook.extend_selection_by(n);
|
||||
}, {i: index, n: (bound - index - 1), moveanchor: moveanchor});
|
||||
};
|
||||
|
||||
casper.click_cell_editor = function(index) {
|
||||
// Emulate a click on a cell's editor.
|
||||
|
||||
// Code Mirror does not play nicely with emulated brower events.
|
||||
// Instead of trying to emulate a click, here we run code similar to
|
||||
// the code used in Code Mirror that handles the mousedown event on a
|
||||
// region of codemirror that the user can focus.
|
||||
this.evaluate(function (i) {
|
||||
var cm = IPython.notebook.get_cell(i).code_mirror;
|
||||
if (cm.options.readOnly != "nocursor" && (document.activeElement != cm.display.input)){
|
||||
cm.display.input.focus();
|
||||
}
|
||||
}, {i: index});
|
||||
};
|
||||
|
||||
casper.set_cell_editor_cursor = function(index, line_index, char_index) {
|
||||
// Set the Code Mirror instance cursor's location.
|
||||
this.evaluate(function (i, l, c) {
|
||||
IPython.notebook.get_cell(i).code_mirror.setCursor(l, c);
|
||||
}, {i: index, l: line_index, c: char_index});
|
||||
};
|
||||
|
||||
casper.focus_notebook = function() {
|
||||
// Focus the notebook div.
|
||||
this.evaluate(function (){
|
||||
$('#notebook').focus();
|
||||
}, {});
|
||||
};
|
||||
|
||||
casper.trigger_keydown = function() {
|
||||
// Emulate a keydown in the notebook.
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this.evaluate(function (k) {
|
||||
var element = $(document);
|
||||
var event = IPython.keyboard.shortcut_to_event(k, 'keydown');
|
||||
element.trigger(event);
|
||||
}, {k: arguments[i]});
|
||||
}
|
||||
};
|
||||
|
||||
casper.get_keyboard_mode = function() {
|
||||
// Get the mode of the keyboard manager.
|
||||
return this.evaluate(function() {
|
||||
return IPython.keyboard_manager.mode;
|
||||
}, {});
|
||||
};
|
||||
|
||||
casper.get_notebook_mode = function() {
|
||||
// Get the mode of the notebook.
|
||||
return this.evaluate(function() {
|
||||
return IPython.notebook.mode;
|
||||
}, {});
|
||||
};
|
||||
|
||||
casper.get_cell = function(index) {
|
||||
// Get a single cell.
|
||||
//
|
||||
// Note: Handles to DOM elements stored in the cell will be useless once in
|
||||
// CasperJS context.
|
||||
return this.evaluate(function(i) {
|
||||
var cell = IPython.notebook.get_cell(i);
|
||||
if (cell) {
|
||||
return cell;
|
||||
}
|
||||
return null;
|
||||
}, {i : index});
|
||||
};
|
||||
|
||||
casper.is_cell_editor_focused = function(index) {
|
||||
// Make sure a cell's editor is the only editor focused on the page.
|
||||
return this.evaluate(function(i) {
|
||||
var focused_textarea = $('#notebook .CodeMirror-focused textarea');
|
||||
if (focused_textarea.length > 1) { throw 'More than one Code Mirror editor is focused at once!'; }
|
||||
if (i === null) {
|
||||
return focused_textarea.length === 0;
|
||||
} else {
|
||||
var cell = IPython.notebook.get_cell(i);
|
||||
if (cell) {
|
||||
return cell.code_mirror.getInputField() == focused_textarea[0];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, {i : index});
|
||||
};
|
||||
|
||||
casper.is_only_cell_selected = function(index) {
|
||||
// Check if a cell is the only cell selected.
|
||||
// Pass null as the index to check if no cells are selected.
|
||||
return this.is_only_cell_on(index, 'selected', 'unselected');
|
||||
};
|
||||
|
||||
casper.is_only_cell_edit = function(index) {
|
||||
// Check if a cell is the only cell in edit mode.
|
||||
// Pass null as the index to check if all of the cells are in command mode.
|
||||
var cells_length = this.get_cells_length();
|
||||
for (var j = 0; j < cells_length; j++) {
|
||||
if (j === index) {
|
||||
if (!this.cell_mode_is(j, 'edit')) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (this.cell_mode_is(j, 'edit')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
casper.is_only_cell_on = function(i, on_class, off_class) {
|
||||
// Check if a cell is the only cell with the `on_class` DOM class applied to it.
|
||||
// All of the other cells are checked for the `off_class` DOM class.
|
||||
// Pass null as the index to check if all of the cells have the `off_class`.
|
||||
var cells_length = this.get_cells_length();
|
||||
for (var j = 0; j < cells_length; j++) {
|
||||
if (j === i) {
|
||||
if (this.cell_has_class(j, off_class) || !this.cell_has_class(j, on_class)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!this.cell_has_class(j, off_class) || this.cell_has_class(j, on_class)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
casper.cells_modes = function(){
|
||||
return this.evaluate(function(){
|
||||
return IPython.notebook.get_cells().map(function(x,c){return x.mode})
|
||||
}, {});
|
||||
};
|
||||
|
||||
casper.cell_mode_is = function(index, mode) {
|
||||
// Check if a cell is in a specific mode
|
||||
return this.evaluate(function(i, m) {
|
||||
var cell = IPython.notebook.get_cell(i);
|
||||
if (cell) {
|
||||
return cell.mode === m;
|
||||
}
|
||||
return false;
|
||||
}, {i : index, m: mode});
|
||||
};
|
||||
|
||||
|
||||
casper.cell_has_class = function(index, classes) {
|
||||
// Check if a cell has a class.
|
||||
return this.evaluate(function(i, c) {
|
||||
var cell = IPython.notebook.get_cell(i);
|
||||
if (cell) {
|
||||
return cell.element.hasClass(c);
|
||||
}
|
||||
return false;
|
||||
}, {i : index, c: classes});
|
||||
};
|
||||
|
||||
casper.is_cell_rendered = function (index) {
|
||||
return this.evaluate(function(i) {
|
||||
return !!IPython.notebook.get_cell(i).rendered;
|
||||
}, {i:index});
|
||||
};
|
||||
|
||||
casper.assert_colors_equal = function (hex_color, local_color, msg) {
|
||||
// Tests to see if two colors are equal.
|
||||
//
|
||||
// Parameters
|
||||
// hex_color: string
|
||||
// Hexadecimal color code, with or without preceding hash character.
|
||||
// local_color: string
|
||||
// Local color representation. Can either be hexadecimal (default for
|
||||
// phantom) or rgb (default for slimer).
|
||||
|
||||
// Remove parentheses, hashes, semi-colons, and space characters.
|
||||
hex_color = hex_color.replace(/[\(\); #]/, '');
|
||||
local_color = local_color.replace(/[\(\); #]/, '');
|
||||
|
||||
// If the local color is rgb, clean it up and replace
|
||||
if (local_color.substr(0,3).toLowerCase() == 'rgb') {
|
||||
var components = local_color.substr(3).split(',');
|
||||
local_color = '';
|
||||
for (var i = 0; i < components.length; i++) {
|
||||
var part = parseInt(components[i]).toString(16);
|
||||
while (part.length < 2) part = '0' + part;
|
||||
local_color += part;
|
||||
}
|
||||
}
|
||||
|
||||
this.test.assertEquals(hex_color.toUpperCase(), local_color.toUpperCase(), msg);
|
||||
};
|
||||
|
||||
casper.notebook_test = function(test) {
|
||||
// Wrap a notebook test to reduce boilerplate.
|
||||
this.open_new_notebook();
|
||||
|
||||
// Echo whether or not we are running this test using SlimerJS
|
||||
if (this.evaluate(function(){
|
||||
return typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
|
||||
})) {
|
||||
console.log('This test is running in SlimerJS.');
|
||||
this.slimerjs = true;
|
||||
}
|
||||
|
||||
// Make sure to remove the onbeforeunload callback. This callback is
|
||||
// responsible for the "Are you sure you want to quit?" type messages.
|
||||
// PhantomJS ignores these prompts, SlimerJS does not which causes hangs.
|
||||
this.then(function(){
|
||||
this.evaluate(function(){
|
||||
window.onbeforeunload = function(){};
|
||||
});
|
||||
});
|
||||
|
||||
this.then(test);
|
||||
|
||||
// Kill the kernel and delete the notebook.
|
||||
this.shutdown_current_kernel();
|
||||
// This is still broken but shouldn't be a problem for now.
|
||||
// this.delete_current_notebook();
|
||||
|
||||
// This is required to clean up the page we just finished with. If we don't call this
|
||||
// casperjs will leak file descriptors of all the open WebSockets in that page. We
|
||||
// have to set this.page=null so that next time casper.start runs, it will create a
|
||||
// new page from scratch.
|
||||
this.then(function () {
|
||||
this.page.close();
|
||||
this.page = null;
|
||||
});
|
||||
|
||||
// Run the browser automation.
|
||||
this.run(function() {
|
||||
this.test.done();
|
||||
});
|
||||
};
|
||||
|
||||
casper.wait_for_dashboard = function () {
|
||||
// Wait for the dashboard list to load.
|
||||
casper.waitForSelector('.list_item');
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the dashboard page
|
||||
* @param {bool} use_start - If true, will use casper.start(), otherwise
|
||||
* casper.open(). You should only set it to true if the dashboard
|
||||
* is the first URL to be opened in the test, because calling
|
||||
* casper.start() multiple times causes casper to skip subsequent then()
|
||||
*/
|
||||
casper.open_dashboard = function (use_start) {
|
||||
if (use_start === undefined) {
|
||||
use_start = false;
|
||||
}
|
||||
// Start casper by opening the dashboard page.
|
||||
var baseUrl = this.get_notebook_server();
|
||||
if (use_start) {
|
||||
this.start(baseUrl);
|
||||
} else {
|
||||
this.open(baseUrl);
|
||||
}
|
||||
this.waitFor(this.page_loaded);
|
||||
this.wait_for_dashboard();
|
||||
};
|
||||
|
||||
casper.dashboard_test = function (test) {
|
||||
// Open the dashboard page and run a test.
|
||||
this.open_dashboard(true);
|
||||
this.then(test);
|
||||
|
||||
this.then(function () {
|
||||
this.page.close();
|
||||
this.page = null;
|
||||
});
|
||||
|
||||
// Run the browser automation.
|
||||
this.run(function() {
|
||||
this.test.done();
|
||||
});
|
||||
};
|
||||
|
||||
// note that this will only work for UNIQUE events -- if you want to
|
||||
// listen for the same event twice, this will not work!
|
||||
casper.event_test = function (name, events, action, timeout) {
|
||||
|
||||
// set up handlers to listen for each of the events
|
||||
this.thenEvaluate(function (events) {
|
||||
var make_handler = function (event) {
|
||||
return function () {
|
||||
IPython._events_triggered.push(event);
|
||||
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
|
||||
delete IPython._event_handlers[event];
|
||||
};
|
||||
};
|
||||
IPython._event_handlers = {};
|
||||
IPython._events_triggered = [];
|
||||
for (var i=0; i < events.length; i++) {
|
||||
IPython._event_handlers[events[i]] = make_handler(events[i]);
|
||||
IPython.notebook.events.on(events[i], IPython._event_handlers[events[i]]);
|
||||
}
|
||||
}, [events]);
|
||||
|
||||
// execute the requested action
|
||||
this.then(action);
|
||||
|
||||
// wait for all the events to be triggered
|
||||
this.waitFor(function () {
|
||||
return this.evaluate(function (events) {
|
||||
return IPython._events_triggered.length >= events.length;
|
||||
}, [events]);
|
||||
}, undefined, undefined, timeout);
|
||||
|
||||
// test that the events were triggered in the proper order
|
||||
this.then(function () {
|
||||
var triggered = this.evaluate(function () {
|
||||
return IPython._events_triggered;
|
||||
});
|
||||
var handlers = this.evaluate(function () {
|
||||
return Object.keys(IPython._event_handlers);
|
||||
});
|
||||
this.test.assertEquals(triggered.length, events.length, name + ': ' + events.length + ' events were triggered');
|
||||
this.test.assertEquals(handlers.length, 0, name + ': all handlers triggered');
|
||||
for (var i=0; i < events.length; i++) {
|
||||
this.test.assertEquals(triggered[i], events[i], name + ': ' + events[i] + ' was triggered');
|
||||
}
|
||||
});
|
||||
|
||||
// turn off any remaining event listeners
|
||||
this.thenEvaluate(function () {
|
||||
for (var event in IPython._event_handlers) {
|
||||
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
|
||||
delete IPython._event_handlers[event];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
casper.options.waitTimeout=10000;
|
||||
casper.on('waitFor.timeout', function onWaitForTimeout(timeout) {
|
||||
this.echo("Timeout for " + casper.get_notebook_server());
|
||||
this.echo("Is the notebook server running?");
|
||||
});
|
||||
|
||||
casper.print_log = function () {
|
||||
// Pass `console.log` calls from page JS to casper.
|
||||
this.on('remote.message', function(msg) {
|
||||
this.echo('Remote message caught: ' + msg);
|
||||
});
|
||||
};
|
||||
|
||||
casper.on("page.error", function onError(msg, trace) {
|
||||
// show errors in the browser
|
||||
this.echo("Page Error");
|
||||
this.echo(" Message: " + msg.split('\n').join('\n '));
|
||||
this.echo(" Call stack:");
|
||||
var local_path = this.get_notebook_server();
|
||||
for (var i = 0; i < trace.length; i++) {
|
||||
var frame = trace[i];
|
||||
var file = frame.file;
|
||||
// shorten common phantomjs evaluate url
|
||||
// this will have a different value on slimerjs
|
||||
if (file === "phantomjs://webpage.evaluate()") {
|
||||
file = "evaluate";
|
||||
}
|
||||
// remove the version tag from the path
|
||||
file = file.replace(/(\?v=[0-9abcdef]+)/, '');
|
||||
// remove the local address from the beginning of the path
|
||||
if (file.indexOf(local_path) === 0) {
|
||||
file = file.substr(local_path.length);
|
||||
}
|
||||
var frame_text = (frame.function.length > 0) ? " in " + frame.function : "";
|
||||
this.echo(" line " + frame.line + " of " + file + frame_text);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
casper.capture_log = function () {
|
||||
// show captured errors
|
||||
var captured_log = [];
|
||||
var seen_errors = 0;
|
||||
this.on('remote.message', function(msg) {
|
||||
captured_log.push(msg);
|
||||
});
|
||||
|
||||
var that = this;
|
||||
this.test.on("test.done", function (result) {
|
||||
// test.done runs per-file,
|
||||
// but suiteResults is per-suite (directory)
|
||||
var current_errors;
|
||||
if (this.suiteResults) {
|
||||
// casper 1.1 has suiteResults
|
||||
current_errors = this.suiteResults.countErrors() + this.suiteResults.countFailed();
|
||||
} else {
|
||||
// casper 1.0 has testResults instead
|
||||
current_errors = this.testResults.failed;
|
||||
}
|
||||
|
||||
if (current_errors > seen_errors && captured_log.length > 0) {
|
||||
casper.echo("\nCaptured console.log:");
|
||||
for (var i = 0; i < captured_log.length; i++) {
|
||||
var output = String(captured_log[i]).split('\n');
|
||||
for (var j = 0; j < output.length; j++) {
|
||||
casper.echo(" " + output[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seen_errors = current_errors;
|
||||
captured_log = [];
|
||||
});
|
||||
};
|
||||
|
||||
casper.interact = function() {
|
||||
// Start an interactive Javascript console.
|
||||
var system = require('system');
|
||||
system.stdout.writeLine('JS interactive console.');
|
||||
system.stdout.writeLine('Type `exit` to quit.');
|
||||
|
||||
function read_line() {
|
||||
system.stdout.writeLine('JS: ');
|
||||
var line = system.stdin.readLine();
|
||||
return line;
|
||||
}
|
||||
|
||||
var input = read_line();
|
||||
while (input.trim() != 'exit') {
|
||||
var output = this.evaluate(function(code) {
|
||||
return String(eval(code));
|
||||
}, {code: input});
|
||||
system.stdout.writeLine('\nOut: ' + output);
|
||||
input = read_line();
|
||||
}
|
||||
};
|
||||
|
||||
casper.capture_log();
|
Reference in New Issue
Block a user