\n",
+ " Name: \n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Try modifying the `user` defined in channel `a`. This change will not impact the user defined in channel `b`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now to something a bit more complex. What if you want to have Scala code that reacts to changes in a value on a template. Lets start with the template below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "%%html\n",
+ "\n",
+ "
Type something
\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can create a Scala function that will watch for changes to the value."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "import urth.widgets.WidgetChannels.channel\n",
+ "\n",
+ "val on_aSomething_change = (oldVal: Option[String], newVal: String) => {\n",
+ " val msg = s\"Hello from on_aNumber_change! Got ${newVal}\"\n",
+ " channel(\"c\").set(\"message\", msg)\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "lazy val initChannelWatch = () => {\n",
+ " channel(\"c\").watch(\"aSomething\", on_aSomething_change)\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "%%html\n",
+ "\n",
+ " \n",
+ " \n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Lets create a template where we can set a message."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "%%html\n",
+ "\n",
+ " {{message}}\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now when you type something on the input box, it triggers the Scala function `on_aSomething_change`. This function can then also set values on the channel by using the `set` method. Notice that the template with `{{message}}` is getting updated."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Click [here](../examples/urth-core-bind.ipynb) to learn more about ``"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Support for Spark DataFrames is provided. Below is a DataFrame with some basic contact information."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "val sqlContext = new org.apache.spark.sql.SQLContext(sc)\n",
+ "import sqlContext.implicits._"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "case class Contact(name: String, email: String)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "val df = sqlContext.createDataFrame(Seq(\n",
+ " Contact(\"Jane Doe\", \"jane@doe.com\"), \n",
+ " Contact(\"John Doe\", \"john@doe.com\")))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Below, we print out the contents of the DataFrame in a more readable format."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "%%html\n",
+ "\n",
+ " \n",
+ " \n",
+ "
\n",
+ " {{item.0}}, {{item.1}}\n",
+ "
\n",
+ " \n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "By setting the `auto` keyword, the resulting output will update whenever the DataFrame is modified. Try changing which set of contact information is used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "val df = sqlContext.createDataFrame(Seq(\n",
+ " Contact(\"Richard Roe\", \"richard@roe.com\"),\n",
+ " Contact(\"Bob Murphy\", \"bob@murphy.com\")))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Click [here](../examples/urth-core-dataframe.ipynb) to learn more about ``"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can also import web components. Below, we bring in the `paper-input` element from the [Polymer Catalog](https://elements.polymer-project.org/)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "%%html\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Click [here](../examples/urth-core-import.ipynb) to learn more about ``"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Apache_Toree",
+ "language": "",
+ "name": "apache_toree"
+ },
+ "language_info": {
+ "name": "scala"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/package.json b/package.json
index 037c91fe..4aef09ec 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,8 @@
"minimist": "latest",
"selenium-standalone": "latest",
"web-component-tester": "^4",
- "vulcanize": "1.14.8"
+ "vulcanize": "1.14.8",
+ "bluebird" : "3.3.5"
},
"config": {},
"scripts": {
diff --git a/system-test/urth-core-bind-specs.js b/system-test/urth-core-bind-specs.js
index 76c493af..2863344a 100644
--- a/system-test/urth-core-bind-specs.js
+++ b/system-test/urth-core-bind-specs.js
@@ -7,7 +7,7 @@ var boilerplate = new Boilerplate();
describe('Urth Core Bind', function() {
- boilerplate.setup(this.title, '/notebooks/tests/urth-core-bind.ipynb');
+ boilerplate.setup(this.title, '/notebooks/tests/urth-core-bind.ipynb', 4);
it('should wait for dependency to load', function(done) {
// Using a random number to protect against the possibility of a previous
diff --git a/system-test/urth-r-widgets-specs.js b/system-test/urth-r-widgets-specs.js
index a7628d09..44e1f608 100644
--- a/system-test/urth-r-widgets-specs.js
+++ b/system-test/urth-r-widgets-specs.js
@@ -6,7 +6,7 @@ var Boilerplate = require('./utils/boilerplate');
var boilerplate = new Boilerplate();
process.env.PYTHON != "python2" && describe('Widgets R System Test', function() {
- boilerplate.setup(this.title, '/notebooks/tests/urth-r-widgets.ipynb');
+ boilerplate.setup(this.title, '/notebooks/tests/urth-r-widgets.ipynb', 8);
it('should print the result of a Function Widget invocation', function(done) {
boilerplate.browser
diff --git a/system-test/urth-scala-widgets-specs.js b/system-test/urth-scala-widgets-specs.js
new file mode 100644
index 00000000..804c853c
--- /dev/null
+++ b/system-test/urth-scala-widgets-specs.js
@@ -0,0 +1,68 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+var wd = require('wd');
+var Boilerplate = require('./utils/boilerplate');
+var boilerplate = new Boilerplate();
+
+process.env.PYTHON != "python2" && describe('Widgets Scala System Test', function() {
+ boilerplate.setup(this.title, '/notebooks/tests/Walkthrough-Scala.ipynb', 11);
+
+ var timeout = 30000;
+
+ it('should print the correct variable that is used for urth-core-function', function(done) {
+
+ boilerplate.browser
+ .elementsByCssSelector('div.code_cell').nth(5)
+ .elementByXPath('//button[text()="invoke"]').click()
+ .waitForElementById('test1', wd.asserters.textInclude('world'), timeout)
+ .nodeify(done);
+ });
+
+ it('should bind variable to channel a', function(done) {
+ boilerplate.browser
+ .elementsByCssSelector('div.code_cell').nth(6)
+ .elementByCssSelector('>', 'input')
+ .type('A')
+ .waitForElementById('test2', wd.asserters.textInclude('A'), timeout)
+ .nodeify(done);
+ });
+
+ it('should bind variable to channel b', function(done) {
+ boilerplate.browser
+ .elementsByCssSelector('div.code_cell').nth(7)
+ .elementByCssSelector('>', 'input')
+ .type('B')
+ .waitForElementById('test3', wd.asserters.textInclude('B'), timeout)
+ .nodeify(done);
+ });
+
+ it('should bind variables to channels independently', function(done) {
+ boilerplate.browser
+ .elementsByCssSelector('div.code_cell').nth(6)
+ .elementByCssSelector('>', 'input')
+ .type('2')
+ .elementByCssSelector('#test2')
+ .text().should.eventually.include('A2')
+ .waitForElementById('test2', wd.asserters.textInclude('A2'), timeout)
+ .waitForElementById('test3', wd.asserters.textInclude('B'), timeout)
+ .nodeify(done);
+ });
+
+ it('should watch for changes in a watched variable', function(done) {
+ boilerplate.browser
+ .elementByXPath('//button[text()="initChannelWatch"]').click()
+ .elementsByCssSelector('div.code_cell').nth(8)
+ .elementByCssSelector('>', 'input')
+ .type('watched message')
+ .waitForElementById('test4', wd.asserters.textInclude('watched message'), timeout)
+ .nodeify(done);
+ });
+
+ it('should update output when DataFrame is modified and set to auto', function(done) {
+ boilerplate.browser
+ .elementsByCssSelector('div.code_cell').nth(14)
+ .waitForElementByClassName('test5', wd.asserters.textInclude('Richard Roe'), timeout)
+ .nodeify(done);
+ });
+});
\ No newline at end of file
diff --git a/system-test/urth-system-test-specs.js b/system-test/urth-system-test-specs.js
index 810e7b3e..fdec215a 100644
--- a/system-test/urth-system-test-specs.js
+++ b/system-test/urth-system-test-specs.js
@@ -5,8 +5,8 @@ var wd = require('wd');
var Boilerplate = require('./utils/boilerplate');
var boilerplate = new Boilerplate();
-describe('Widgets System Test', function() {
- boilerplate.setup(this.title, '/notebooks/tests/Walkthrough.ipynb');
+describe('Widgets Python System Test', function() {
+ boilerplate.setup(this.title, '/notebooks/tests/Walkthrough.ipynb', 13);
it('should not execute Urth.whenReady API until components have upgraded', function(done) {
boilerplate.browser
@@ -61,6 +61,8 @@ describe('Widgets System Test', function() {
.type(boilerplate.SPECIAL_KEYS.Enter) // Needed for IE
.type('watched message')
.waitForElementById('test4', wd.asserters.textInclude('watched message'), 10000)
+ .elementsByCssSelector('div.output_area').nth(7)
+ .click()
.nodeify(done);
});
@@ -86,3 +88,4 @@ describe('Widgets System Test', function() {
.nodeify(done);
});
});
+
diff --git a/system-test/urth-viz-table-specs.js b/system-test/urth-viz-table-specs.js
index 2aabf7ac..da773944 100644
--- a/system-test/urth-viz-table-specs.js
+++ b/system-test/urth-viz-table-specs.js
@@ -22,7 +22,7 @@ describe('Urth Viz Table Test', function() {
.catch(tagChaiAssertionError);
};
- boilerplate.setup(this.title, '/notebooks/tests/urth-viz-table.ipynb');
+ boilerplate.setup(this.title, '/notebooks/tests/urth-viz-table.ipynb', 4);
it('should run all cells and find a handsontable in the 3rd output area', function(done) {
boilerplate.browser
diff --git a/system-test/utils/boilerplate.js b/system-test/utils/boilerplate.js
index 6d0440cd..f5ffabf9 100644
--- a/system-test/utils/boilerplate.js
+++ b/system-test/utils/boilerplate.js
@@ -1,6 +1,6 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
-
+var Promise = require('bluebird');
var wd = require('wd');
require('colors');
var chai = require('chai');
@@ -18,6 +18,13 @@ chai.use(chaiAsPromised);
chai.should();
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
+// tagging chai assertion errors for retry
+var tagChaiAssertionError = function(err) {
+ // throw error and tag as retriable to poll again
+ err.retriable = err instanceof chai.AssertionError;
+ throw err;
+};
+
// Configure webdriver
wd.configureHttp({
timeout: 60000,
@@ -67,9 +74,22 @@ var Boilerplate = function(){
* Setups the before and after calls for each of your tests. The boilerplate
* will start each test on startingURL, which is a relative path to the resource to load.
*/
-Boilerplate.prototype.setup = function(testName, startingURL){
+Boilerplate.prototype.setup = function(testName, startingURL, outputCount){
var that = this;
+ var outputAsserter = new wd.Asserter(
+ function(target) { // browser or el
+ return target
+ .elementsByCssSelector('div.output_area').then(function(nodes) {
+ console.log("output areas visible: ", nodes.length, "/", outputCount)
+ nodes.should.have.length.above(outputCount-1);
+ return target; // this will be returned by waitFor
+ // and ignored by waitForElement.
+ })
+ .catch(tagChaiAssertionError); // tag errors for retry in catch.
+ }
+ );
+
before(function(done){
if (args.verbose) {
// optional logging
@@ -86,7 +106,7 @@ Boilerplate.prototype.setup = function(testName, startingURL){
this.browser.init(desired)
.get(startingURL || '/')
- .waitForElementByCssSelector('#kernel_indicator_icon.kernel_idle_icon', wd.asserters.isDisplayed, 10000)
+ .waitForElementByCssSelector('#kernel_indicator_icon.kernel_idle_icon', wd.asserters.isDisplayed, 80000)
.waitForElementByLinkText('Cell', wd.asserters.isDisplayed, 10000)
.safeExecute('localStorage.clear()')
.elementByLinkText('Cell')
@@ -102,6 +122,7 @@ Boilerplate.prototype.setup = function(testName, startingURL){
.waitForConditionInBrowser('window.Urth && Urth.kernel && Urth.kernel.is_connected()', 10000)
.waitForElementByCssSelector('#kernel_indicator_icon.kernel_idle_icon', wd.asserters.isDisplayed, 20000)
.waitForConditionInBrowser('typeof Urth.whenReady === "function"', 10000)
+ .waitFor(outputAsserter, 250000, 1000)
.nodeify(done);
}.bind(this));