diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6014ed3a..01565a95 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,6 +36,7 @@ jobs: - signals - state - toolbar-button + - toparea-text-widget - widgets os: [ubuntu-latest, macos-latest, windows-latest] diff --git a/README.md b/README.md index e28e0c37..ee6e9ac7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ 1. [Signals](#signals) 1. [State](#state) 1. [Toolbar Item](#toolbar-item) + 1. [Top Area Text Widget](#top-area-text-widget) 1. [Widgets](#widgets) 1. [Prerequisites](#prerequisites) 1. [Develop and Use the Examples](#develop-and-use-the-examples) @@ -116,6 +117,7 @@ Start with the [Hello World](hello-world) and then jump to the topic you are int - [Signals](signals) - [State](state) - [Toolbar item](toolbar-button) +- [Top Area Text Widget](toparea-text-widget) - [Widgets](widgets) You can expect from each example: @@ -225,7 +227,7 @@ Add user interface to edit cell or notebook metadata. [![Metadata Form](metadata-form/preview.gif)](metadata-form) -## [MIME Renderer](mimerenderer) +### [MIME Renderer](mimerenderer) Add a MIME renderer for mp4 content to the application. @@ -273,6 +275,13 @@ Add a new button to the notebook toolbar. [![Toolbar button](toolbar-button/preview.gif)](toolbar-button) +### [Top Area Text Widget](toparea-text-widget) + +A very simple example that adds a basic text widget to the top area. See [related video.](https://www.youtube.com/watch?v=mqotG1MkHa4). +This example is part of the [Extension Dual Compatibility Guide](https://jupyterlab.readthedocs.io/en/latest/extension_dual_compatibility.html). + +[![Top Area Text Widget](toparea-text-widget/preview.jpg)](toparea-text-widget) + ### [Widgets](widgets) Add a new Widget element to the main window. diff --git a/package.json b/package.json index 12a3a91e..1380b078 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "signals", "state", "toolbar-button", + "toparea-text-widget", "widgets" ], "devDependencies": { diff --git a/toparea-text-widget/.copier-answers.yml b/toparea-text-widget/.copier-answers.yml new file mode 100644 index 00000000..3bb40024 --- /dev/null +++ b/toparea-text-widget/.copier-answers.yml @@ -0,0 +1,19 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: v4.2.4 +_src_path: https://github.com/jupyterlab/extension-template +author_email: '' +author_name: Project Jupyter Contributors +data_format: string +file_extension: '' +has_binder: true +has_settings: false +kind: frontend +labextension_name: '@jupyterlab-examples/toparea' +mimetype: '' +mimetype_name: '' +project_short_description: A JupyterLab extension to add text in the top area. +python_name: jupyterlab_examples_toparea +repository: https://github.com/jupyterlab/extension-examples +test: false +viewer_name: '' + diff --git a/toparea-text-widget/.gitignore b/toparea-text-widget/.gitignore new file mode 100644 index 00000000..b7fccf13 --- /dev/null +++ b/toparea-text-widget/.gitignore @@ -0,0 +1,121 @@ +*.bundle.* +lib/ +node_modules/ +*.log +.eslintcache +.stylelintcache +*.egg-info/ +.ipynb_checkpoints +*.tsbuildinfo +jupyterlab_examples_toparea/labextension +# Version file is handled by hatchling +jupyterlab_examples_toparea/_version.py + +# Created by https://www.gitignore.io/api/python +# Edit at https://www.gitignore.io/?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage/ +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# End of https://www.gitignore.io/api/python + +# OSX files +.DS_Store + +# Yarn cache +.yarn/ diff --git a/toparea-text-widget/.prettierignore b/toparea-text-widget/.prettierignore new file mode 100644 index 00000000..1f176908 --- /dev/null +++ b/toparea-text-widget/.prettierignore @@ -0,0 +1,6 @@ +node_modules +**/node_modules +**/lib +**/package.json +!/package.json +jupyterlab_examples_toparea diff --git a/toparea-text-widget/.yarnrc.yml b/toparea-text-widget/.yarnrc.yml new file mode 100644 index 00000000..3186f3f0 --- /dev/null +++ b/toparea-text-widget/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/toparea-text-widget/README.md b/toparea-text-widget/README.md new file mode 100644 index 00000000..fb3035c6 --- /dev/null +++ b/toparea-text-widget/README.md @@ -0,0 +1,41 @@ +# Top area widget (cross compatible extension) + +This example defines an extension that adds a single self-contained text widget +to the top bar of the UI. + +![preview](./preview.jpg) + +## Jupyter Notebook / JupyterLab compatibility + +As Jupyter Notebook 7+ is built with components from JupyterLab, and since +both use the same building blocks, that means your extension can work +on both (or any other frontend built with JupyterLab components) with +little or no modification depending on its design. + +This extension doesn't need to do anything at all to be compatible +with both JupyterLab and Notebook 7+ (both apps have a top area that can hold the +widget, so it will be visible in both JupyterLab and Notebook 7+ upon install and +after launch). This will be the case if your extension only uses features +that both applications have. + +## Adding a widget to the top area + +You can add a widget to the top area by calling the following +method of the application shell: + +```ts +// src/index.ts#L34-L34 + +app.shell.add(widget, 'top', { rank: 1000 }); +``` + +The _rank_ is used to order the widget within the top area. + +## Where to Go Next + +If your extension is using features that are not common to both interfaces, +special steps needs to be taken. You can have a look at the [Shout button example](../shout-button-message) +that uses a feature available in JupyterLab but not in Notebook 7+. + +You can have more information about this in the +[Extension Dual Compatibility Guide](https://jupyterlab.readthedocs.io/en/latest/extension_dual_compatibility.html). diff --git a/toparea-text-widget/install.json b/toparea-text-widget/install.json new file mode 100644 index 00000000..c228f499 --- /dev/null +++ b/toparea-text-widget/install.json @@ -0,0 +1,5 @@ +{ + "packageManager": "python", + "packageName": "jupyterlab_examples_toparea", + "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package jupyterlab_examples_toparea" +} diff --git a/toparea-text-widget/jupyterlab_examples_toparea/__init__.py b/toparea-text-widget/jupyterlab_examples_toparea/__init__.py new file mode 100644 index 00000000..a31f262d --- /dev/null +++ b/toparea-text-widget/jupyterlab_examples_toparea/__init__.py @@ -0,0 +1,16 @@ +try: + from ._version import __version__ +except ImportError: + # Fallback when using the package in dev mode without installing + # in editable mode with pip. It is highly recommended to install + # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs + import warnings + warnings.warn("Importing 'jupyterlab_examples_toparea' outside a proper installation.") + __version__ = "dev" + + +def _jupyter_labextension_paths(): + return [{ + "src": "labextension", + "dest": "@jupyterlab-examples/toparea" + }] diff --git a/toparea-text-widget/package.json b/toparea-text-widget/package.json new file mode 100644 index 00000000..b3cb21c8 --- /dev/null +++ b/toparea-text-widget/package.json @@ -0,0 +1,188 @@ +{ + "name": "@jupyterlab-examples/toparea", + "version": "0.1.0", + "description": "A JupyterLab extension to add text in the top area.", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/jupyterlab/extension-examples", + "bugs": { + "url": "https://github.com/jupyterlab/extension-examples/issues" + }, + "license": "BSD-3-Clause", + "author": "Project Jupyter Contributors", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "repository": { + "type": "git", + "url": "https://github.com/jupyterlab/extension-examples.git" + }, + "scripts": { + "build": "jlpm build:lib && jlpm build:labextension:dev", + "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", + "build:labextension": "jupyter labextension build .", + "build:labextension:dev": "jupyter labextension build --development True .", + "build:lib": "tsc --sourceMap", + "build:lib:prod": "tsc", + "clean": "jlpm clean:lib", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", + "clean:lintcache": "rimraf .eslintcache .stylelintcache", + "clean:labextension": "rimraf jupyterlab_examples_toparea/labextension jupyterlab_examples_toparea/_version.py", + "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", + "eslint": "jlpm eslint:check --fix", + "eslint:check": "eslint . --cache --ext .ts,.tsx", + "install:extension": "jlpm build", + "lint": "jlpm stylelint && jlpm prettier && jlpm eslint", + "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check", + "prettier": "jlpm prettier:base --write --list-different", + "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", + "prettier:check": "jlpm prettier:base --check", + "stylelint": "jlpm stylelint:check --fix", + "stylelint:check": "stylelint --cache \"style/**/*.css\"", + "watch": "run-p watch:src watch:labextension", + "watch:src": "tsc -w --sourceMap", + "watch:labextension": "jupyter labextension watch ." + }, + "dependencies": { + "@jupyterlab/application": "^4.0.0", + "@jupyterlab/apputils": "^4.0.0", + "@lumino/widgets": "^2.0.0" + }, + "devDependencies": { + "@jupyterlab/builder": "^4.0.0", + "@types/json-schema": "^7.0.11", + "@types/react": "^18.0.26", + "@types/react-addons-linked-state-mixin": "^0.14.22", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", + "css-loader": "^6.7.1", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^5.0.1", + "source-map-loader": "^1.0.2", + "style-loader": "^3.3.1", + "stylelint": "^15.10.1", + "stylelint-config-recommended": "^13.0.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-csstree-validator": "^3.0.0", + "stylelint-prettier": "^4.0.0", + "typescript": "~5.0.2", + "yjs": "^13.5.0" + }, + "sideEffects": [ + "style/*.css", + "style/index.js" + ], + "styleModule": "style/index.js", + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "extension": true, + "outputDir": "jupyterlab_examples_toparea/labextension" + }, + "eslintIgnore": [ + "node_modules", + "dist", + "coverage", + "**/*.d.ts", + "tests", + "**/__tests__", + "ui-tests" + ], + "eslintConfig": { + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "interface", + "format": [ + "PascalCase" + ], + "custom": { + "regex": "^I[A-Z]", + "match": true + } + } + ], + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "none" + } + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": false + } + ], + "curly": [ + "error", + "all" + ], + "eqeqeq": "error", + "prefer-arrow-callback": "error" + } + }, + "prettier": { + "singleQuote": true, + "trailingComma": "none", + "arrowParens": "avoid", + "endOfLine": "auto", + "overrides": [ + { + "files": "package.json", + "options": { + "tabWidth": 4 + } + } + ] + }, + "stylelint": { + "extends": [ + "stylelint-config-recommended", + "stylelint-config-standard", + "stylelint-prettier/recommended" + ], + "plugins": [ + "stylelint-csstree-validator" + ], + "rules": { + "csstree/validator": true, + "property-no-vendor-prefix": null, + "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$", + "selector-no-vendor-prefix": null, + "value-no-vendor-prefix": null + } + } +} diff --git a/toparea-text-widget/preview.jpg b/toparea-text-widget/preview.jpg new file mode 100644 index 00000000..38859df1 Binary files /dev/null and b/toparea-text-widget/preview.jpg differ diff --git a/toparea-text-widget/pyproject.toml b/toparea-text-widget/pyproject.toml new file mode 100644 index 00000000..9a2b92ea --- /dev/null +++ b/toparea-text-widget/pyproject.toml @@ -0,0 +1,77 @@ +[build-system] +requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0.3.2"] +build-backend = "hatchling.build" + +[project] +name = "jupyterlab_examples_toparea" +readme = "README.md" +license = {text = "BSD-3-Clause License"} +requires-python = ">=3.8" +classifiers = [ + "Framework :: Jupyter", + "Framework :: Jupyter :: JupyterLab", + "Framework :: Jupyter :: JupyterLab :: 4", + "Framework :: Jupyter :: JupyterLab :: Extensions", + "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ +] +dynamic = ["version", "description", "authors", "urls", "keywords"] + +[tool.hatch.version] +source = "nodejs" + +[tool.hatch.metadata.hooks.nodejs] +fields = ["description", "authors", "urls"] + +[tool.hatch.build.targets.sdist] +artifacts = ["jupyterlab_examples_toparea/labextension"] +exclude = [".github", "binder"] + +[tool.hatch.build.targets.wheel.shared-data] +"jupyterlab_examples_toparea/labextension" = "share/jupyter/labextensions/@jupyterlab-examples/toparea" +"install.json" = "share/jupyter/labextensions/@jupyterlab-examples/toparea/install.json" + +[tool.hatch.build.hooks.version] +path = "jupyterlab_examples_toparea/_version.py" + +[tool.hatch.build.hooks.jupyter-builder] +dependencies = ["hatch-jupyter-builder>=0.5"] +build-function = "hatch_jupyter_builder.npm_builder" +ensured-targets = [ + "jupyterlab_examples_toparea/labextension/static/style.js", + "jupyterlab_examples_toparea/labextension/package.json", +] +skip-if-exists = ["jupyterlab_examples_toparea/labextension/static/style.js"] + +[tool.hatch.build.hooks.jupyter-builder.build-kwargs] +build_cmd = "build:prod" +npm = ["jlpm"] + +[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs] +build_cmd = "install:extension" +npm = ["jlpm"] +source_dir = "src" +build_dir = "jupyterlab_examples_toparea/labextension" + +[tool.jupyter-releaser.options] +version_cmd = "hatch version" + +[tool.jupyter-releaser.hooks] +before-build-npm = [ + "python -m pip install 'jupyterlab>=4.0.0,<5'", + "jlpm", + "jlpm build:prod" +] +before-build-python = ["jlpm clean:all"] + +[tool.check-wheel-contents] +ignore = ["W002"] diff --git a/toparea-text-widget/setup.py b/toparea-text-widget/setup.py new file mode 100644 index 00000000..aefdf20d --- /dev/null +++ b/toparea-text-widget/setup.py @@ -0,0 +1 @@ +__import__("setuptools").setup() diff --git a/toparea-text-widget/src/index.ts b/toparea-text-widget/src/index.ts new file mode 100644 index 00000000..09b1036d --- /dev/null +++ b/toparea-text-widget/src/index.ts @@ -0,0 +1,38 @@ +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { DOMUtils } from '@jupyterlab/apputils'; + +import { Widget } from '@lumino/widgets'; + +const TOP_AREA_CSS_CLASS = 'jp-TopAreaText'; + +/** + * Initialization data for the @jupyterlab-examples/toparea extension. + */ +const plugin: JupyterFrontEndPlugin = { + id: '@jupyterlab-examples/toparea:plugin', + description: 'A JupyterLab extension to add text in the top area.', + autoStart: true, + activate: (app: JupyterFrontEnd) => { + console.log( + 'JupyterLab extension @jupyterlab-examples/toparea is activated!' + ); + + // Create the HTML content of the widget + const node = document.createElement('div'); + node.textContent = 'Hello World'; + + // Create the widget + const widget = new Widget({ node }); + widget.id = DOMUtils.createDomID(); + widget.addClass(TOP_AREA_CSS_CLASS); + + // Add the widget to the top area + app.shell.add(widget, 'top', { rank: 1000 }); + } +}; + +export default plugin; diff --git a/toparea-text-widget/style/base.css b/toparea-text-widget/style/base.css new file mode 100644 index 00000000..1a282a21 --- /dev/null +++ b/toparea-text-widget/style/base.css @@ -0,0 +1,13 @@ +/* + See the JupyterLab Developer Guide for useful CSS Patterns: + + https://jupyterlab.readthedocs.io/en/stable/developer/css.html +*/ + +/* stylelint-disable selector-class-pattern */ +.jp-TopAreaText { + display: flex; + justify-content: center; + align-items: center; + margin-right: 10px; +} diff --git a/toparea-text-widget/style/index.css b/toparea-text-widget/style/index.css new file mode 100644 index 00000000..8a7ea29e --- /dev/null +++ b/toparea-text-widget/style/index.css @@ -0,0 +1 @@ +@import url('base.css'); diff --git a/toparea-text-widget/style/index.js b/toparea-text-widget/style/index.js new file mode 100644 index 00000000..a028a764 --- /dev/null +++ b/toparea-text-widget/style/index.js @@ -0,0 +1 @@ +import './base.css'; diff --git a/toparea-text-widget/tsconfig.json b/toparea-text-widget/tsconfig.json new file mode 100644 index 00000000..98979175 --- /dev/null +++ b/toparea-text-widget/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, + "declaration": true, + "esModuleInterop": true, + "incremental": true, + "jsx": "react", + "module": "esnext", + "moduleResolution": "node", + "noEmitOnError": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "preserveWatchOutput": true, + "resolveJsonModule": true, + "outDir": "lib", + "rootDir": "src", + "strict": true, + "strictNullChecks": true, + "target": "ES2018" + }, + "include": ["src/*"] +} diff --git a/toparea-text-widget/ui-tests/README.md b/toparea-text-widget/ui-tests/README.md new file mode 100644 index 00000000..dbe6e8aa --- /dev/null +++ b/toparea-text-widget/ui-tests/README.md @@ -0,0 +1,167 @@ +# Integration Testing + +This folder contains the integration tests of the extension. + +They are defined using [Playwright](https://playwright.dev/docs/intro) test runner +and [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) helper. + +The Playwright configuration is defined in [playwright.config.js](./playwright.config.js). + +The JupyterLab server configuration to use for the integration test is defined +in [jupyter_server_test_config.py](./jupyter_server_test_config.py). + +The default configuration will produce video for failing tests and an HTML report. + +> There is a new experimental UI mode that you may fall in love with; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0). + +## Run the tests + +> All commands are assumed to be executed from the root directory + +To run the tests, you need to: + +1. Compile the extension: + +```sh +jlpm install +jlpm build:prod +``` + +> Check the extension is installed in JupyterLab. + +2. Install test dependencies (needed only once): + +```sh +cd ./ui-tests +jlpm install +jlpm playwright install +cd .. +``` + +3. Execute the [Playwright](https://playwright.dev/docs/intro) tests: + +```sh +cd ./ui-tests +jlpm playwright test +``` + +Test results will be shown in the terminal. In case of any test failures, the test report +will be opened in your browser at the end of the tests execution; see +[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter) +for configuring that behavior. + +## Update the tests snapshots + +> All commands are assumed to be executed from the root directory + +If you are comparing snapshots to validate your tests, you may need to update +the reference snapshots stored in the repository. To do that, you need to: + +1. Compile the extension: + +```sh +jlpm install +jlpm build:prod +``` + +> Check the extension is installed in JupyterLab. + +2. Install test dependencies (needed only once): + +```sh +cd ./ui-tests +jlpm install +jlpm playwright install +cd .. +``` + +3. Execute the [Playwright](https://playwright.dev/docs/intro) command: + +```sh +cd ./ui-tests +jlpm playwright test -u +``` + +> Some discrepancy may occurs between the snapshots generated on your computer and +> the one generated on the CI. To ease updating the snapshots on a PR, you can +> type `please update playwright snapshots` to trigger the update by a bot on the CI. +> Once the bot has computed new snapshots, it will commit them to the PR branch. + +## Create tests + +> All commands are assumed to be executed from the root directory + +To create tests, the easiest way is to use the code generator tool of playwright: + +1. Compile the extension: + +```sh +jlpm install +jlpm build:prod +``` + +> Check the extension is installed in JupyterLab. + +2. Install test dependencies (needed only once): + +```sh +cd ./ui-tests +jlpm install +jlpm playwright install +cd .. +``` + +3. Start the server: + +```sh +cd ./ui-tests +jlpm start +``` + +4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**: + +```sh +cd ./ui-tests +jlpm playwright codegen localhost:8888 +``` + +## Debug tests + +> All commands are assumed to be executed from the root directory + +To debug tests, a good way is to use the inspector tool of playwright: + +1. Compile the extension: + +```sh +jlpm install +jlpm build:prod +``` + +> Check the extension is installed in JupyterLab. + +2. Install test dependencies (needed only once): + +```sh +cd ./ui-tests +jlpm install +jlpm playwright install +cd .. +``` + +3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug): + +```sh +cd ./ui-tests +jlpm playwright test --debug +``` + +## Upgrade Playwright and the browsers + +To update the web browser versions, you must update the package `@playwright/test`: + +```sh +cd ./ui-tests +jlpm up "@playwright/test" +jlpm playwright install +``` diff --git a/toparea-text-widget/ui-tests/jupyter_server_test_config.py b/toparea-text-widget/ui-tests/jupyter_server_test_config.py new file mode 100644 index 00000000..f2a94782 --- /dev/null +++ b/toparea-text-widget/ui-tests/jupyter_server_test_config.py @@ -0,0 +1,12 @@ +"""Server configuration for integration tests. + +!! Never use this configuration in production because it +opens the server to the world and provide access to JupyterLab +JavaScript objects through the global window variable. +""" +from jupyterlab.galata import configure_jupyter_server + +configure_jupyter_server(c) + +# Uncomment to set server log level to debug level +# c.ServerApp.log_level = "DEBUG" diff --git a/toparea-text-widget/ui-tests/package.json b/toparea-text-widget/ui-tests/package.json new file mode 100644 index 00000000..947cd072 --- /dev/null +++ b/toparea-text-widget/ui-tests/package.json @@ -0,0 +1,15 @@ +{ + "name": "jupyterlab_examples_toparea-ui-tests", + "version": "1.0.0", + "description": "JupyterLab jupyterlab_examples_toparea Integration Tests", + "private": true, + "scripts": { + "start": "jupyter lab --config jupyter_server_test_config.py", + "test": "jlpm playwright test", + "test:update": "jlpm playwright test --update-snapshots" + }, + "devDependencies": { + "@jupyterlab/galata": "^5.0.5", + "@playwright/test": "^1.37.0" + } +} diff --git a/toparea-text-widget/ui-tests/playwright.config.js b/toparea-text-widget/ui-tests/playwright.config.js new file mode 100644 index 00000000..9ece6fa1 --- /dev/null +++ b/toparea-text-widget/ui-tests/playwright.config.js @@ -0,0 +1,14 @@ +/** + * Configuration for Playwright using default from @jupyterlab/galata + */ +const baseConfig = require('@jupyterlab/galata/lib/playwright-config'); + +module.exports = { + ...baseConfig, + webServer: { + command: 'jlpm start', + url: 'http://localhost:8888/lab', + timeout: 120 * 1000, + reuseExistingServer: !process.env.CI + } +}; diff --git a/toparea-text-widget/ui-tests/tests/toparea.spec.ts b/toparea-text-widget/ui-tests/tests/toparea.spec.ts new file mode 100644 index 00000000..26e31eb7 --- /dev/null +++ b/toparea-text-widget/ui-tests/tests/toparea.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from '@jupyterlab/galata'; + +test('should add a top area text', async ({ page }) => { + await expect(page.locator('.jp-TopAreaText')).toHaveText('Hello World'); +}); diff --git a/toparea-text-widget/ui-tests/yarn.lock b/toparea-text-widget/ui-tests/yarn.lock new file mode 100644 index 00000000..e69de29b