@jupyter/chat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/lib/__tests__/model.spec.d.ts +1 -0
  2. package/lib/__tests__/model.spec.js +72 -0
  3. package/lib/__tests__/widgets.spec.d.ts +1 -0
  4. package/lib/__tests__/widgets.spec.js +33 -0
  5. package/lib/components/chat-input.d.ts +33 -0
  6. package/lib/components/chat-input.js +60 -0
  7. package/lib/components/chat-messages.d.ts +32 -0
  8. package/lib/components/chat-messages.js +162 -0
  9. package/lib/components/chat.d.ts +43 -0
  10. package/lib/components/chat.js +100 -0
  11. package/lib/components/copy-button.d.ts +6 -0
  12. package/lib/components/copy-button.js +35 -0
  13. package/lib/components/jl-theme-provider.d.ts +6 -0
  14. package/lib/components/jl-theme-provider.js +19 -0
  15. package/lib/components/mui-extras/stacking-alert.d.ts +28 -0
  16. package/lib/components/mui-extras/stacking-alert.js +56 -0
  17. package/lib/components/rendermime-markdown.d.ts +12 -0
  18. package/lib/components/rendermime-markdown.js +54 -0
  19. package/lib/components/scroll-container.d.ts +23 -0
  20. package/lib/components/scroll-container.js +51 -0
  21. package/lib/components/toolbar.d.ts +11 -0
  22. package/lib/components/toolbar.js +30 -0
  23. package/lib/icons.d.ts +2 -0
  24. package/lib/icons.js +11 -0
  25. package/lib/index.d.ts +6 -0
  26. package/lib/index.js +10 -0
  27. package/lib/model.d.ts +177 -0
  28. package/lib/model.js +128 -0
  29. package/lib/theme-provider.d.ts +3 -0
  30. package/lib/theme-provider.js +133 -0
  31. package/lib/types.d.ts +49 -0
  32. package/lib/types.js +5 -0
  33. package/lib/widgets/chat-error.d.ts +2 -0
  34. package/lib/widgets/chat-error.js +26 -0
  35. package/lib/widgets/chat-sidebar.d.ts +4 -0
  36. package/lib/widgets/chat-sidebar.js +15 -0
  37. package/lib/widgets/chat-widget.d.ts +19 -0
  38. package/lib/widgets/chat-widget.js +28 -0
  39. package/package.json +209 -0
  40. package/src/__tests__/model.spec.ts +84 -0
  41. package/src/__tests__/widgets.spec.ts +43 -0
  42. package/src/components/chat-input.tsx +143 -0
  43. package/src/components/chat-messages.tsx +283 -0
  44. package/src/components/chat.tsx +179 -0
  45. package/src/components/copy-button.tsx +55 -0
  46. package/src/components/jl-theme-provider.tsx +28 -0
  47. package/src/components/mui-extras/stacking-alert.tsx +105 -0
  48. package/src/components/rendermime-markdown.tsx +88 -0
  49. package/src/components/scroll-container.tsx +74 -0
  50. package/src/components/toolbar.tsx +50 -0
  51. package/src/icons.ts +15 -0
  52. package/src/index.ts +11 -0
  53. package/src/model.ts +272 -0
  54. package/src/theme-provider.ts +137 -0
  55. package/src/types/mui.d.ts +18 -0
  56. package/src/types/svg.d.ts +17 -0
  57. package/src/types.ts +58 -0
  58. package/src/widgets/chat-error.tsx +43 -0
  59. package/src/widgets/chat-sidebar.tsx +30 -0
  60. package/src/widgets/chat-widget.tsx +51 -0
  61. package/style/base.css +13 -0
  62. package/style/chat-settings.css +10 -0
  63. package/style/chat.css +53 -0
  64. package/style/icons/chat.svg +6 -0
  65. package/style/index.css +6 -0
  66. package/style/index.js +6 -0
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { createTheme } from '@mui/material/styles';
6
+ function getCSSVariable(name) {
7
+ return getComputedStyle(document.body).getPropertyValue(name).trim();
8
+ }
9
+ export async function pollUntilReady() {
10
+ while (!document.body.hasAttribute('data-jp-theme-light')) {
11
+ await new Promise(resolve => setTimeout(resolve, 100)); // Wait 100ms
12
+ }
13
+ }
14
+ export async function getJupyterLabTheme() {
15
+ await pollUntilReady();
16
+ const light = document.body.getAttribute('data-jp-theme-light');
17
+ return createTheme({
18
+ spacing: 4,
19
+ components: {
20
+ MuiButton: {
21
+ defaultProps: {
22
+ size: 'small'
23
+ }
24
+ },
25
+ MuiFilledInput: {
26
+ defaultProps: {
27
+ margin: 'dense'
28
+ }
29
+ },
30
+ MuiFormControl: {
31
+ defaultProps: {
32
+ margin: 'dense',
33
+ size: 'small'
34
+ }
35
+ },
36
+ MuiFormHelperText: {
37
+ defaultProps: {
38
+ margin: 'dense'
39
+ }
40
+ },
41
+ MuiIconButton: {
42
+ defaultProps: {
43
+ size: 'small'
44
+ }
45
+ },
46
+ MuiInputBase: {
47
+ defaultProps: {
48
+ margin: 'dense',
49
+ size: 'small'
50
+ }
51
+ },
52
+ MuiInputLabel: {
53
+ defaultProps: {
54
+ margin: 'dense'
55
+ }
56
+ },
57
+ MuiListItem: {
58
+ defaultProps: {
59
+ dense: true
60
+ }
61
+ },
62
+ MuiOutlinedInput: {
63
+ defaultProps: {
64
+ margin: 'dense'
65
+ }
66
+ },
67
+ MuiFab: {
68
+ defaultProps: {
69
+ size: 'small'
70
+ }
71
+ },
72
+ MuiTable: {
73
+ defaultProps: {
74
+ size: 'small'
75
+ }
76
+ },
77
+ MuiTextField: {
78
+ defaultProps: {
79
+ margin: 'dense',
80
+ size: 'small'
81
+ }
82
+ },
83
+ MuiToolbar: {
84
+ defaultProps: {
85
+ variant: 'dense'
86
+ }
87
+ }
88
+ },
89
+ palette: {
90
+ background: {
91
+ paper: getCSSVariable('--jp-layout-color1'),
92
+ default: getCSSVariable('--jp-layout-color1')
93
+ },
94
+ mode: light === 'true' ? 'light' : 'dark',
95
+ primary: {
96
+ main: getCSSVariable('--jp-brand-color1'),
97
+ light: getCSSVariable('--jp-brand-color2'),
98
+ dark: getCSSVariable('--jp-brand-color0')
99
+ },
100
+ error: {
101
+ main: getCSSVariable('--jp-error-color1'),
102
+ light: getCSSVariable('--jp-error-color2'),
103
+ dark: getCSSVariable('--jp-error-color0')
104
+ },
105
+ warning: {
106
+ main: getCSSVariable('--jp-warn-color1'),
107
+ light: getCSSVariable('--jp-warn-color2'),
108
+ dark: getCSSVariable('--jp-warn-color0')
109
+ },
110
+ success: {
111
+ main: getCSSVariable('--jp-success-color1'),
112
+ light: getCSSVariable('--jp-success-color2'),
113
+ dark: getCSSVariable('--jp-success-color0')
114
+ },
115
+ text: {
116
+ primary: getCSSVariable('--jp-ui-font-color1'),
117
+ secondary: getCSSVariable('--jp-ui-font-color2'),
118
+ disabled: getCSSVariable('--jp-ui-font-color3')
119
+ }
120
+ },
121
+ shape: {
122
+ borderRadius: 2
123
+ },
124
+ typography: {
125
+ fontFamily: getCSSVariable('--jp-ui-font-family'),
126
+ fontSize: 12,
127
+ htmlFontSize: 16,
128
+ button: {
129
+ textTransform: 'capitalize'
130
+ }
131
+ }
132
+ });
133
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The user description.
3
+ */
4
+ export interface IUser {
5
+ username: string;
6
+ name?: string;
7
+ display_name?: string;
8
+ initials?: string;
9
+ color?: string;
10
+ avatar_url?: string;
11
+ }
12
+ /**
13
+ * The configuration interface.
14
+ */
15
+ export interface IConfig {
16
+ sendWithShiftEnter?: boolean;
17
+ lastRead?: number;
18
+ }
19
+ /**
20
+ * The chat message decription.
21
+ */
22
+ export interface IChatMessage {
23
+ type: 'msg';
24
+ body: string;
25
+ id: string;
26
+ time: number;
27
+ sender: IUser | string;
28
+ raw_time?: boolean;
29
+ deleted?: boolean;
30
+ edited?: boolean;
31
+ }
32
+ /**
33
+ * The chat history interface.
34
+ */
35
+ export interface IChatHistory {
36
+ messages: IChatMessage[];
37
+ }
38
+ /**
39
+ * The content of a new message.
40
+ */
41
+ export interface INewMessage {
42
+ body: string;
43
+ id?: string;
44
+ }
45
+ /**
46
+ * An empty interface to describe optional settings taht could be fetched from server.
47
+ */
48
+ export interface ISettings {
49
+ }
package/lib/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export {};
@@ -0,0 +1,2 @@
1
+ import { IThemeManager, ReactWidget } from '@jupyterlab/apputils';
2
+ export declare function buildErrorWidget(themeManager: IThemeManager | null): ReactWidget;
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { ReactWidget } from '@jupyterlab/apputils';
6
+ import { Alert, Box } from '@mui/material';
7
+ import React from 'react';
8
+ import { JlThemeProvider } from '../components/jl-theme-provider';
9
+ import { chatIcon } from '../icons';
10
+ export function buildErrorWidget(themeManager) {
11
+ const ErrorWidget = ReactWidget.create(React.createElement(JlThemeProvider, { themeManager: themeManager },
12
+ React.createElement(Box, { sx: {
13
+ width: '100%',
14
+ height: '100%',
15
+ boxSizing: 'border-box',
16
+ background: 'var(--jp-layout-color0)',
17
+ display: 'flex',
18
+ flexDirection: 'column'
19
+ } },
20
+ React.createElement(Box, { sx: { padding: 4 } },
21
+ React.createElement(Alert, { severity: "error" }, "There seems to be a problem with the Chat backend, please look at the JupyterLab server logs or contact your administrator to correct this problem.")))));
22
+ ErrorWidget.id = 'jupyter-chat::chat';
23
+ ErrorWidget.title.icon = chatIcon;
24
+ ErrorWidget.title.caption = 'Jupyter Chat'; // TODO: i18n
25
+ return ErrorWidget;
26
+ }
@@ -0,0 +1,4 @@
1
+ import { IThemeManager, ReactWidget } from '@jupyterlab/apputils';
2
+ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3
+ import { IChatModel } from '../model';
4
+ export declare function buildChatSidebar(chatModel: IChatModel, themeManager: IThemeManager | null, rmRegistry: IRenderMimeRegistry): ReactWidget;
@@ -0,0 +1,15 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { ReactWidget } from '@jupyterlab/apputils';
6
+ import React from 'react';
7
+ import { Chat } from '../components/chat';
8
+ import { chatIcon } from '../icons';
9
+ export function buildChatSidebar(chatModel, themeManager, rmRegistry) {
10
+ const ChatWidget = ReactWidget.create(React.createElement(Chat, { model: chatModel, themeManager: themeManager, rmRegistry: rmRegistry }));
11
+ ChatWidget.id = 'jupyter-chat::side-panel';
12
+ ChatWidget.title.icon = chatIcon;
13
+ ChatWidget.title.caption = 'Jupyter Chat'; // TODO: i18n
14
+ return ChatWidget;
15
+ }
@@ -0,0 +1,19 @@
1
+ import { ReactWidget } from '@jupyterlab/apputils';
2
+ import React from 'react';
3
+ import { Chat } from '../components/chat';
4
+ import { IChatModel } from '../model';
5
+ export declare class ChatWidget extends ReactWidget {
6
+ constructor(options: Chat.IOptions);
7
+ /**
8
+ * Gte the model of the widget.
9
+ */
10
+ get model(): IChatModel;
11
+ render(): React.JSX.Element;
12
+ private readonly _model;
13
+ private _themeManager;
14
+ private _rmRegistry;
15
+ }
16
+ export declare namespace ChatWidget {
17
+ interface IOptions extends Chat.IOptions {
18
+ }
19
+ }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { ReactWidget } from '@jupyterlab/apputils';
6
+ import React from 'react';
7
+ import { Chat } from '../components/chat';
8
+ import { chatIcon } from '../icons';
9
+ export class ChatWidget extends ReactWidget {
10
+ constructor(options) {
11
+ super();
12
+ this.id = 'jupyter-chat::widget';
13
+ this.title.icon = chatIcon;
14
+ this.title.caption = 'Jupyter Chat'; // TODO: i18n
15
+ this._model = options.model;
16
+ this._themeManager = (options === null || options === void 0 ? void 0 : options.themeManager) || null;
17
+ this._rmRegistry = options.rmRegistry;
18
+ }
19
+ /**
20
+ * Gte the model of the widget.
21
+ */
22
+ get model() {
23
+ return this._model;
24
+ }
25
+ render() {
26
+ return (React.createElement(Chat, { model: this._model, themeManager: this._themeManager, rmRegistry: this._rmRegistry }));
27
+ }
28
+ }
package/package.json ADDED
@@ -0,0 +1,209 @@
1
+ {
2
+ "name": "@jupyter/chat",
3
+ "version": "0.1.0",
4
+ "description": "A package that provides UI components that can be used to create a chat in a Jupyterlab extension.",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension"
9
+ ],
10
+ "homepage": "https://github.com/jupyterlab/jupyter-chat",
11
+ "bugs": {
12
+ "url": "https://github.com/jupyterlab/jupyter-chat/issues"
13
+ },
14
+ "license": "BSD-3-Clause",
15
+ "author": {
16
+ "name": "Jupyter Development Team",
17
+ "email": "jupyter@googlegroups.com"
18
+ },
19
+ "files": [
20
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
21
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
22
+ "src/**/*.{ts,tsx}",
23
+ "schema/*.json"
24
+ ],
25
+ "main": "lib/index.js",
26
+ "types": "lib/index.d.ts",
27
+ "style": "style/index.css",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/jupyterlab/jupyter-chat.git"
31
+ },
32
+ "scripts": {
33
+ "build": "jlpm build:lib",
34
+ "build:prod": "jlpm clean && jlpm build:lib:prod",
35
+ "build:lib": "tsc --sourceMap",
36
+ "build:lib:prod": "tsc",
37
+ "clean": "jlpm clean:lib",
38
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
39
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
40
+ "clean:all": "jlpm clean:lib && jlpm clean:lintcache",
41
+ "eslint": "jlpm eslint:check --fix",
42
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
43
+ "install:extension": "jlpm build",
44
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
45
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
46
+ "prettier": "jlpm prettier:base --write --list-different",
47
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
48
+ "prettier:check": "jlpm prettier:base --check",
49
+ "stylelint": "jlpm stylelint:check --fix",
50
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
51
+ "test": "jest --coverage",
52
+ "watch:src": "tsc -w --sourceMap"
53
+ },
54
+ "dependencies": {
55
+ "@emotion/react": "^11.10.5",
56
+ "@emotion/styled": "^11.10.5",
57
+ "@jupyterlab/apputils": "^4.0.0",
58
+ "@jupyterlab/rendermime": "^4.0.0",
59
+ "@jupyterlab/ui-components": "^4.0.0",
60
+ "@lumino/disposable": "^2.0.0",
61
+ "@lumino/signaling": "^2.0.0",
62
+ "@mui/icons-material": "^5.11.0",
63
+ "@mui/material": "^5.11.0",
64
+ "clsx": "^2.1.0",
65
+ "react": "^18.2.0",
66
+ "react-dom": "^18.2.0"
67
+ },
68
+ "devDependencies": {
69
+ "@types/jest": "^29.2.0",
70
+ "@types/json-schema": "^7.0.11",
71
+ "@types/react": "^18.2.0",
72
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
73
+ "@types/react-dom": "^18.2.0",
74
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
75
+ "@typescript-eslint/parser": "^6.1.0",
76
+ "css-loader": "^6.7.1",
77
+ "eslint": "^8.36.0",
78
+ "eslint-config-prettier": "^8.8.0",
79
+ "eslint-plugin-prettier": "^5.0.0",
80
+ "jest": "^29.2.0",
81
+ "npm-run-all": "^4.1.5",
82
+ "prettier": "^3.0.0",
83
+ "rimraf": "^5.0.1",
84
+ "source-map-loader": "^1.0.2",
85
+ "style-loader": "^3.3.1",
86
+ "stylelint": "^15.10.1",
87
+ "stylelint-config-recommended": "^13.0.0",
88
+ "stylelint-config-standard": "^34.0.0",
89
+ "stylelint-csstree-validator": "^3.0.0",
90
+ "stylelint-prettier": "^4.0.0",
91
+ "typescript": "~5.0.2"
92
+ },
93
+ "sideEffects": [
94
+ "style/*.css",
95
+ "style/index.js"
96
+ ],
97
+ "styleModule": "style/index.js",
98
+ "publishConfig": {
99
+ "access": "public"
100
+ },
101
+ "jupyterlab": {
102
+ "discovery": {
103
+ "server": {
104
+ "managers": [
105
+ "pip"
106
+ ],
107
+ "base": {
108
+ "name": "jupyter_chat"
109
+ }
110
+ }
111
+ },
112
+ "extension": false,
113
+ "schemaDir": "schema"
114
+ },
115
+ "eslintIgnore": [
116
+ "node_modules",
117
+ "dist",
118
+ "coverage",
119
+ "**/*.d.ts",
120
+ "tests",
121
+ "**/__tests__",
122
+ "ui-tests"
123
+ ],
124
+ "eslintConfig": {
125
+ "extends": [
126
+ "eslint:recommended",
127
+ "plugin:@typescript-eslint/eslint-recommended",
128
+ "plugin:@typescript-eslint/recommended",
129
+ "plugin:prettier/recommended"
130
+ ],
131
+ "parser": "@typescript-eslint/parser",
132
+ "parserOptions": {
133
+ "project": "tsconfig.json",
134
+ "sourceType": "module"
135
+ },
136
+ "plugins": [
137
+ "@typescript-eslint"
138
+ ],
139
+ "rules": {
140
+ "@typescript-eslint/naming-convention": [
141
+ "error",
142
+ {
143
+ "selector": "interface",
144
+ "format": [
145
+ "PascalCase"
146
+ ],
147
+ "custom": {
148
+ "regex": "^I[A-Z]",
149
+ "match": true
150
+ }
151
+ }
152
+ ],
153
+ "@typescript-eslint/no-unused-vars": [
154
+ "warn",
155
+ {
156
+ "args": "none"
157
+ }
158
+ ],
159
+ "@typescript-eslint/no-explicit-any": "off",
160
+ "@typescript-eslint/no-namespace": "off",
161
+ "@typescript-eslint/no-use-before-define": "off",
162
+ "@typescript-eslint/quotes": [
163
+ "error",
164
+ "single",
165
+ {
166
+ "avoidEscape": true,
167
+ "allowTemplateLiterals": false
168
+ }
169
+ ],
170
+ "curly": [
171
+ "error",
172
+ "all"
173
+ ],
174
+ "eqeqeq": "error",
175
+ "prefer-arrow-callback": "error"
176
+ }
177
+ },
178
+ "prettier": {
179
+ "singleQuote": true,
180
+ "trailingComma": "none",
181
+ "arrowParens": "avoid",
182
+ "endOfLine": "auto",
183
+ "overrides": [
184
+ {
185
+ "files": "package.json",
186
+ "options": {
187
+ "tabWidth": 4
188
+ }
189
+ }
190
+ ]
191
+ },
192
+ "stylelint": {
193
+ "extends": [
194
+ "stylelint-config-recommended",
195
+ "stylelint-config-standard",
196
+ "stylelint-prettier/recommended"
197
+ ],
198
+ "plugins": [
199
+ "stylelint-csstree-validator"
200
+ ],
201
+ "rules": {
202
+ "csstree/validator": true,
203
+ "property-no-vendor-prefix": null,
204
+ "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
205
+ "selector-no-vendor-prefix": null,
206
+ "value-no-vendor-prefix": null
207
+ }
208
+ }
209
+ }
@@ -0,0 +1,84 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ /**
7
+ * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
8
+ */
9
+
10
+ import { ChatModel, IChatModel } from '../model';
11
+ import { IChatMessage } from '../types';
12
+
13
+ describe('test chat model', () => {
14
+ describe('model instantiation', () => {
15
+ it('should create a ChatModel', () => {
16
+ const model = new ChatModel();
17
+ expect(model).toBeInstanceOf(ChatModel);
18
+ });
19
+
20
+ it('should dispose a ChatModel', () => {
21
+ const model = new ChatModel();
22
+ model.dispose();
23
+ expect(model.isDisposed).toBeTruthy();
24
+ });
25
+ });
26
+
27
+ describe('incoming message', () => {
28
+ class TestChat extends ChatModel {
29
+ protected formatChatMessage(message: IChatMessage): IChatMessage {
30
+ message.body = 'formatted msg';
31
+ return message;
32
+ }
33
+ }
34
+
35
+ let model: IChatModel;
36
+ let messages: IChatMessage[];
37
+ const msg = {
38
+ type: 'msg',
39
+ id: 'message1',
40
+ time: Date.now() / 1000,
41
+ body: 'message test',
42
+ sender: { username: 'user' }
43
+ } as IChatMessage;
44
+
45
+ beforeEach(() => {
46
+ messages = [];
47
+ });
48
+
49
+ it('should signal incoming message', () => {
50
+ model = new ChatModel();
51
+ model.messagesUpdated.connect((sender: IChatModel) => {
52
+ expect(sender).toBe(model);
53
+ messages = model.messages;
54
+ });
55
+ model.messageAdded(msg);
56
+ expect(messages).toHaveLength(1);
57
+ expect(messages[0]).toBe(msg);
58
+ });
59
+
60
+ it('should format message', () => {
61
+ model = new TestChat();
62
+ model.messagesUpdated.connect((sender: IChatModel) => {
63
+ expect(sender).toBe(model);
64
+ messages = model.messages;
65
+ });
66
+ model.messageAdded({ ...msg });
67
+ expect(messages).toHaveLength(1);
68
+ expect(messages[0]).not.toBe(msg);
69
+ expect((messages[0] as IChatMessage).body).toBe('formatted msg');
70
+ });
71
+ });
72
+
73
+ describe('model config', () => {
74
+ it('should have empty config', () => {
75
+ const model = new ChatModel();
76
+ expect(model.config.sendWithShiftEnter).toBeUndefined();
77
+ });
78
+
79
+ it('should allow config', () => {
80
+ const model = new ChatModel({ config: { sendWithShiftEnter: true } });
81
+ expect(model.config.sendWithShiftEnter).toBeTruthy();
82
+ });
83
+ });
84
+ });
@@ -0,0 +1,43 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ /**
7
+ * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
8
+ */
9
+
10
+ import {
11
+ IRenderMimeRegistry,
12
+ RenderMimeRegistry
13
+ } from '@jupyterlab/rendermime';
14
+ import { ChatModel, IChatModel } from '../model';
15
+ import { ChatWidget } from '../widgets/chat-widget';
16
+
17
+ describe('test chat widget', () => {
18
+ let model: IChatModel;
19
+ let rmRegistry: IRenderMimeRegistry;
20
+
21
+ beforeEach(() => {
22
+ model = new ChatModel();
23
+ rmRegistry = new RenderMimeRegistry();
24
+ });
25
+
26
+ describe('model instantiation', () => {
27
+ it('should create a ChatModel', () => {
28
+ const widget = new ChatWidget({ model, rmRegistry });
29
+ expect(widget).toBeInstanceOf(ChatWidget);
30
+ });
31
+
32
+ it('should dispose a ChatModel', () => {
33
+ const widget = new ChatWidget({ model, rmRegistry });
34
+ widget.dispose();
35
+ expect(widget.isDisposed).toBeTruthy();
36
+ });
37
+
38
+ it('should provides the model', () => {
39
+ const widget = new ChatWidget({ model, rmRegistry });
40
+ expect(widget.model).toBe(model);
41
+ });
42
+ });
43
+ });