diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 7b6eed9..e97fe5c 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -7,6 +7,8 @@ on: pull_request: branches: - master + schedule: + - cron: '0 23 * * *' jobs: build: @@ -35,3 +37,7 @@ jobs: - name: Test shell: bash run: yarn test + + - name: Generate and Build Templates + shell: bash + run: ./.github/workflows/generate-and-build-all-templates.sh diff --git a/.github/workflows/generate-and-build-all-templates.sh b/.github/workflows/generate-and-build-all-templates.sh new file mode 100755 index 0000000..858ce8b --- /dev/null +++ b/.github/workflows/generate-and-build-all-templates.sh @@ -0,0 +1,14 @@ +npm install -g yo +npm link +mkdir tmp +cd tmp +exit_code=0 +for d in ../templates/*/ ; do + extension=$(basename $d) + mkdir $extension + cd $extension + yo theia-extension $extension -y $extension || exit_code=$? + cd .. +done +cd .. +exit $exit_code diff --git a/README.md b/README.md index a84c805..f242707 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,17 @@
theia logo -

THEIA - EXTENSION GENERATOR

+

ECLIPSE THEIA - EXTENSION GENERATOR

+ + [![Build](https://github.com/theia-ide/generator-theia-extension/workflows/Build/badge.svg?branch=master)](https://github.com/theia-ide/generator-theia-extension/actions?query=branch%3Amaster) +![npm](https://img.shields.io/npm/v/generator-theia-extension?color=blue) + +
+ +A [yeoman](https://yeoman.io/) generator that scaffolds a project structure for developing [Eclipse Theia](https://github.com/eclipse-theia/theia) extensions. -A [Yeoman](yeoman.io) generator that scaffolds a project structure for developing extensions to the [Theia IDE](https://github.com/theia-ide/theia).
@@ -28,18 +34,25 @@ mkdir my-extension && cd my-extension yo theia-extension ``` -Extension options -1. `hello-world`: creates a simple extension which provides a command and menu item which displays a message. -2. `widget`: creates the basis for a simple widget including a toggle command, alert message and button displaying a message. -3. `labelprovider`: create a simple extension which adds a custom label (with icon) for `.my` files. -4. `tree-editor`: create a tree editor extension. - -For configuration options, see +For configuration options, see: ``` yo theia-extension --help ``` +## Extension Options + + +| Template Option | Description | Documentation | +|:---|:---|:---| +| `hello-world` | Creates a simple extension which provides a command and menu item which displays a message | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/hello-world/README.md) | +| `widget` | Creates the basis for a simple widget including a toggle command, alert message and button displaying a message. The template also contains an example unit test. | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/widget/README.md) | +| `labelprovider` | Creates a simple extension which adds a custom label (with icon) for .my files | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/labelprovider/README.md) | +| `tree-editor` | Creates a tree editor extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/tree-editor/README.md) | +| `empty` | Creates a simple, minimal extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/empty/README.md) | +| `backend` | Creates a backend communication extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/backend/README.md) | + + ## Publishing diff --git a/package.json b/package.json index 6c0e4e4..10bd68d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-theia-extension", - "version": "0.1.21", + "version": "0.1.25", "description": "Helps to setup the project structure for developing extensions to the Theia IDE", "repository": { "type": "git", diff --git a/src/app/index.ts b/src/app/index.ts index 87ed555..1a65ef8 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -46,6 +46,11 @@ module.exports = class TheiaExtension extends Base { skipInstall: boolean standalone: boolean dependencies: string + browserDevDependencies: string + devdependencies: string + scripts: string + rootscripts: string + containsTests: boolean }; constructor(args: string | string[], options: any) { @@ -147,7 +152,7 @@ module.exports = class TheiaExtension extends Base { message: "The extension's type", choices: [ { value: ExtensionType.HelloWorld, name: 'Hello World' }, - { value: ExtensionType.Widget, name: 'Widget' }, + { value: ExtensionType.Widget, name: 'Widget (with unit tests)' }, { value: ExtensionType.LabelProvider, name: 'LabelProvider' }, { value: ExtensionType.TreeEditor, name: 'TreeEditor' }, { value: ExtensionType.Backend, name: 'Backend Communication' }, @@ -192,10 +197,17 @@ module.exports = class TheiaExtension extends Base { lernaVersion: options["lerna-version"], backend: options["extensionType"] === ExtensionType.Backend } + this.params.dependencies = ''; + this.params.browserDevDependencies = ''; if (this.params.extensionType === ExtensionType.TreeEditor) { this.params.dependencies = `,\n "@theia/editor": "${this.params.theiaVersion}",\n "@theia/filesystem": "${this.params.theiaVersion}",\n "@theia/workspace": "${this.params.theiaVersion}",\n "@eclipse-emfcloud/theia-tree-editor": "latest",\n "uuid": "^3.3.2"`; - } else { - this.params.dependencies = ''; + this.params.browserDevDependencies = `,\n "node-polyfill-webpack-plugin": "latest"`; + } + if (this.params.extensionType === ExtensionType.Widget) { + this.params.devdependencies = `,\n "@testing-library/react": "^11.2.7",\n "@types/jest": "^26.0.20",\n "jest": "^26.6.3",\n "ts-node": "^9.1.1",\n "ts-jest": "^26.5.6"`; + this.params.scripts = `,\n "test": "jest --config configs/jest.config.ts"`; + this.params.rootscripts =`,\n "test": "cd ${this.params.extensionPath} && yarn test"`; + this.params.containsTests = true; } options.params = this.params if (!options.standalone) { @@ -317,7 +329,17 @@ module.exports = class TheiaExtension extends Base { this.extensionPath('README.md'), { params: this.params } ); - } + this.fs.copyTpl( + this.templatePath('widget/widget.test.ts'), + this.extensionPath(`src/browser/${this.params.extensionPath}-widget.test.ts`), + { params: this.params } + ); + this.fs.copyTpl( + this.templatePath('widget/configs/jest.config.ts'), + this.extensionPath(`configs/jest.config.ts`), + { params: this.params } + ); + } /** backend */ if (this.params.extensionType === ExtensionType.Backend) { @@ -409,7 +431,13 @@ module.exports = class TheiaExtension extends Base { install() { if (!(this.options as any).skipInstall) { - this.spawnCommand('yarn', []); + var command = this.spawnCommand('yarn', []); + + command.on('close', function(code: number){ + if (code !== 0 ) { + process.exit(code); + } + }) } } @@ -417,3 +445,5 @@ module.exports = class TheiaExtension extends Base { return name.substring(0, 1).toUpperCase() + name.substring(1) } } + +module.exports.ExtensionType = ExtensionType; \ No newline at end of file diff --git a/src/browser/index.ts b/src/browser/index.ts index c8aea96..0c0de9d 100644 --- a/src/browser/index.ts +++ b/src/browser/index.ts @@ -23,13 +23,21 @@ module.exports = class TheiaBrowser extends Base { } writing() { + const params = (this.options as any).params this.fs.copyTpl( this.templatePath('app-browser-package.json'), this.destinationPath('browser-app/package.json'), { appMode: 'browser', - params: (this.options as any).params + params } ); + if (params.extensionType === 'tree-editor') { + this.fs.copyTpl( + this.templatePath('app-browser-webpack-config.js'), + this.destinationPath('browser-app/webpack.config.js'), + {} + ); + } } } diff --git a/templates/README.md b/templates/README.md index cad29df..64e0e4e 100644 --- a/templates/README.md +++ b/templates/README.md @@ -41,6 +41,18 @@ Open http://localhost:3000 in the browser. yarn start *or:* launch `Start Electron Backend` configuration from VS code. + +<%if(params.containsTests){%> +## Running the tests + + yarn test + +*or* run the tests of a specific package with + + cd <%= params.extensionPath %> + yarn test + +<%}%> ## Developing with the browser example Start watching all packages, including `browser-app`, of your application with diff --git a/templates/app-browser-package.json b/templates/app-browser-package.json index b521b78..5a6d3f1 100644 --- a/templates/app-browser-package.json +++ b/templates/app-browser-package.json @@ -17,7 +17,7 @@ "<%= params.extensionName %>": "<%= params.version %>" }, "devDependencies": { - "@theia/cli": "<%= params.theiaVersion %>" + "@theia/cli": "<%= params.theiaVersion %>"<% if (params.browserDevDependencies) { %><%- params.browserDevDependencies %><% } %> }, "scripts": { "prepare": "theia build --mode development", diff --git a/templates/app-browser-webpack-config.js b/templates/app-browser-webpack-config.js new file mode 100644 index 0000000..72861ce --- /dev/null +++ b/templates/app-browser-webpack-config.js @@ -0,0 +1,21 @@ +/** + * This file can be edited to customize webpack configuration. + * To reset delete this file and rerun theia build again. + */ +// @ts-check +const config = require('./gen-webpack.config.js'); + +/** + * Expose bundled modules on window.theia.moduleName namespace, e.g. + * window['theia']['@theia/core/lib/common/uri']. + * Such syntax can be used by external code, for instance, for testing. +config.module.rules.push({ + test: /\.js$/, + loader: require.resolve('@theia/application-manager/lib/expose-loader') +}); */ + +// Load node polyfills as they are no longer automatically included with webpack 5. +const NodePolyfillPlugin = require("node-polyfill-webpack-plugin") +config.plugins.push(new NodePolyfillPlugin()); + +module.exports = config; diff --git a/templates/backend/backend-module.ts b/templates/backend/backend-module.ts index 9886856..7e6d983 100644 --- a/templates/backend/backend-module.ts +++ b/templates/backend/backend-module.ts @@ -1,8 +1,8 @@ -import { ConnectionHandler, JsonRpcConnectionHandler } from "@theia/core"; -import { ContainerModule } from "inversify"; -import { BackendClient, HelloBackendWithClientService, HelloBackendService, HELLO_BACKEND_PATH, HELLO_BACKEND_WITH_CLIENT_PATH } from "../common/protocol"; -import { HelloBackendWithClientServiceImpl } from "./hello-backend-with-client-service"; -import { HelloBackendServiceImpl } from "./hello-backend-service"; +import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { BackendClient, HelloBackendWithClientService, HelloBackendService, HELLO_BACKEND_PATH, HELLO_BACKEND_WITH_CLIENT_PATH } from '../common/protocol'; +import { HelloBackendWithClientServiceImpl } from './hello-backend-with-client-service'; +import { HelloBackendServiceImpl } from './hello-backend-service'; export default new ContainerModule(bind => { bind(HelloBackendService).to(HelloBackendServiceImpl).inSingletonScope() diff --git a/templates/backend/contribution.ts b/templates/backend/contribution.ts index 889a16b..9b03d9c 100644 --- a/templates/backend/contribution.ts +++ b/templates/backend/contribution.ts @@ -1,5 +1,5 @@ import { Command, CommandContribution, CommandRegistry} from '@theia/core/lib/common'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { HelloBackendWithClientService, HelloBackendService } from '../common/protocol'; const SayHelloViaBackendCommandWithCallBack: Command = { diff --git a/templates/backend/frontend-module.ts b/templates/backend/frontend-module.ts index 381c1ba..3f1435c 100644 --- a/templates/backend/frontend-module.ts +++ b/templates/backend/frontend-module.ts @@ -1,6 +1,6 @@ import { CommandContribution} from '@theia/core'; -import { WebSocketConnectionProvider } from "@theia/core/lib/browser"; -import { ContainerModule, injectable } from "inversify"; +import { WebSocketConnectionProvider } from '@theia/core/lib/browser'; +import { ContainerModule, injectable } from '@theia/core/shared/inversify'; import { BackendClient, HelloBackendWithClientService, HelloBackendService, HELLO_BACKEND_PATH, HELLO_BACKEND_WITH_CLIENT_PATH } from '../common/protocol'; import { <%= params.extensionPrefix %>CommandContribution} from './<%= params.extensionPath %>-contribution'; diff --git a/templates/backend/hello-backend-service.ts b/templates/backend/hello-backend-service.ts index 8b4f22c..51dfc10 100644 --- a/templates/backend/hello-backend-service.ts +++ b/templates/backend/hello-backend-service.ts @@ -1,5 +1,5 @@ -import { injectable } from "inversify"; -import { HelloBackendService } from "../common/protocol"; +import { injectable } from '@theia/core/shared/inversify'; +import { HelloBackendService } from '../common/protocol'; @injectable() export class HelloBackendServiceImpl implements HelloBackendService { diff --git a/templates/backend/hello-backend-with-client-service.ts b/templates/backend/hello-backend-with-client-service.ts index 02cf352..0a491d9 100644 --- a/templates/backend/hello-backend-with-client-service.ts +++ b/templates/backend/hello-backend-with-client-service.ts @@ -1,5 +1,5 @@ -import { injectable } from "inversify"; -import { BackendClient, HelloBackendWithClientService } from "../common/protocol"; +import { injectable } from '@theia/core/shared/inversify'; +import { BackendClient, HelloBackendWithClientService } from '../common/protocol'; @injectable() export class HelloBackendWithClientServiceImpl implements HelloBackendWithClientService { diff --git a/templates/empty/contribution.ts b/templates/empty/contribution.ts index 8980cf5..8c139b0 100644 --- a/templates/empty/contribution.ts +++ b/templates/empty/contribution.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; @injectable() // Add contribution interface to be implemented, e.g. "<%= params.extensionPrefix %>Contribution implements CommandContribution" diff --git a/templates/empty/frontend-module.ts b/templates/empty/frontend-module.ts index 68dd7c5..74b7368 100644 --- a/templates/empty/frontend-module.ts +++ b/templates/empty/frontend-module.ts @@ -1,7 +1,7 @@ /** * Generated using theia-extension-generator */ -import { ContainerModule } from 'inversify'; +import { ContainerModule } from '@theia/core/shared/inversify'; import { <%= params.extensionPrefix %>Contribution } from './<%= params.extensionPath %>-contribution'; diff --git a/templates/extension-package.json b/templates/extension-package.json index 8f68f13..ec007e8 100644 --- a/templates/extension-package.json +++ b/templates/extension-package.json @@ -31,13 +31,13 @@ }, "devDependencies": { "rimraf": "latest", - "typescript": "latest" + "typescript": "latest"<% if (params.devdependencies) { %><%- params.devdependencies %><% } %> }, "scripts": { "prepare": "yarn run clean && yarn run build", "clean": "rimraf lib", "build": "tsc", - "watch": "tsc -w" + "watch": "tsc -w"<% if (params.scripts) { %><%- params.scripts %><% } %> }, "theiaExtensions": [ { diff --git a/templates/hello-world/contribution.ts b/templates/hello-world/contribution.ts index 93de0b8..34e6a86 100644 --- a/templates/hello-world/contribution.ts +++ b/templates/hello-world/contribution.ts @@ -1,10 +1,10 @@ -import { injectable, inject } from "inversify"; -import { Command, CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, MessageService } from "@theia/core/lib/common"; -import { CommonMenus } from "@theia/core/lib/browser"; +import { injectable, inject } from '@theia/core/shared/inversify'; +import { Command, CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, MessageService } from '@theia/core/lib/common'; +import { CommonMenus } from '@theia/core/lib/browser'; export const <%= params.extensionPrefix %>Command: Command = { id: '<%= params.extensionPrefix %>.command', - label: "Say Hello" + label: 'Say Hello' }; @injectable() diff --git a/templates/hello-world/frontend-module.ts b/templates/hello-world/frontend-module.ts index 1db5809..b014191 100644 --- a/templates/hello-world/frontend-module.ts +++ b/templates/hello-world/frontend-module.ts @@ -2,11 +2,8 @@ * Generated using theia-extension-generator */ import { <%= params.extensionPrefix %>CommandContribution, <%= params.extensionPrefix %>MenuContribution } from './<%= params.extensionPath %>-contribution'; -import { - CommandContribution, - MenuContribution -} from "@theia/core/lib/common"; -import { ContainerModule } from "inversify"; +import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; +import { ContainerModule } from '@theia/core/shared/inversify'; export default new ContainerModule(bind => { // add your contribution bindings here diff --git a/templates/labelprovider/contribution.ts b/templates/labelprovider/contribution.ts index 512ec40..a583d50 100644 --- a/templates/labelprovider/contribution.ts +++ b/templates/labelprovider/contribution.ts @@ -1,6 +1,6 @@ -import { FileStatNode } from "@theia/filesystem/lib/browser/file-tree/file-tree"; -import { FileTreeLabelProvider } from "@theia/filesystem/lib/browser/file-tree/file-tree-label-provider"; -import { injectable, } from "inversify"; +import { FileStatNode } from '@theia/filesystem/lib/browser/file-tree/file-tree'; +import { FileTreeLabelProvider } from '@theia/filesystem/lib/browser/file-tree/file-tree-label-provider'; +import { injectable } from '@theia/core/shared/inversify'; @injectable() export class <%= params.extensionPrefix %>LabelProviderContribution extends FileTreeLabelProvider { diff --git a/templates/labelprovider/frontend-module.ts b/templates/labelprovider/frontend-module.ts index ca34b5e..03df5b6 100644 --- a/templates/labelprovider/frontend-module.ts +++ b/templates/labelprovider/frontend-module.ts @@ -2,7 +2,7 @@ * Generated using theia-extension-generator */ import { LabelProviderContribution } from "@theia/core/lib/browser"; -import { ContainerModule } from "inversify"; +import { ContainerModule } from "@theia/core/shared/inversify"; import { <%= params.extensionPrefix %>LabelProviderContribution } from './<%= params.extensionPath %>-contribution'; import '../../src/browser/style/example.css'; diff --git a/templates/launch.json b/templates/launch.json index 63be103..58b45a2 100644 --- a/templates/launch.json +++ b/templates/launch.json @@ -20,7 +20,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceRoot}/node_modules/@theia/*/lib/**/*.js", - "${workspaceRoot}/browser-app/lib/**/*.js", + "${workspaceRoot}/*/lib/**/*.js", "${workspaceRoot}/browser-app/src-gen/**/*.js" ], "smartStep": true, @@ -49,7 +49,7 @@ "outFiles": [ "${workspaceRoot}/electron-app/src-gen/frontend/electron-main.js", "${workspaceRoot}/electron-app/src-gen/backend/main.js", - "${workspaceRoot}/electron-app/lib/**/*.js", + "${workspaceRoot}/*/lib/**/*.js", "${workspaceRoot}/node_modules/@theia/*/lib/**/*.js" ], "smartStep": true, diff --git a/templates/root-package.json b/templates/root-package.json index bfe1a62..55e3961 100644 --- a/templates/root-package.json +++ b/templates/root-package.json @@ -6,7 +6,7 @@ "rebuild:electron": "theia rebuild:electron", "start:browser": "yarn rebuild:browser && yarn --cwd browser-app start", "start:electron": "yarn rebuild:electron && yarn --cwd electron-app start", - "watch": "lerna run --parallel watch" + "watch": "lerna run --parallel watch"<% if (params.rootscripts) { %><%- params.rootscripts %><% } %> }, "devDependencies": { "lerna": "<%= params.lernaVersion %>" diff --git a/templates/tree-editor/example-file/example-file-command.ts b/templates/tree-editor/example-file/example-file-command.ts index bdd7b76..c8b552e 100644 --- a/templates/tree-editor/example-file/example-file-command.ts +++ b/templates/tree-editor/example-file/example-file-command.ts @@ -6,7 +6,7 @@ import URI from '@theia/core/lib/common/uri'; import { SingleUriCommandHandler } from '@theia/core/lib/common/uri-command-handler'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileSystemUtils } from '@theia/filesystem/lib/common'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { OpenerService } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; diff --git a/templates/tree-editor/example-file/example-file-contribution.ts b/templates/tree-editor/example-file/example-file-contribution.ts index ac31ad0..31dc7d2 100644 --- a/templates/tree-editor/example-file/example-file-contribution.ts +++ b/templates/tree-editor/example-file/example-file-contribution.ts @@ -1,5 +1,5 @@ import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, SelectionService, MAIN_MENU_BAR } from '@theia/core/lib/common'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { WorkspaceRootUriAwareCommandHandler } from '@theia/workspace/lib/browser/workspace-commands'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { NewTreeExampleFileCommandHandler, NewTreeExampleFileCommand } from './example-file-command'; diff --git a/templates/tree-editor/tree-contribution.ts b/templates/tree-editor/tree-contribution.ts index 3131bde..129759a 100644 --- a/templates/tree-editor/tree-contribution.ts +++ b/templates/tree-editor/tree-contribution.ts @@ -1,16 +1,11 @@ +import URI from '@theia/core/lib/common/uri'; import { CommandRegistry, MenuModelRegistry } from '@theia/core'; import { ApplicationShell, NavigatableWidgetOptions, OpenerService, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import URI from '@theia/core/lib/common/uri'; -import { inject, injectable } from 'inversify'; -import { - BaseTreeEditorContribution, - MasterTreeWidget, - TreeEditor, -} from '@eclipse-emfcloud/theia-tree-editor'; - +import { inject, injectable } from '@theia/core/shared/inversify'; import { TreeModelService } from './tree/tree-model-service'; import { TreeEditorWidget } from './tree/tree-editor-widget'; import { TreeLabelProvider } from './tree/tree-label-provider'; +import { BaseTreeEditorContribution, MasterTreeWidget, TreeEditor,} from '@eclipse-emfcloud/theia-tree-editor'; @injectable() export class TreeContribution extends BaseTreeEditorContribution { diff --git a/templates/tree-editor/tree-frontend-module.ts b/templates/tree-editor/tree-frontend-module.ts index 5b6db3d..f0a0ccd 100644 --- a/templates/tree-editor/tree-frontend-module.ts +++ b/templates/tree-editor/tree-frontend-module.ts @@ -5,9 +5,7 @@ import '../../src/browser/style/editor.css'; import { CommandContribution, MenuContribution } from '@theia/core'; import { LabelProviderContribution, NavigatableWidgetOptions, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; -import { ContainerModule } from 'inversify'; -import { createBasicTreeContainer, NavigatableTreeEditorOptions } from '@eclipse-emfcloud/theia-tree-editor'; - +import { ContainerModule } from '@theia/core/shared/inversify'; import { TreeContribution } from './tree-contribution'; import { TreeModelService } from './tree/tree-model-service'; import { TreeNodeFactory } from './tree/tree-node-factory'; @@ -16,6 +14,7 @@ import { TreeLabelProvider } from './tree/tree-label-provider'; import { TreeLabelProviderContribution } from './tree-label-provider-contribution'; import { NewTreeExampleFileCommandHandler } from './example-file/example-file-command'; import { NewTreeExampleFileCommandContribution, NewTreeExampleFileMenuContribution } from './example-file/example-file-contribution'; +import { createBasicTreeContainer, NavigatableTreeEditorOptions } from '@eclipse-emfcloud/theia-tree-editor'; export default new ContainerModule(bind => { // Bind Theia IDE contributions for the example file creation menu entry. diff --git a/templates/tree-editor/tree-label-provider-contribution.ts b/templates/tree-editor/tree-label-provider-contribution.ts index 806e64e..45fd91d 100644 --- a/templates/tree-editor/tree-label-provider-contribution.ts +++ b/templates/tree-editor/tree-label-provider-contribution.ts @@ -1,7 +1,7 @@ import { LabelProviderContribution } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { FileStat } from '@theia/filesystem/lib/common'; -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; @injectable() export class TreeLabelProviderContribution implements LabelProviderContribution { diff --git a/templates/tree-editor/tree/tree-editor-widget.ts b/templates/tree-editor/tree/tree-editor-widget.ts index 952beed..f1d6e19 100644 --- a/templates/tree-editor/tree/tree-editor-widget.ts +++ b/templates/tree-editor/tree/tree-editor-widget.ts @@ -2,7 +2,7 @@ import { Title, Widget } from '@theia/core/lib/browser'; import { DefaultResourceProvider, ILogger } from '@theia/core/lib/common'; import { EditorPreferences } from '@theia/editor/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; -import { inject, injectable } from 'inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { MasterTreeWidget, DetailFormWidget, diff --git a/templates/tree-editor/tree/tree-label-provider.ts b/templates/tree-editor/tree/tree-label-provider.ts index 4e86655..c2331ef 100644 --- a/templates/tree-editor/tree/tree-label-provider.ts +++ b/templates/tree-editor/tree/tree-label-provider.ts @@ -1,9 +1,8 @@ +import { injectable } from '@theia/core/shared/inversify'; import { LabelProviderContribution } from '@theia/core/lib/browser'; -import { injectable } from 'inversify'; -import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; - import { CoffeeModel } from './tree-model'; import { TreeEditorWidget } from './tree-editor-widget'; +import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; const DEFAULT_COLOR = 'black'; diff --git a/templates/tree-editor/tree/tree-model-service.ts b/templates/tree-editor/tree/tree-model-service.ts index 25b1102..904ad8a 100644 --- a/templates/tree-editor/tree/tree-model-service.ts +++ b/templates/tree-editor/tree/tree-model-service.ts @@ -1,8 +1,7 @@ import { ILogger } from '@theia/core'; -import { inject, injectable } from 'inversify'; -import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; - +import { inject, injectable } from '@theia/core/shared/inversify'; import { CoffeeModel } from './tree-model'; +import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; import { brewingView, coffeeSchema, diff --git a/templates/tree-editor/tree/tree-node-factory.ts b/templates/tree-editor/tree/tree-node-factory.ts index 3c80c60..e10e99a 100644 --- a/templates/tree-editor/tree/tree-node-factory.ts +++ b/templates/tree-editor/tree/tree-node-factory.ts @@ -1,11 +1,10 @@ -import { ILogger } from '@theia/core'; -import { inject, injectable } from 'inversify'; -import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; import { v4 } from 'uuid'; - +import { ILogger } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { CoffeeModel } from './tree-model'; import { TreeEditorWidget } from './tree-editor-widget'; import { TreeLabelProvider } from './tree-label-provider'; +import { TreeEditor } from '@eclipse-emfcloud/theia-tree-editor'; @injectable() export class TreeNodeFactory implements TreeEditor.NodeFactory { diff --git a/templates/tsconfig.json b/templates/tsconfig.json index 889b54d..8419101 100644 --- a/templates/tsconfig.json +++ b/templates/tsconfig.json @@ -14,10 +14,10 @@ "resolveJsonModule": true, "module": "commonjs", "moduleResolution": "node", - "target": "es5", + "target": "ES2017", "jsx": "react", "lib": [ - "es6", + "ES2017", "dom" ], "sourceMap": true, diff --git a/templates/widget/README.md b/templates/widget/README.md index 6e1507e..99df021 100644 --- a/templates/widget/README.md +++ b/templates/widget/README.md @@ -1,7 +1,9 @@ # Example Widget -The example extension demonstrates how to contribute a custom widget (i.e. a view or editor) to Eclipse Theia. +The example extension demonstrates how to contribute a custom widget (i.e. a view or editor) to Eclipse Theia. Furthermore, the template contains an example unit test. ## How to use the widget example In the running application, open the widget using the menu "View" => "<%= params.extensionPrefix %> Widget" +To execute the test, run + yarn test \ No newline at end of file diff --git a/templates/widget/configs/jest.config.ts b/templates/widget/configs/jest.config.ts new file mode 100644 index 0000000..1a7f8a2 --- /dev/null +++ b/templates/widget/configs/jest.config.ts @@ -0,0 +1,10 @@ +import type { Config } from '@jest/types'; + +export default async (): Promise => ({ + preset: 'ts-jest', + testMatch: ['**.test.ts'], + rootDir: '../', + transform: { + '^.+\\.(ts)$': 'ts-jest', + } +}); diff --git a/templates/widget/contribution.ts b/templates/widget/contribution.ts index 09dcbf6..90f20e5 100644 --- a/templates/widget/contribution.ts +++ b/templates/widget/contribution.ts @@ -1,4 +1,4 @@ -import { injectable } from 'inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { MenuModelRegistry } from '@theia/core'; import { <%= params.extensionPrefix %>Widget } from './<%= params.extensionPath %>-widget'; import { AbstractViewContribution } from '@theia/core/lib/browser'; diff --git a/templates/widget/frontend-module.ts b/templates/widget/frontend-module.ts index ba01abc..333101c 100644 --- a/templates/widget/frontend-module.ts +++ b/templates/widget/frontend-module.ts @@ -1,4 +1,4 @@ -import { ContainerModule } from 'inversify'; +import { ContainerModule } from '@theia/core/shared/inversify'; import { <%= params.extensionPrefix %>Widget } from './<%= params.extensionPath %>-widget'; import { <%= params.extensionPrefix %>Contribution } from './<%= params.extensionPath %>-contribution'; import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; diff --git a/templates/widget/widget.test.ts b/templates/widget/widget.test.ts new file mode 100644 index 0000000..817e787 --- /dev/null +++ b/templates/widget/widget.test.ts @@ -0,0 +1,36 @@ +import 'reflect-metadata'; +import { MessageService } from '@theia/core'; +import { ContainerModule, Container } from '@theia/core/shared/inversify'; +import { <%= params.extensionPrefix %>Widget } from './<%= params.extensionPath %>-widget'; +import { render } from '@testing-library/react' + +describe('<%= params.extensionPrefix %>Widget', () => { + + let widget: <%= params.extensionPrefix %>Widget; + + beforeEach(async () => { + const module = new ContainerModule( bind => { + bind(MessageService).toConstantValue({ + info(message: string): void { + console.log(message); + } + } as MessageService); + bind(<%= params.extensionPrefix %>Widget).toSelf(); + }); + const container = new Container(); + container.load(module); + widget = container.resolve<<%= params.extensionPrefix %>Widget>(<%= params.extensionPrefix %>Widget); + }); + + it('should render react node correctly', async () => { + const element = render(widget.render()); + expect(element.queryByText('Display Message')).toBeTruthy(); + }); + + it('should inject \'MessageService\'', () => { + const spy = jest.spyOn(widget as any, 'displayMessage') + widget['displayMessage'](); + expect(spy).toBeCalled(); + }); + +}); diff --git a/templates/widget/widget.tsx b/templates/widget/widget.tsx index bbc8242..6e6e8eb 100644 --- a/templates/widget/widget.tsx +++ b/templates/widget/widget.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { injectable, postConstruct, inject } from 'inversify'; +import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; import { AlertMessage } from '@theia/core/lib/browser/widgets/alert-message'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { MessageService } from '@theia/core'; @@ -23,7 +23,7 @@ export class <%= params.extensionPrefix %>Widget extends ReactWidget { this.update(); } - protected render(): React.ReactNode { + render(): React.ReactElement { const header = `This is a sample widget which simply calls the messageService in order to display an info message to end users.`; return