Skip to content

Commit 91e2f73

Browse files
subhash-arabhiAchal1607
authored andcommitted
Added tests for mimeType and executionSummary of notebook code
1 parent c7ca6f5 commit 91e2f73

File tree

4 files changed

+327
-0
lines changed

4 files changed

+327
-0
lines changed

vscode/src/test/unit/mocks/vscode/mockVscode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import { URI } from './uri';
1919
import { mockWindowNamespace } from './namespaces/window';
2020
import { mockEnvNamespace } from './namespaces/env';
2121
import { mockedEnums } from './vscodeHostedTypes';
22+
import { NotebookCellOutputItem } from './notebookCellOutputItem';
2223

2324
type VSCode = typeof vscode;
2425
const mockedVSCode: Partial<VSCode> = {};
2526

2627
const mockedVscodeClassesAndTypes = () => {
2728
mockedVSCode.Uri = URI as any;
2829
mockedVSCode.ViewColumn = mockedEnums.viewColumn;
30+
mockedVSCode.NotebookCellOutputItem = NotebookCellOutputItem as any;
2931
}
3032

3133
const mockNamespaces = () => {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export class NotebookCellOutputItem {
18+
public data: Uint8Array;
19+
public mime: string;
20+
private constructor(data: Uint8Array, mime: string) {
21+
this.data = data;
22+
this.mime = mime;
23+
}
24+
public static create(data: Uint8Array, mime: string) {
25+
return new NotebookCellOutputItem(data, mime);
26+
}
27+
public static text(text: string, mime: string) {
28+
const enc = new TextEncoder().encode(text);
29+
return new NotebookCellOutputItem(enc, mime);
30+
}
31+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { expect } from 'chai';
18+
import { ExecutionSummary, ExecutionSummaryData } from '../../../notebooks/executionSummary';
19+
import { describe, it } from 'mocha';
20+
21+
describe('ExecutionSummary', () => {
22+
describe('constructor defaults', () => {
23+
it('defaults to null executionOrder and false success', () => {
24+
const es = new ExecutionSummary();
25+
expect(es.executionOrder).to.be.null;
26+
expect(es.success).to.equal(false);
27+
});
28+
29+
it('accepts explicit values', () => {
30+
const es = new ExecutionSummary(3, true);
31+
expect(es.executionOrder).to.equal(3);
32+
expect(es.success).to.equal(true);
33+
34+
const es2 = new ExecutionSummary(0, false);
35+
expect(es2.executionOrder).to.equal(0);
36+
expect(es2.success).to.equal(false);
37+
});
38+
});
39+
40+
describe('static fromMetadata()', () => {
41+
it('returns defaults when meta and fallback are undefined', () => {
42+
const es = ExecutionSummary.fromMetadata();
43+
expect(es.executionOrder).to.be.null;
44+
expect(es.success).to.equal(false);
45+
});
46+
47+
it('uses fallbackExecCount when meta.executionOrder is undefined', () => {
48+
const es = ExecutionSummary.fromMetadata({}, 7);
49+
expect(es.executionOrder).to.equal(7);
50+
expect(es.success).to.equal(false);
51+
});
52+
53+
it('uses fallbackExecCount when meta.executionOrder is null', () => {
54+
const meta: ExecutionSummaryData = { executionOrder: null };
55+
const es = ExecutionSummary.fromMetadata(meta, 12);
56+
expect(es.executionOrder).to.equal(12);
57+
expect(es.success).to.equal(false);
58+
});
59+
60+
it('ignores fallback when meta.executionOrder is provided', () => {
61+
const meta: ExecutionSummaryData = { executionOrder: 5, success: true };
62+
const es = ExecutionSummary.fromMetadata(meta, 10);
63+
expect(es.executionOrder).to.equal(5);
64+
expect(es.success).to.equal(true);
65+
});
66+
67+
it('defaults success to false if meta.success is undefined', () => {
68+
const meta: ExecutionSummaryData = { executionOrder: 2 };
69+
const es = ExecutionSummary.fromMetadata(meta, null);
70+
expect(es.executionOrder).to.equal(2);
71+
expect(es.success).to.equal(false);
72+
});
73+
74+
it('respects meta.success when false explicitly', () => {
75+
const meta: ExecutionSummaryData = { executionOrder: 8, success: false };
76+
const es = ExecutionSummary.fromMetadata(meta, 1);
77+
expect(es.executionOrder).to.equal(8);
78+
expect(es.success).to.equal(false);
79+
});
80+
});
81+
82+
describe('toJSON()', () => {
83+
it('serializes current state to ExecutionSummaryData', () => {
84+
const es = new ExecutionSummary(9, true);
85+
const json = es.toJSON();
86+
expect(json).to.deep.equal({ executionOrder: 9, success: true });
87+
});
88+
89+
it('handles null executionOrder serialization', () => {
90+
const es = new ExecutionSummary(null, false);
91+
const json = es.toJSON();
92+
expect(json).to.deep.equal({ executionOrder: null, success: false });
93+
});
94+
95+
it('round-trips fromMetadata → toJSON', () => {
96+
const meta: ExecutionSummaryData = { executionOrder: 15, success: true };
97+
const es = ExecutionSummary.fromMetadata(meta, 20);
98+
const json = es.toJSON();
99+
expect(json).to.deep.equal(meta);
100+
});
101+
102+
it('round-trips fallbackExecCount when meta missing', () => {
103+
const es = ExecutionSummary.fromMetadata(undefined, 33);
104+
const json = es.toJSON();
105+
expect(json).to.deep.equal({ executionOrder: 33, success: false });
106+
});
107+
});
108+
});
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { expect } from 'chai';
18+
import { Buffer } from 'buffer';
19+
import { NotebookCellOutputItem } from 'vscode';
20+
import { MimeTypeHandler } from '../../../notebooks/mimeTypeHandler';
21+
import { mimeTypes } from '../../../notebooks/constants';
22+
import { IMimeBundle } from '../../../notebooks/types';
23+
import { describe, it } from 'mocha';
24+
25+
describe('MimeTypeHandler', () => {
26+
27+
function decodeData(item: NotebookCellOutputItem): string {
28+
const bytes: Uint8Array = (item as any).data;
29+
return new TextDecoder().decode(bytes);
30+
}
31+
32+
describe('getters isText / isImage', () => {
33+
it('isText ⇢ true only for mimeTypes.TEXT', () => {
34+
expect(new MimeTypeHandler(mimeTypes.TEXT).isText).to.be.true;
35+
expect(new MimeTypeHandler('image/jpeg').isText).to.be.false;
36+
});
37+
38+
it('isImage ⇢ true only for image/*', () => {
39+
expect(new MimeTypeHandler('image/png').isImage).to.be.true;
40+
expect(new MimeTypeHandler(mimeTypes.TEXT).isImage).to.be.false;
41+
});
42+
43+
});
44+
45+
describe('static toBytes()', () => {
46+
it('decodes a base64 string into the original Uint8Array', () => {
47+
const text = 'Hello, 世界!';
48+
const b64 = Buffer.from(text).toString('base64');
49+
const out = MimeTypeHandler.toBytes(b64);
50+
expect(out).to.be.instanceOf(Uint8Array);
51+
expect(Buffer.from(out).toString()).to.equal(text);
52+
});
53+
54+
it('returns the same Uint8Array when passed through', () => {
55+
const arr = new Uint8Array([1, 2, 3]);
56+
expect(MimeTypeHandler.toBytes(arr)).to.equal(arr);
57+
});
58+
59+
it('decodes empty string to empty Uint8Array', () => {
60+
const out = MimeTypeHandler.toBytes('');
61+
expect(out).to.be.instanceOf(Uint8Array);
62+
expect(out.length).to.equal(0);
63+
});
64+
});
65+
66+
describe('static toString()', () => {
67+
it('returns the same string if input is already string', () => {
68+
const s = 'just a test';
69+
expect(MimeTypeHandler.toString(s)).to.equal(s);
70+
});
71+
72+
it('decodes a Uint8Array into a string via TextDecoder', () => {
73+
const text = '¡Hola!';
74+
const encoder = new TextEncoder();
75+
const arr = encoder.encode(text);
76+
expect(MimeTypeHandler.toString(arr)).to.equal(text);
77+
});
78+
79+
it('decodes empty Uint8Array to empty string', () => {
80+
const arr = new Uint8Array([]);
81+
expect(MimeTypeHandler.toString(arr)).to.equal('');
82+
});
83+
});
84+
85+
describe('makeOutputItem()', () => {
86+
it('for TEXT builds a NotebookCellOutputItem containing the UTF-8 bytes of the string', () => {
87+
const handler = new MimeTypeHandler(mimeTypes.TEXT);
88+
const item = handler.makeOutputItem('plain text');
89+
expect(item).to.be.instanceOf(NotebookCellOutputItem);
90+
expect((item as any).mime).to.equal(mimeTypes.TEXT);
91+
expect(decodeData(item)).to.equal('plain text');
92+
});
93+
94+
it('handles empty text payload correctly', () => {
95+
const handler = new MimeTypeHandler(mimeTypes.TEXT);
96+
const item = handler.makeOutputItem('');
97+
expect(decodeData(item)).to.equal('');
98+
expect((item as any).data.length).to.equal(0);
99+
});
100+
101+
it('for image/* with a base64 string decodes back to the original bytes', () => {
102+
const raw = new Uint8Array([10, 20, 30]);
103+
const b64 = Buffer.from(raw).toString('base64');
104+
const handler = new MimeTypeHandler('image/png');
105+
const item = handler.makeOutputItem(b64);
106+
expect((item as any).mime).to.equal('image/png');
107+
const got = (item as any).data as Uint8Array;
108+
expect(Array.from(got)).to.deep.equal(Array.from(raw));
109+
});
110+
111+
it('for image/* with a Uint8Array leaves the bytes untouched', () => {
112+
const raw = new Uint8Array([5, 6, 7]);
113+
const handler = new MimeTypeHandler('image/gif');
114+
const item = handler.makeOutputItem(raw);
115+
expect((item as any).mime).to.equal('image/gif');
116+
expect((item as any).data).to.equal(raw);
117+
});
118+
119+
it('unknown mime routes through text branch', () => {
120+
const handler = new MimeTypeHandler('application/xml');
121+
const payload = '<note/>';
122+
const item = handler.makeOutputItem(payload);
123+
expect((item as any).mime).to.equal('application/xml');
124+
expect(decodeData(item)).to.equal(payload);
125+
});
126+
});
127+
128+
describe('static itemsFromBundle()', () => {
129+
it('filters to only text & image entries and decodes each correctly', () => {
130+
const rawImg = new Uint8Array([1, 2, 3]);
131+
const b64img = Buffer.from(rawImg).toString('base64');
132+
const bundle: IMimeBundle = {
133+
[mimeTypes.TEXT]: 'foo',
134+
'image/png': b64img,
135+
'application/json': '{"x":1}',
136+
};
137+
138+
const items = MimeTypeHandler.itemsFromBundle(bundle);
139+
expect(items).to.have.length(2);
140+
141+
const textItem = items.find(i => (i as any).mime === mimeTypes.TEXT)!;
142+
expect(decodeData(textItem)).to.equal('foo');
143+
144+
const imgItem = items.find(i => (i as any).mime === 'image/png')!;
145+
const gotBytes = (imgItem as any).data as Uint8Array;
146+
expect(Array.from(gotBytes)).to.deep.equal(Array.from(rawImg));
147+
});
148+
149+
it('joins string-array values before creating the output item', () => {
150+
const bundle: IMimeBundle = {
151+
[mimeTypes.TEXT]: ['a', 'b', 'c'],
152+
};
153+
const items = MimeTypeHandler.itemsFromBundle(bundle);
154+
expect(items).to.have.length(1);
155+
const only = items[0];
156+
expect(decodeData(only)).to.equal('abc');
157+
expect((only as any).mime).to.equal(mimeTypes.TEXT);
158+
});
159+
160+
it('joins base64-string-array for image payload', () => {
161+
// "SGVs" + "bG8=" => "SGVsbG8=" which decodes to "Hello"
162+
const segs = ['SGVs', 'bG8='];
163+
const bundle: IMimeBundle = {
164+
'image/png': segs,
165+
};
166+
const items = MimeTypeHandler.itemsFromBundle(bundle);
167+
expect(items).to.have.length(1);
168+
const only = items[0];
169+
expect((only as any).mime).to.equal('image/png');
170+
expect(decodeData(only)).to.equal('Hello');
171+
});
172+
173+
it('empty bundle yields no items', () => {
174+
const items = MimeTypeHandler.itemsFromBundle({});
175+
expect(items).to.be.empty;
176+
});
177+
178+
it('unsupported mime types are dropped', () => {
179+
const bundle: IMimeBundle = {
180+
'application/json': '[1,2,3]',
181+
'video/mp4': 'abcd',
182+
};
183+
expect(MimeTypeHandler.itemsFromBundle(bundle)).to.be.empty;
184+
});
185+
});
186+
});

0 commit comments

Comments
 (0)