Skip to content

Commit bd63880

Browse files
authored
Fixes multiple bugs (#131)
* Fixes multiple bugs * Fix tests * Review * Improves tests * Lint
1 parent 930ac19 commit bd63880

File tree

9 files changed

+471
-277
lines changed

9 files changed

+471
-277
lines changed

javascript/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"prettier": "^2.6.0",
5959
"rimraf": "^3.0.0",
6060
"typedoc": "^0.23.21",
61-
"typescript": "^4.8.0"
61+
"typescript": "^4.8.0",
62+
"process": "0.11.10"
6263
},
6364
"publishConfig": {
6465
"access": "public"

javascript/src/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,9 @@ export namespace ISharedNotebook {
388388
disableDocumentWideUndoRedo?: boolean;
389389

390390
/**
391-
* The language preference for the model.
391+
* The content of the notebook.
392392
*/
393-
languagePreference?: string;
393+
data?: Partial<nbformat.INotebookContent>;
394394
}
395395
}
396396

javascript/src/ycell.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import type {
1818
ISharedRawCell,
1919
SharedCell
2020
} from './api.js';
21-
import { IYText } from './ytext';
22-
import { YNotebook } from './ynotebook';
21+
import { IYText } from './ytext.js';
22+
import { YNotebook } from './ynotebook.js';
2323

