Skip to content

Commit c85dc4e

Browse files
jamesbhobbsdevin-ai-integration[bot]dinohamzic
authored
test: test coverage for core transformation functions (#45)
* Add comprehensive test coverage for core transformation functions - Add tests for convert-deepnote-block-type-to-jupyter.ts (100% coverage) - Add tests for convert-deepnote-block-to-jupyter-cell.ts (100% coverage) - Add tests for transform-deepnote-yaml-to-notebook-content.ts (100% coverage) This increases overall test coverage from 26.57% to 55.94%, providing comprehensive testing for the core Deepnote-to-Jupyter transformation logic. * Address CodeRabbit review comments - Add assertions to verify createPythonCode is called correctly in code cell tests - Add assertions to verify createMarkdown is called correctly in markdown cell tests - Add assertions to verify convertDeepnoteBlockToJupyterCell is called for all notebooks in transform tests - Verify mock call counts and arguments to strengthen test contracts * fix: Add ESLint rules for type safety and fix violations (#46) * Add ESLint rules for type safety and fix violations - Add @typescript-eslint/no-floating-promises: error - Add @typescript-eslint/no-non-null-assertion: error - Add @typescript-eslint/prefer-nullish-coalescing: error - Change @typescript-eslint/no-explicit-any from off to error Fixes: - Fix floating promise in NotebookPicker constructor by adding .catch() - Fix non-null assertion in NotebookPicker.onAfterAttach with null check - Fix prefer-nullish-coalescing in handler.ts by using ?? instead of || - Add inline eslint-disable comments for legitimate any usage in handler.ts * Add test for error handling in NotebookPicker constructor Improves test coverage for the .catch() error handler added to handle promise rejections in the constructor. * Add comprehensive test coverage for NotebookPicker - Add test for null model in handleChange - Add test for invalid metadata in handleChange - Add test for onAfterAttach without parent - Import Message type for test Coverage improved from 84.09% to 97.72% for NotebookPicker.tsx * Merge test coverage from PR #45 Added comprehensive test suites for core transformation functions: - convert-deepnote-block-to-jupyter-cell.spec.ts (13 tests) - transform-deepnote-yaml-to-notebook-content.spec.ts (11 tests) Overall coverage improved from 31.03% to 57.24% Both transformation files now have 100% line coverage * fix: remove mocks, test real implementation * chore: format * chore: remove out of scope tests * chore: remove out of scope test * refactor: fix test and format * chore: remove out of scope test * fix: address violations * refactor: improve test * refactor: TypeError * fix: drop unnecessary property * refactor: simplify promise * chore: improve "dirty" test --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: dinohamzic <dino@subtlebits.com> * chore: improve test, avoid unnecessary mocks * chore: remove copyright comment * chore: improve test by avoiding any mocking * chore: new line --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: dinohamzic <dino@subtlebits.com>
1 parent 48898e4 commit c85dc4e

File tree

4 files changed

+697
-3
lines changed

4 files changed

+697
-3
lines changed

src/__tests__/NotebookPicker.spec.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) Deepnote
2-
// Distributed under the terms of the Modified BSD License.
3-
41
import type { INotebookModel, NotebookPanel } from '@jupyterlab/notebook';
52
import { framePromise } from '@jupyterlab/testing';
63
import type { PartialJSONObject } from '@lumino/coreutils';
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import type { DeepnoteBlock } from '@deepnote/blocks';
2+
import { convertDeepnoteBlockToJupyterCell } from '../convert-deepnote-block-to-jupyter-cell';
3+
4+
describe('convertDeepnoteBlockToJupyterCell', () => {
5+
describe('code cells', () => {
6+
it('should convert a basic code block to a Jupyter code cell', () => {
7+
const block: DeepnoteBlock = {
8+
id: 'block-1',
9+
type: 'code',
10+
content: 'print("hello")',
11+
metadata: { foo: 'bar' },
12+
sortingKey: '1'
13+
};
14+
15+
const result = convertDeepnoteBlockToJupyterCell(block);
16+
17+
expect(result.cell_type).toBe('code');
18+
expect(result.metadata).toEqual({ foo: 'bar', cell_id: 'block-1' });
19+
expect(result.source).toBe('print("hello")');
20+
expect(result.execution_count).toBeNull();
21+
expect(result.outputs).toEqual([]);
22+
});
23+
24+
it('should include execution count if present', () => {
25+
const block: DeepnoteBlock = {
26+
id: 'block-2',
27+
type: 'code',
28+
content: 'x = 1',
29+
metadata: {},
30+
executionCount: 5,
31+
sortingKey: '1'
32+
};
33+
34+
const result = convertDeepnoteBlockToJupyterCell(block);
35+
36+
expect(result.cell_type).toBe('code');
37+
expect(result.execution_count).toBe(5);
38+
});
39+
40+
it('should include outputs if present', () => {
41+
const blockOutputs = [
42+
{
43+
output_type: 'stream',
44+
name: 'stdout',
45+
text: 'hello\n'
46+
}
47+
];
48+
49+
const block: DeepnoteBlock = {
50+
id: 'block-3',
51+
type: 'code',
52+
content: 'print("hello")',
53+
metadata: {},
54+
outputs: blockOutputs,
55+
sortingKey: '1'
56+
};
57+
58+
const result = convertDeepnoteBlockToJupyterCell(block);
59+
60+
expect(result.cell_type).toBe('code');
61+
expect(result.outputs).toEqual(blockOutputs);
62+
});
63+
64+
it('should remove truncated property from outputs', () => {
65+
const blockOutputs = [
66+
{
67+
output_type: 'stream',
68+
name: 'stdout',
69+
text: 'hello\n',
70+
truncated: true
71+
}
72+
];
73+
74+
const block: DeepnoteBlock = {
75+
id: 'block-4',
76+
type: 'code',
77+
content: 'print("hello")',
78+
metadata: {},
79+
outputs: blockOutputs,
80+
sortingKey: '1'
81+
};
82+
83+
const result = convertDeepnoteBlockToJupyterCell(block);
84+
85+
expect(result.cell_type).toBe('code');
86+
expect(result.outputs).toHaveLength(1);
87+
const resultOutputs = result.outputs as Array<{
88+
output_type: string;
89+
name: string;
90+
text: string;
91+
}>;
92+
expect(resultOutputs[0]).not.toHaveProperty('truncated');
93+
expect(resultOutputs[0]).toEqual({
94+
output_type: 'stream',
95+
name: 'stdout',
96+
text: 'hello\n'
97+
});
98+
});
99+
100+
it('should handle multiple outputs with truncated properties', () => {
101+
const blockOutputs = [
102+
{
103+
output_type: 'stream',
104+
name: 'stdout',
105+
text: 'line1\n',
106+
truncated: true
107+
},
108+
{
109+
output_type: 'stream',
110+
name: 'stdout',
111+
text: 'line2\n',
112+
truncated: false
113+
}
114+
];
115+
116+
const block: DeepnoteBlock = {
117+
id: 'block-5',
118+
type: 'code',
119+
content: 'print("test")',
120+
metadata: {},
121+
outputs: blockOutputs,
122+
sortingKey: '1'
123+
};
124+
125+
const result = convertDeepnoteBlockToJupyterCell(block);
126+
127+
expect(result.cell_type).toBe('code');
128+
expect(result.outputs).toHaveLength(2);
129+
const resultOutputs = result.outputs as Array<{
130+
output_type: string;
131+
name: string;
132+
text: string;
133+
}>;
134+
expect(resultOutputs[0]).not.toHaveProperty('truncated');
135+
expect(resultOutputs[1]).not.toHaveProperty('truncated');
136+
});
137+
138+
it('should not mutate the original block', () => {
139+
const blockOutputs = [
140+
{
141+
output_type: 'stream',
142+
name: 'stdout',
143+
text: 'hello\n',
144+
truncated: true
145+
}
146+
];
147+
148+
const block: DeepnoteBlock = {
149+
id: 'block-6',
150+
type: 'code',
151+
content: 'print("hello")',
152+
metadata: { test: 'value' },
153+
outputs: blockOutputs,
154+
sortingKey: '1'
155+
};
156+
157+
convertDeepnoteBlockToJupyterCell(block);
158+
159+
expect(block.outputs?.[0]).toHaveProperty('truncated');
160+
expect(block.metadata).toEqual({ test: 'value' });
161+
});
162+
});
163+
164+
describe('markdown cells', () => {
165+
it('should convert a basic markdown block to a Jupyter markdown cell', () => {
166+
const block: DeepnoteBlock = {
167+
id: 'block-7',
168+
type: 'markdown',
169+
content: '# Hello',
170+
metadata: { foo: 'bar' },
171+
sortingKey: '1'
172+
};
173+
174+
const result = convertDeepnoteBlockToJupyterCell(block);
175+
176+
expect(result.cell_type).toBe('markdown');
177+
expect(result.metadata).toEqual({});
178+
expect(result.source).toBe('# Hello');
179+
});
180+
181+
it('should convert text-cell-h1 to markdown cell', () => {
182+
const block: DeepnoteBlock = {
183+
id: 'block-8',
184+
type: 'text-cell-h1',
185+
content: 'Heading 1',
186+
metadata: {},
187+
sortingKey: '1'
188+
};
189+
190+
const result = convertDeepnoteBlockToJupyterCell(block);
191+
192+
expect(result.cell_type).toBe('markdown');
193+
});
194+
195+
it('should convert image block to markdown cell', () => {
196+
const block: DeepnoteBlock = {
197+
id: 'block-9',
198+
type: 'image',
199+
content: '![alt](url)',
200+
metadata: {},
201+
sortingKey: '1'
202+
};
203+
204+
const result = convertDeepnoteBlockToJupyterCell(block);
205+
206+
expect(result.cell_type).toBe('markdown');
207+
});
208+
209+
it('should not include metadata from Deepnote block in markdown cells', () => {
210+
const block: DeepnoteBlock = {
211+
id: 'block-10',
212+
type: 'markdown',
213+
content: 'Text',
214+
metadata: { deepnoteMetadata: 'should not appear' },
215+
sortingKey: '1'
216+
};
217+
218+
const result = convertDeepnoteBlockToJupyterCell(block);
219+
220+
expect(result.cell_type).toBe('markdown');
221+
expect(result.metadata).toEqual({});
222+
});
223+
});
224+
225+
describe('special block types', () => {
226+
it('should convert sql block to code cell', () => {
227+
const block: DeepnoteBlock = {
228+
id: 'block-11',
229+
type: 'sql',
230+
content: 'SELECT * FROM table',
231+
metadata: {},
232+
sortingKey: '1'
233+
};
234+
235+
const result = convertDeepnoteBlockToJupyterCell(block);
236+
237+
expect(result.cell_type).toBe('code');
238+
});
239+
240+
it('should convert visualization block to code cell', () => {
241+
const block: DeepnoteBlock = {
242+
id: 'block-12',
243+
type: 'visualization',
244+
content: 'chart_data',
245+
metadata: {},
246+
sortingKey: '1'
247+
};
248+
249+
const result = convertDeepnoteBlockToJupyterCell(block);
250+
251+
expect(result.cell_type).toBe('code');
252+
});
253+
});
254+
});

0 commit comments

Comments
 (0)