Port unit tests from vscode-jupyter to ipynb extension

#129446
This commit is contained in:
Rob Lourens 2021-08-03 10:56:43 -07:00
parent e73053d712
commit 190a89dd1b
3 changed files with 437 additions and 0 deletions

View file

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const testRunner = require('../../../../test/integration/electron/testrunner');
const options: any = {
ui: 'tdd',
color: true,
timeout: 60000
};
// These integration tests is being run in multiple environments (electron, web, remote)
// so we need to set the suite name based on the environment as the suite name is used
// for the test results file name
let suite = '';
if (process.env.VSCODE_BROWSER) {
suite = `${process.env.VSCODE_BROWSER} Browser Integration .ipynb Tests`;
} else if (process.env.REMOTE_VSCODE) {
suite = 'Remote Integration .ipynb Tests';
} else {
suite = 'Integration .ipynb Tests';
}
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
testRunner.configure(options);
export = testRunner;

View file

@ -0,0 +1,394 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { nbformat } from '@jupyterlab/coreutils';
import * as assert from 'assert';
import * as vscode from 'vscode';
import { jupyterNotebookModelToNotebookData } from '../deserializers';
function deepStripProperties(obj: any, props: string[]) {
for (let prop in obj) {
if (obj[prop]) {
delete obj[prop];
} else if (typeof obj[prop] === 'object') {
deepStripProperties(obj[prop], props);
}
}
}
suite('ipynb serializer', () => {
const base64EncodedImage =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOUlZL6DwAB/wFSU1jVmgAAAABJRU5ErkJggg==';
test('Deserialize', async () => {
const cells: nbformat.ICell[] = [
{
cell_type: 'code',
execution_count: 10,
outputs: [],
source: 'print(1)',
metadata: {}
},
{
cell_type: 'markdown',
source: '# HEAD',
metadata: {}
}
];
const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python');
assert.ok(notebook);
const expectedCodeCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, 'print(1)', 'python');
expectedCodeCell.outputs = [];
expectedCodeCell.metadata = { custom: { metadata: {} } };
expectedCodeCell.executionSummary = { executionOrder: 10 };
const expectedMarkdownCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Markup, '# HEAD', 'markdown');
expectedMarkdownCell.outputs = [];
expectedMarkdownCell.metadata = {
custom: { metadata: {} }
};
assert.deepStrictEqual(notebook.cells, [expectedCodeCell, expectedMarkdownCell]);
});
suite('Outputs', () => {
function validateCellOutputTranslation(
outputs: nbformat.IOutput[],
expectedOutputs: vscode.NotebookCellOutput[],
propertiesToExcludeFromComparison: string[] = []
) {
const cells: nbformat.ICell[] = [
{
cell_type: 'code',
execution_count: 10,
outputs,
source: 'print(1)',
metadata: {}
}
];
const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python');
// OutputItems contain an `id` property generated by VSC.
// Exclude that property when comparing.
const propertiesToExclude = propertiesToExcludeFromComparison.concat(['id']);
const actualOuts = notebook.cells[0].outputs;
deepStripProperties(actualOuts, propertiesToExclude);
deepStripProperties(expectedOutputs, propertiesToExclude);
assert.deepStrictEqual(actualOuts, expectedOutputs);
}
test('Empty output', () => {
validateCellOutputTranslation([], []);
});
test('Stream output', () => {
validateCellOutputTranslation(
[
{
output_type: 'stream',
name: 'stderr',
text: 'Error'
},
{
output_type: 'stream',
name: 'stdout',
text: 'NoError'
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('Error')], {
outputType: 'stream'
}),
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('NoError')], {
outputType: 'stream'
})
]
);
});
test('Streamed text with Ansi characters', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '\u001b[K\u001b[33m✅ \u001b[0m Loading\n',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput(
[vscode.NotebookCellOutputItem.stderr('\u001b[K\u001b[33m✅ \u001b[0m Loading\n')],
{
outputType: 'stream'
}
)
]
);
});
test('Streamed text with angle bracket characters', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '1 is < 2',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('1 is < 2')], {
outputType: 'stream'
})
]
);
});
test('Streamed text with angle bracket characters and ansi chars', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput(
[vscode.NotebookCellOutputItem.stderr('1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n')],
{
outputType: 'stream'
}
)
]
);
});
test('Error', async () => {
validateCellOutputTranslation(
[
{
ename: 'Error Name',
evalue: 'Error Value',
traceback: ['stack1', 'stack2', 'stack3'],
output_type: 'error'
}
],
[
new vscode.NotebookCellOutput(
[
vscode.NotebookCellOutputItem.error({
name: 'Error Name',
message: 'Error Value',
stack: ['stack1', 'stack2', 'stack3'].join('\n')
})
],
{
outputType: 'error',
originalError: {
ename: 'Error Name',
evalue: 'Error Value',
traceback: ['stack1', 'stack2', 'stack3'],
output_type: 'error'
}
}
)
]
);
});
['display_data', 'execute_result'].forEach(output_type => {
suite(`Rich output for output_type = ${output_type}`, () => {
// Properties to exclude when comparing.
let propertiesToExcludeFromComparison: string[] = [];
setup(() => {
if (output_type === 'display_data') {
// With display_data the execution_count property will never exist in the output.
// We can ignore that (as it will never exist).
// But we leave it in the case of `output_type === 'execute_result'`
propertiesToExcludeFromComparison = ['execution_count', 'executionCount'];
}
});
test('Text mimeType output', async () => {
validateCellOutputTranslation(
[
{
data: {
'text/plain': 'Hello World!'
},
output_type,
metadata: {},
execution_count: 1
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from('Hello World!', 'utf8'), 'text/plain')],
{
outputType: output_type,
metadata: {}, // display_data & execute_result always have metadata.
executionCount: 1
}
)
],
propertiesToExcludeFromComparison
);
});
test('png,jpeg images', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage,
'image/jpeg': base64EncodedImage
},
metadata: {},
output_type
}
],
[
new vscode.NotebookCellOutput(
[
new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png'),
new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/jpeg')
],
{
executionCount: 1,
outputType: output_type,
metadata: {} // display_data & execute_result always have metadata.
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with a light background', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
needs_background: 'light'
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
needs_background: 'light'
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with a dark background', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
needs_background: 'dark'
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
needs_background: 'dark'
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with custom dimensions', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
'image/png': { height: '111px', width: '999px' }
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
'image/png': { height: '111px', width: '999px' }
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png allowed to scroll', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
unconfined: true,
'image/png': { width: '999px' }
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
unconfined: true,
'image/png': { width: '999px' }
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
});
});
});
});

View file

@ -89,6 +89,9 @@ after_suite
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS
after_suite
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/ipynb --extensionTestsPath=$ROOT/extensions/ipynb/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS
after_suite
# Tests standalone (CommonJS)