2424
/**
2525
* Cell type.
@@ -606,12 +606,21 @@ export class YBaseCell<Metadata extends nbformat.IBaseCellMetadata>
606606
});
607607
break;
608608
case 'update':
609-
if (!JSONExt.deepEqual(change.oldValue, this._ymetadata.get(key))) {
609+
const newValue = this._ymetadata.get(key);
610+
const oldValue = change.oldValue;
611+
let equal = true;
612+
if (typeof oldValue == 'object' && typeof newValue == 'object') {
613+
equal = JSONExt.deepEqual(oldValue, newValue);
614+
} else {
615+
equal = oldValue === newValue;
616+
}
617+
618+
if (!equal) {
610619
this._metadataChanged.emit({
611620
key,
612-
newValue: this._ymetadata.get(key),
613-
oldValue: change.oldValue,
614-
type: 'change'
621+
type: 'change',
622+
oldValue,
623+
newValue
615624
});
616625
}
617626
break;

javascript/src/yfile.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
import * as Y from 'yjs';
77
import type { Delta, FileChange, ISharedFile, ISharedText } from './api.js';
8-
import { IYText } from './ytext';
9-
import { YDocument } from './ydocument';
8+
import { IYText } from './ytext.js';
9+
import { YDocument } from './ydocument.js';
1010

1111
/**
1212
* Shareable text file.

javascript/src/ynotebook.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import type {
1616
SharedCell
1717
} from './api.js';
1818

19-
import { YDocument } from './ydocument';
19+
import { YDocument } from './ydocument.js';
2020
import {
2121
createCell,
2222
createCellModelFromSharedType,
2323
YBaseCell,
2424
YCellType
25-
} from './ycell';
25+
} from './ycell.js';
2626

2727
/**
2828
* Shared implementation of the Shared Document types.
@@ -46,7 +46,7 @@ export class YNotebook
4646
*
4747
* @param options
4848
*/
49-
constructor(options: ISharedNotebook.IOptions = {}) {
49+
constructor(options: Omit<ISharedNotebook.IOptions, 'data'> = {}) {
5050
super();
5151
this._disableDocumentWideUndoRedo =
5252
options.disableDocumentWideUndoRedo ?? false;
@@ -75,14 +75,15 @@ export class YNotebook
7575
const ynotebook = new YNotebook({
7676
disableDocumentWideUndoRedo: options.disableDocumentWideUndoRedo ?? false
7777
});
78-
const ymetadata = new Y.Map();
79-
ymetadata.set('language_info', { name: options.languagePreference ?? '' });
80-
ymetadata.set('kernelspec', {
81-
name: '',
82-
display_name: ''
83-
});
84-
ynotebook.ymeta.set('metadata', ymetadata);
8578

79+
const data: nbformat.INotebookContent = {
80+
cells: options.data?.cells ?? [],
81+
nbformat: options.data?.nbformat ?? 4,
82+
nbformat_minor: options.data?.nbformat_minor ?? 5,
83+
metadata: options.data?.metadata ?? {}
84+
};
85+
86+
ynotebook.fromJSON(data);
8687
return ynotebook;
8788
}
8889

@@ -483,12 +484,21 @@ export class YNotebook
483484
});
484485
break;
485486
case 'update':
486-
if (!JSONExt.deepEqual(change.oldValue, ymetadata.get(key))) {
487+
const newValue = ymetadata.get(key);
488+
const oldValue = change.oldValue;
489+
let equal = true;
490+
if (typeof oldValue == 'object' && typeof newValue == 'object') {
491+
equal = JSONExt.deepEqual(oldValue, newValue);
492+
} else {
493+
equal = oldValue === newValue;
494+
}
495+
496+
if (!equal) {
487497
this._metadataChanged.emit({
488498
key,
489499
type: 'change',
490-
oldValue: change.oldValue,
491-
newValue: ymetadata.get(key)
500+
oldValue,
501+
newValue
492502
});
493503
}
494504
break;

javascript/test/ycell.spec.ts

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
import { IMapChange, YCodeCell, YNotebook } from '../src';
5+
6+
describe('@jupyter/ydoc', () => {
7+
describe('YCell standalone', () => {
8+
test('should set source', () => {
9+
const codeCell = YCodeCell.create();
10+
codeCell.setSource('test');
11+
expect(codeCell.getSource()).toBe('test');
12+
codeCell.dispose();
13+
});
14+
15+
test('should update source', () => {
16+
const codeCell = YCodeCell.create();
17+
codeCell.setSource('test');
18+
codeCell.updateSource(0, 0, 'hello');
19+
expect(codeCell.getSource()).toBe('hellotest');
20+
codeCell.dispose();
21+
});
22+
23+
test('should get metadata', () => {
24+
const cell = YCodeCell.create();
25+
const metadata = {
26+
collapsed: true,
27+
editable: false,
28+
name: 'cell-name'
29+
};
30+
31+
cell.setMetadata(metadata);
32+
33+
expect(cell.metadata).toEqual({
34+
...metadata,
35+
jupyter: { outputs_hidden: true }
36+
});
37+
cell.dispose();
38+
});
39+
40+
test('should get all metadata', () => {
41+
const cell = YCodeCell.create();
42+
const metadata = {
43+
jupyter: { outputs_hidden: true },
44+
editable: false,
45+
name: 'cell-name'
46+
};
47+
48+
cell.setMetadata(metadata);
49+
50+
expect(cell.getMetadata()).toEqual({ ...metadata, collapsed: true });
51+
cell.dispose();
52+
});
53+
54+
test('should get one metadata', () => {
55+
const cell = YCodeCell.create();
56+
const metadata = {
57+
collapsed: true,
58+
editable: false,
59+
name: 'cell-name'
60+
};
61+
62+
cell.setMetadata(metadata);
63+
64+
expect(cell.getMetadata('editable')).toEqual(metadata.editable);
65+
cell.dispose();
66+
});
67+
68+
it.each([null, undefined, 1, true, 'string', { a: 1 }, [1, 2]])(
69+
'should get single metadata %s',
70+
value => {
71+
const cell = YCodeCell.create();
72+
const metadata = {
73+
collapsed: true,
74+
editable: false,
75+
name: 'cell-name',
76+
test: value
77+
};
78+
79+
cell.setMetadata(metadata);
80+
81+
expect(cell.getMetadata('test')).toEqual(value);
82+
cell.dispose();
83+
}
84+
);
85+
86+
test('should set one metadata', () => {
87+
const cell = YCodeCell.create();
88+
const metadata = {
89+
collapsed: true,
90+
editable: false,
91+
name: 'cell-name'
92+
};
93+
94+
cell.setMetadata(metadata);
95+
cell.setMetadata('test', 'banana');
96+
97+
expect(cell.getMetadata('test')).toEqual('banana');
98+
cell.dispose();
99+
});
100+
101+
test('should emit all metadata changes', () => {
102+
const notebook = YNotebook.create();
103+
104+
const metadata = {
105+
collapsed: true,
106+
editable: false,
107+
name: 'cell-name'
108+
};
109+
110+
const changes: IMapChange[] = [];
111+
notebook.metadataChanged.connect((_, c) => {
112+
changes.push(c);
113+
});
114+
notebook.metadata = metadata;
115+
116+
expect(changes).toHaveLength(3);
117+
expect(changes).toEqual([
118+
{
119+
type: 'add',
120+
key: 'collapsed',
121+
newValue: metadata.collapsed,
122+
oldValue: undefined
123+
},
124+
{
125+
type: 'add',
126+
key: 'editable',
127+
newValue: metadata.editable,
128+
oldValue: undefined
129+
},
130+
{
131+
type: 'add',
132+
key: 'name',
133+
newValue: metadata.name,
134+
oldValue: undefined
135+
}
136+
]);
137+
138+
notebook.dispose();
139+
});
140+
141+
test('should emit a add metadata change', () => {
142+
const cell = YCodeCell.create();
143+
const metadata = {
144+
collapsed: true,
145+
editable: false,
146+
name: 'cell-name'
147+
};
148+
cell.metadata = metadata;
149+
150+
const changes: IMapChange[] = [];
151+
cell.metadataChanged.connect((_, c) => {
152+
changes.push(c);
153+
});
154+
cell.setMetadata('test', 'banana');
155+
156+
try {
157+
expect(changes).toHaveLength(1);
158+
expect(changes).toEqual([
159+
{ type: 'add', key: 'test', newValue: 'banana', oldValue: undefined }
160+
]);
161+
} finally {
162+
cell.dispose();
163+
}
164+
});
165+
166+
test('should emit a delete metadata change', () => {
167+
const cell = YCodeCell.create();
168+
const metadata = {
169+
collapsed: true,
170+
editable: false,
171+
name: 'cell-name'
172+
};
173+
cell.metadata = metadata;
174+
175+
const changes: IMapChange[] = [];
176+
cell.setMetadata('test', 'banana');
177+
178+
cell.metadataChanged.connect((_, c) => {
179+
changes.push(c);
180+
});
181+
cell.deleteMetadata('test');
182+
183+
try {
184+
expect(changes).toHaveLength(1);
185+
expect(changes).toEqual([
186+
{
187+
type: 'remove',
188+
key: 'test',
189+
newValue: undefined,
190+
oldValue: 'banana'
191+
}
192+
]);
193+
} finally {
194+
cell.dispose();
195+
}
196+
});
197+
198+
test('should emit an update metadata change', () => {
199+
const cell = YCodeCell.create();
200+
const metadata = {
201+
collapsed: true,
202+
editable: false,
203+
name: 'cell-name'
204+
};
205+
cell.metadata = metadata;
206+
207+
const changes: IMapChange[] = [];
208+
cell.setMetadata('test', 'banana');
209+
210+
cell.metadataChanged.connect((_, c) => {
211+
changes.push(c);
212+
});
213+
cell.setMetadata('test', 'orange');
214+
215+
try {
216+
expect(changes).toHaveLength(1);
217+
expect(changes).toEqual([
218+
{
219+
type: 'change',
220+
key: 'test',
221+
newValue: 'orange',
222+
oldValue: 'banana'
223+
}
224+
]);
225+
} finally {
226+
cell.dispose();
227+
}
228+
});
229+
});
230+
});

0 commit comments

Comments
 (0)