From 468d981ffa5bea66e30b0cb81b290f9f43b1c626 Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Mon, 2 Jan 2023 14:40:04 -0800 Subject: [PATCH] Restore jupyter-widget tests (#7545) --- modules/jupyter-widget/package.json | 2 +- modules/jupyter-widget/src/deck-bundle.js | 22 ++-- modules/jupyter-widget/src/index.js | 28 ++--- .../src/lib/jupyter-transport-model.js | 109 ++++++++++-------- .../src/lib/jupyter-transport-view.js | 90 ++++++++------- .../src/playground/create-deck.js | 2 +- .../src/playground/playground.js | 1 + .../src/playground/utils/google-maps-utils.js | 2 +- .../src/playground/utils/mapbox-utils.js | 13 +-- test/modules/index.js | 2 +- .../dummy-jupyter-widgets-base.js | 13 +++ .../modules/jupyter-widget/dummy-mapbox-gl.js | 2 + test/modules/jupyter-widget/index.js | 16 +-- .../jupyter-widget/mock-widget-base.js | 2 +- test/render/index.js | 2 +- 15 files changed, 163 insertions(+), 143 deletions(-) create mode 100644 test/modules/jupyter-widget/dummy-jupyter-widgets-base.js create mode 100644 test/modules/jupyter-widget/dummy-mapbox-gl.js diff --git a/modules/jupyter-widget/package.json b/modules/jupyter-widget/package.json index d1228fe8d4d..6a10d73a594 100644 --- a/modules/jupyter-widget/package.json +++ b/modules/jupyter-widget/package.json @@ -40,7 +40,7 @@ "@loaders.gl/core": "^3.2.10", "@loaders.gl/csv": "^3.2.10", "@luma.gl/constants": "^8.5.16", - "mapbox-gl": "^1.2.1" + "mapbox-gl": "^1.13.2" }, "jupyterlab": { "extension": "src/plugin", diff --git a/modules/jupyter-widget/src/deck-bundle.js b/modules/jupyter-widget/src/deck-bundle.js index 6e9f6a563c4..bee6bfd01f3 100644 --- a/modules/jupyter-widget/src/deck-bundle.js +++ b/modules/jupyter-widget/src/deck-bundle.js @@ -2,16 +2,22 @@ * Pulls together all deck.gl dependencies used * in @deck.gl/jupyter-widget */ -const deck = require('../../core/bundle'); +import deck from '../../core/bundle'; +import * as deckglLayers from '@deck.gl/layers'; +import * as deckglAggregationLayers from '@deck.gl/aggregation-layers'; +import * as deckglGeoLayers from '@deck.gl/geo-layers'; +import * as deckglMeshLayers from '@deck.gl/mesh-layers'; +import * as GoogleMapsUtils from '@deck.gl/google-maps'; +import * as JSONUtils from '@deck.gl/json'; Object.assign( deck, - require('@deck.gl/layers'), - require('@deck.gl/aggregation-layers'), - require('@deck.gl/geo-layers'), - require('@deck.gl/mesh-layers'), - require('@deck.gl/google-maps'), - require('@deck.gl/json') + deckglLayers, + deckglAggregationLayers, + deckglGeoLayers, + deckglMeshLayers, + GoogleMapsUtils, + JSONUtils ); -module.exports = deck; +export default deck; diff --git a/modules/jupyter-widget/src/index.js b/modules/jupyter-widget/src/index.js index 030efa6a396..baa5de282db 100644 --- a/modules/jupyter-widget/src/index.js +++ b/modules/jupyter-widget/src/index.js @@ -3,42 +3,34 @@ // See https://github.com/jupyter-widgets/widget-ts-cookiecutter/blob/master/%7B%7Bcookiecutter.github_project_name%7D%7D/src/extension.ts // Entry point for the Jupyter Notebook bundle containing custom Backbone model and view definitions. +import {MODULE_VERSION, MODULE_NAME} from './version'; +// TODO - this should be placed in a separate module `@deck.gl/playground` +import {createDeck, updateDeck} from './playground/create-deck'; +import {initPlayground} from './playground'; +import jupyterTransport from './lib/jupyter-transport'; +import JupyterTransportModel from './lib/jupyter-transport-model'; +import JupyterTransportView from './lib/jupyter-transport-view'; + // Some static assets may be required by the custom widget javascript. The base // url for the notebook is not known at build time and is therefore computed dynamically. const dataBaseUrl = document.body && document.body.getAttribute('data-base-url'); if (dataBaseUrl) { + // @ts-expect-error undefined global property window.__webpack_public_path__ = `${dataBaseUrl}nbextensions/pydeck/nb_extension`; } // Initialize the transport -const {jupyterTransport} = require('./lib/jupyter-transport').default; - -let JupyterTransportModel = null; -let JupyterTransportView = null; -try { - JupyterTransportModel = require('./lib/jupyter-transport-model').default; - JupyterTransportView = require('./lib/jupyter-transport-view').default; -} catch (err) { - // Note: Happens in the to_html() case -} -const {MODULE_VERSION, MODULE_NAME} = require('./version'); - -// TODO - this should be placed in a separate module `@deck.gl/playground` -const {createDeck, updateDeck} = require('./playground/create-deck'); -const {initPlayground} = require('./playground'); initPlayground(); -module.exports = { +export { // Transports jupyterTransport, - // Jupyter Hooks MODULE_VERSION, MODULE_NAME, JupyterTransportModel, JupyterTransportView, - // For to_html()... initPlayground, // TODO - use playground? diff --git a/modules/jupyter-widget/src/lib/jupyter-transport-model.js b/modules/jupyter-widget/src/lib/jupyter-transport-model.js index 35ef2183f45..8db3f58255d 100644 --- a/modules/jupyter-widget/src/lib/jupyter-transport-model.js +++ b/modules/jupyter-widget/src/lib/jupyter-transport-model.js @@ -1,56 +1,65 @@ -import {DOMWidgetModel} from '@jupyter-widgets/base'; +import * as widgets from '@jupyter-widgets/base'; import {MODULE_NAME, MODULE_VERSION} from '../version'; import {deserializeMatrix} from './utils/deserialize-matrix'; -/** - * - * Note: Variables shared explictly between Python and JavaScript use snake_case - */ -export default class JupyterTransportModel extends DOMWidgetModel { - defaults() { - return { - ...super.defaults(), - _model_name: JupyterTransportModel.model_name, - _model_module: JupyterTransportModel.model_module, - _model_module_version: JupyterTransportModel.model_module_version, - _view_name: JupyterTransportModel.view_name, - _view_module: JupyterTransportModel.view_module, - _view_module_version: JupyterTransportModel.view_module_version, - custom_libraries: [], - json_input: null, - mapbox_key: null, - selected_data: [], - data_buffer: null, - tooltip: null, - width: '100%', - height: 500, - js_warning: false - }; - } - static get serializers() { - return { - ...DOMWidgetModel.serializers, - // Add any extra serializers here - data_buffer: {deserialize: deserializeMatrix} - }; - } +let JupyterTransportModel = null; +const DOMWidgetModel = widgets && widgets.DOMWidgetModel; - static get model_name() { - return 'JupyterTransportModel'; - } - static get model_module() { - return MODULE_NAME; - } - static get model_module_version() { - return MODULE_VERSION; - } - static get view_name() { - return 'JupyterTransportView'; - } - static get view_module() { - return MODULE_NAME; - } - static get view_module_version() { - return MODULE_VERSION; +if (DOMWidgetModel) { + /** + * + * Note: Variables shared explictly between Python and JavaScript use snake_case + */ + class Model extends DOMWidgetModel { + defaults() { + return { + ...super.defaults(), + _model_name: JupyterTransportModel.model_name, + _model_module: JupyterTransportModel.model_module, + _model_module_version: JupyterTransportModel.model_module_version, + _view_name: JupyterTransportModel.view_name, + _view_module: JupyterTransportModel.view_module, + _view_module_version: JupyterTransportModel.view_module_version, + custom_libraries: [], + json_input: null, + mapbox_key: null, + selected_data: [], + data_buffer: null, + tooltip: null, + width: '100%', + height: 500, + js_warning: false + }; + } + + static get serializers() { + return { + ...DOMWidgetModel.serializers, + // Add any extra serializers here + data_buffer: {deserialize: deserializeMatrix} + }; + } + + static get model_name() { + return 'JupyterTransportModel'; + } + static get model_module() { + return MODULE_NAME; + } + static get model_module_version() { + return MODULE_VERSION; + } + static get view_name() { + return 'JupyterTransportView'; + } + static get view_module() { + return MODULE_NAME; + } + static get view_module_version() { + return MODULE_VERSION; + } } + JupyterTransportModel = Model; } + +export default JupyterTransportModel; diff --git a/modules/jupyter-widget/src/lib/jupyter-transport-view.js b/modules/jupyter-widget/src/lib/jupyter-transport-view.js index 106c832842a..3d5068a744f 100644 --- a/modules/jupyter-widget/src/lib/jupyter-transport-view.js +++ b/modules/jupyter-widget/src/lib/jupyter-transport-view.js @@ -1,56 +1,64 @@ -import {DOMWidgetView} from '@jupyter-widgets/base'; +import * as widgets from '@jupyter-widgets/base'; import JupyterTransport from './jupyter-transport'; -export default class JupyterTransportView extends DOMWidgetView { - initialize() { - this.listenTo(this.model, 'destroy', this.remove); +let JupyterTransportView = null; +const DOMWidgetView = widgets && widgets.DOMWidgetView; - // TODO - is there any variable information on the model we can use to - // give an interesting name or id to this instance? - this.transport = new JupyterTransport(); +if (DOMWidgetView) { + class View extends DOMWidgetView { + initialize() { + this.listenTo(this.model, 'destroy', this.remove); - // Expose Jupyter internals to enable work-arounds - this.transport.jupyterModel = this.model; - this.transport.jupyterView = this; - this.transport._initialize(); - super.initialize.apply(this, arguments); - } + // TODO - is there any variable information on the model we can use to + // give an interesting name or id to this instance? + this.transport = new JupyterTransport(); - remove() { - if (this.transport) { - this.transport._finalize(); - this.transport.jupyterModel = null; - this.transport.jupyterView = null; - this.transport = null; + // Expose Jupyter internals to enable work-arounds + this.transport.jupyterModel = this.model; + this.transport.jupyterView = this; + this.transport._initialize(); + super.initialize.apply(this, arguments); } - } - render() { - super.render(); + remove() { + if (this.transport) { + this.transport._finalize(); + this.transport.jupyterModel = null; + this.transport.jupyterView = null; + this.transport = null; + } + } - this.model.on('change:json_input', this.onJsonChanged.bind(this)); - this.model.on('change:data_buffer', this.onDataBufferChanged.bind(this)); + render() { + super.render(); - this.onDataBufferChanged(); - } + this.model.on('change:json_input', this.onJsonChanged.bind(this)); + this.model.on('change:data_buffer', this.onDataBufferChanged.bind(this)); - onJsonChanged() { - const json = JSON.parse(this.model.get('json_input')); - this.transport._messageReceived({type: 'json', json}); - } + this.onDataBufferChanged(); + } - onDataBufferChanged() { - const json = this.model.get('json_input'); - const dataBuffer = this.model.get('data_buffer'); - - if (json && dataBuffer) { - this.transport._messageReceived({ - type: 'json-with-binary', - json, - binary: dataBuffer - }); - } else { + onJsonChanged() { + const json = JSON.parse(this.model.get('json_input')); this.transport._messageReceived({type: 'json', json}); } + + onDataBufferChanged() { + const json = this.model.get('json_input'); + const dataBuffer = this.model.get('data_buffer'); + + if (json && dataBuffer) { + this.transport._messageReceived({ + type: 'json-with-binary', + json, + binary: dataBuffer + }); + } else { + this.transport._messageReceived({type: 'json', json}); + } + } } + JupyterTransportView = View; } + +export default JupyterTransportView; diff --git a/modules/jupyter-widget/src/playground/create-deck.js b/modules/jupyter-widget/src/playground/create-deck.js index f63de637424..8dd6f688898 100644 --- a/modules/jupyter-widget/src/playground/create-deck.js +++ b/modules/jupyter-widget/src/playground/create-deck.js @@ -13,7 +13,7 @@ import {createGoogleMapsDeckOverlay} from './utils/google-maps-utils'; import {addSupportComponents} from '../lib/components/index'; -import * as deck from '../deck-bundle'; +import deck from '../deck-bundle'; const classesFilter = x => x.charAt(0) === x.charAt(0).toUpperCase(); const functionsFilter = x => x.charAt(0) === x.charAt(0).toLowerCase() && x.charAt(0) != '_'; diff --git a/modules/jupyter-widget/src/playground/playground.js b/modules/jupyter-widget/src/playground/playground.js index fe3ca380f1d..429acde709c 100644 --- a/modules/jupyter-widget/src/playground/playground.js +++ b/modules/jupyter-widget/src/playground/playground.js @@ -83,6 +83,7 @@ export function processDataBuffer({binary, convertedJson}) { // Filters circular references on JSON string conversion function filterJsonValue(key, value) { + // eslint-disable-next-line return value instanceof deckBundle.Layer ? value.id : value; } diff --git a/modules/jupyter-widget/src/playground/utils/google-maps-utils.js b/modules/jupyter-widget/src/playground/utils/google-maps-utils.js index cc1ce24b237..70df5997106 100644 --- a/modules/jupyter-widget/src/playground/utils/google-maps-utils.js +++ b/modules/jupyter-widget/src/playground/utils/google-maps-utils.js @@ -1,5 +1,5 @@ /* global window */ -import * as deck from '../../deck-bundle'; +import deck from '../../deck-bundle'; export function createGoogleMapsDeckOverlay({ container, diff --git a/modules/jupyter-widget/src/playground/utils/mapbox-utils.js b/modules/jupyter-widget/src/playground/utils/mapbox-utils.js index 812026c998c..8436ec94ef2 100644 --- a/modules/jupyter-widget/src/playground/utils/mapbox-utils.js +++ b/modules/jupyter-widget/src/playground/utils/mapbox-utils.js @@ -1,16 +1,9 @@ -/* global process, document */ +/* global document */ import {loadCSS} from './css-utils'; -// SSR safe import (ensures this file can be imported under Node.js e.g. for tests) -// From https://github.com/mapbox/mapbox-gl-js/issues/4593#issuecomment-546290823 -// eslint-disable-next-line no-undef -let mapboxgl; +import mapboxgl from 'mapbox-gl'; -if (process.browser) { - mapboxgl = require('mapbox-gl'); -} - -const MAPBOX_CSS_URL = 'https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.1/mapbox-gl.css'; +const MAPBOX_CSS_URL = 'https://api.tiles.mapbox.com/mapbox-gl-js/v1.13.2/mapbox-gl.css'; export default mapboxgl; diff --git a/test/modules/index.js b/test/modules/index.js index 267e4592c00..a2c90623e7b 100644 --- a/test/modules/index.js +++ b/test/modules/index.js @@ -31,5 +31,5 @@ import './google-maps'; import './mapbox'; import './json'; import './react'; -// import './jupyter-widget'; +import './jupyter-widget'; import './extensions'; diff --git a/test/modules/jupyter-widget/dummy-jupyter-widgets-base.js b/test/modules/jupyter-widget/dummy-jupyter-widgets-base.js new file mode 100644 index 00000000000..c3cbea62d41 --- /dev/null +++ b/test/modules/jupyter-widget/dummy-jupyter-widgets-base.js @@ -0,0 +1,13 @@ +// Dummy exports for node tests +import * as Backbone from 'backbone'; + +export class ManagerBase {} +export class DOMWidgetModel extends Backbone.Model { + defaults() { + return {}; + } +} +export class DOMWidgetView {} +export function uuid() { + return 'uuid'; +} diff --git a/test/modules/jupyter-widget/dummy-mapbox-gl.js b/test/modules/jupyter-widget/dummy-mapbox-gl.js new file mode 100644 index 00000000000..e1d5c0f2249 --- /dev/null +++ b/test/modules/jupyter-widget/dummy-mapbox-gl.js @@ -0,0 +1,2 @@ +// Dummy exports for node tests +export default {}; diff --git a/test/modules/jupyter-widget/index.js b/test/modules/jupyter-widget/index.js index 4ca08b44c9c..189acc71197 100644 --- a/test/modules/jupyter-widget/index.js +++ b/test/modules/jupyter-widget/index.js @@ -1,10 +1,6 @@ -try { - require('./binary-transport.spec'); - require('./create-deck.spec'); - require('./index.spec'); - require('./widget-tooltip.spec'); - require('./utils/google-maps-utils.spec'); -} catch (err) { - // eslint-disable-next-line no-console,no-undef - console.log('Skipping browser tests'); -} +import './binary-transport.spec'; +import './create-deck.spec'; +import './widget-tooltip.spec'; +import './utils/google-maps-utils.spec'; + +import './index.spec'; diff --git a/test/modules/jupyter-widget/mock-widget-base.js b/test/modules/jupyter-widget/mock-widget-base.js index b46172a0dd3..1e892359353 100644 --- a/test/modules/jupyter-widget/mock-widget-base.js +++ b/test/modules/jupyter-widget/mock-widget-base.js @@ -1,5 +1,5 @@ /* global window*/ -const widgets = require('@jupyter-widgets/base'); +import * as widgets from '@jupyter-widgets/base'; // Adapted from https://github.com/jupyter-widgets/widget-ts-cookiecutter/blob/f8c4f1d2c61374811423eb724b9eb11c6e2f10b1/%7B%7Bcookiecutter.github_project_name%7D%7D/src/__tests__/utils.ts#L106 export class DummyManager extends widgets.ManagerBase { diff --git a/test/render/index.js b/test/render/index.js index 6d1570f7a16..97d97d2954c 100644 --- a/test/render/index.js +++ b/test/render/index.js @@ -22,7 +22,7 @@ import TEST_CASES from './test-cases'; import {WIDTH, HEIGHT} from './constants'; import {SnapshotTestRunner} from '@deck.gl/test-utils'; -// import './jupyter-widget'; +import './jupyter-widget'; test('Render Test', t => { // tape's default timeout is 500ms