Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submit tests for intellisense (replacing functional tests we had) #7565

Merged
merged 37 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e2f2fed
Tests working independently
rchiodo Sep 1, 2021
da73812
Use dev build of python extension
rchiodo Sep 8, 2021
93b9b06
Fix build error
rchiodo Sep 8, 2021
8709e6b
Do it without azure login
rchiodo Sep 16, 2021
029a180
Add some more logging
rchiodo Sep 16, 2021
add4ede
Add more logging for fetching completions
rchiodo Sep 16, 2021
9b49dab
Add more logging and wait some in the test
rchiodo Sep 16, 2021
75ef2bf
Update logging
rchiodo Sep 16, 2021
8a2b7fd
Fix linter
rchiodo Sep 16, 2021
9e4b56a
Dont use old jedi
rchiodo Sep 16, 2021
c9801c2
See if csv is actually loading
rchiodo Sep 16, 2021
b8be50b
Print out current folder during test
rchiodo Sep 16, 2021
fa76afe
Cell output not in string form
rchiodo Sep 16, 2021
7a20d2a
Fix read csv code to work with default notebookFileRoot
rchiodo Sep 17, 2021
87cf668
Add screenshot and some more logging (looks like execution isn't work…
rchiodo Sep 17, 2021
d549f32
Actually capture a screenshot
rchiodo Sep 17, 2021
997320f
Skip another test and make sure selected controller is accurate
rchiodo Sep 17, 2021
4123cf1
Fix compile errors
rchiodo Sep 17, 2021
9d6f251
Add more logging and try turning off python extension experiments a d…
rchiodo Sep 20, 2021
a63969d
Add verbose logging to VS code
rchiodo Sep 20, 2021
852a557
Add VS code logs
rchiodo Sep 20, 2021
4657383
Change the way waitForKernelAutoSelect works. Have it loop.
rchiodo Sep 20, 2021
1fc9570
Update timeout for affinity. Can't wait too long
rchiodo Sep 20, 2021
a329aea
Fix non python cases
rchiodo Sep 20, 2021
787ac1f
Refactor how selection changes occur
rchiodo Sep 21, 2021
13149c8
Fix some test problems and more logging
rchiodo Sep 21, 2021
5600040
Two more fixes
rchiodo Sep 21, 2021
c54ea19
Merge remote-tracking branch 'origin/main' into rchiodo/intellisense_…
rchiodo Sep 21, 2021
6bc65f8
Disable some more tests that actually found a bug. Update variable vi…
rchiodo Sep 21, 2021
b830e93
Rework how preferred is cached
rchiodo Sep 22, 2021
2166345
Switch back to stable python
rchiodo Sep 22, 2021
23f5559
Fix linter
rchiodo Sep 22, 2021
a93bf3a
Comment out intellisense tests for now
rchiodo Sep 22, 2021
19ecf09
Rework how notebook controller is found for the interactive window. T…
rchiodo Sep 22, 2021
9755d6c
disable some more tests and try to see why interactive test is failing
rchiodo Sep 22, 2021
0183f64
Add screenshot to test failure
rchiodo Sep 22, 2021
e3ef4a7
Dont check env name, may not be there
rchiodo Sep 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ env:
IPYWIDGET_SCREENSHOT_PATH: '*-screenshot.png'
DISABLE_INSIDERS_EXTENSION: 1 # Disable prompts to install Insiders in tests (else it blocks activation of extension).
VSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE: true
VSIX_NAME_PYTHON: 'ms-python-insiders.vsix'

jobs:
pick_environment:
Expand Down Expand Up @@ -399,6 +400,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Download Python VSIX
run: az storage blob download --file ${{env.VSIX_NAME_PYTHON}} --account-name pvsc --container-name extension-builds --name ${{ env.VSIX_NAME_PYTHON }}
if: matrix.test-suite == 'integration' || matrix.test-suite == 'notebook'

- name: Use Python ${{matrix.pythonVersion}}
uses: actions/setup-python@v2
if: matrix.python != 'conda' && matrix.python != 'noPython'
Expand Down Expand Up @@ -505,7 +510,6 @@ jobs:

# debugpy is not shipped, only installed for local tests.
# In production, we get debugpy from python extension.
# Use specific version of Jedi https://github.com/ipython/ipython/issues/12740
- name: Install Python Libs
if: matrix.python != 'conda' && matrix.python != 'noPython'
run: |
Expand All @@ -521,10 +525,7 @@ jobs:
python -m pip install --upgrade -r ./build/ipython-test-requirements.txt
python -m pip install --upgrade -r ./build/conda-functional-requirements.txt
python -m ipykernel install --user
python -m pip uninstall jedi --yes
python -m pip install jedi==0.17.2

# Use specific version of Jedi https://github.com/ipython/ipython/issues/12740
- name: Install Python Libs for conda
shell: bash -l {0}
if: matrix.python == 'conda'
Expand All @@ -535,8 +536,6 @@ jobs:
python ./pythonFiles/install_debugpy.py
conda env update --file ./build/conda-functional-requirements.yml
python -m pip install --upgrade -r ./build/conda-functional-requirements.txt
python -m pip uninstall jedi --yes
python -m pip install jedi==0.17.2
beakerx_kernel_java install
conda install pytorch cpuonly -c pytorch

Expand Down Expand Up @@ -606,6 +605,11 @@ jobs:
uses: ./.github/actions/create-venv-for-tests
if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.os != 'windows-latest' && matrix.jupyter != 'remote'

- name: Create temp folder for user data dir
run: |
echo "VSC_JUPYTER_USER_DATA_DIR=$(mktemp -d)" >> $GITHUB_ENV
echo ${{env.VSC_JUPYTER_USER_DATA_DIR}} is user data dir

# Set the correct xvfb commands to run vscode tests
# https://code.visualstudio.com/api/working-with-extensions/continuous-integration
# Note that xvfb github action only runs through xvfb on linux, so only set the commands there
Expand Down Expand Up @@ -724,6 +728,14 @@ jobs:
path: './${{env.IPYWIDGET_SCREENSHOT_PATH}}'
retention-days: 1

- name: Upload VS code logs
uses: actions/upload-artifact@v2
if: failure()
with:
name: VSCodeLogs-${{matrix.os}}-${{matrix.pythonVersion}}-${{matrix.python}}-${{matrix.jupyter}}-${{matrix.test-suite}}
path: '${{env.VSC_JUPYTER_USER_DATA_DIR}}/logs/**/*'
retention-days: 1

smoke-tests:
timeout-minutes: 30
name: Smoke tests
Expand Down
1 change: 1 addition & 0 deletions src/client/datascience/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const JupyterDaemonModule = 'vscode_datascience_helpers.jupyter_daemon';
export const KernelLauncherDaemonModule = 'vscode_datascience_helpers.kernel_launcher_daemon';

export const PythonExtension = 'ms-python.python';
export const PylanceExtension = 'ms-python.vscode-pylance';

export const LanguagesSupportedByPythonkernel = [
'python',
Expand Down
9 changes: 8 additions & 1 deletion src/client/datascience/jupyter/jupyterNotebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ServerStatus } from '../../../datascience-ui/interactive-common/mainSta
import { IApplicationShell, IWorkspaceService } from '../../common/application/types';
import { CancellationError, createPromiseFromCancellation } from '../../common/cancellation';
import '../../common/extensions';
import { traceError, traceInfo, traceWarning } from '../../common/logger';
import { traceError, traceInfo, traceInfoIf, traceWarning } from '../../common/logger';

import { IConfigurationService, IDisposableRegistry, Resource } from '../../common/types';
import { createDeferred, Deferred } from '../../common/utils/async';
Expand Down Expand Up @@ -44,6 +44,7 @@ import { PythonEnvironment } from '../../pythonEnvironments/info';
import { handleTensorBoardDisplayDataOutput } from '../notebook/helpers/executionHelpers';
import { getInterpreterFromKernelConnectionMetadata, isPythonKernelConnection } from './kernels/helpers';
import { executeSilently } from './kernels/kernel';
import { isCI } from '../../common/constants';

class CellSubscriber {
public get startTime(): number {
Expand Down Expand Up @@ -387,6 +388,12 @@ export class JupyterNotebookBase implements INotebook {
}),
createPromiseFromCancellation({ defaultValue: undefined, cancelAction: 'resolve', token: cancelToken })
]);
traceInfoIf(
isCI,
`Got jupyter notebook completions. Is cancel? ${cancelToken?.isCancellationRequested}: ${
result ? JSON.stringify(result) : 'empty'
}`
);
if (result && result.content) {
if ('matches' in result.content) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class NotebookCompletionProvider implements CompletionItemProvider {
// Allow slower timeouts for CI (testing).
const timeout =
parseInt(process.env.VSC_JUPYTER_IntellisenseTimeout || '0', 10) || Settings.IntellisenseTimeout;
traceInfoIf(isCI, `Notebook completion request for ${document.getText()}, ${document.offsetAt(position)}`);
const result = await Promise.race([
notebook.getCompletion(document.getText(), document.offsetAt(position), token),
sleep(timeout).then(() => {
Expand All @@ -76,7 +77,10 @@ export class NotebookCompletionProvider implements CompletionItemProvider {
})
]);
if (!result) {
traceInfoIf(isCI, `Notebook completions not found.`);
return [];
} else {
traceInfoIf(isCI, `Completions found, filtering the list: ${JSON.stringify(result)}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just turn this into traceIfCI, lol, feels kinda redundant to always pass in isCI. What do you think.
We might have a few places where we'd have some other conditions being passed in, but i think traceIfCIortraceOnCI` is simpler.
Absolutely no need to make that change, just a thought

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sounds like a good idea. I'll add a debt issue

}
const experimentMatches = result.metadata ? result.metadata._jupyter_types_experimental : [];
// Check if we have more information about the complication items & whether its valid.
Expand Down
4 changes: 3 additions & 1 deletion src/client/datascience/notebook/notebookControllerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ export class NotebookControllerManager implements INotebookControllerManager, IE
Telemetry.FailedToCreateNotebookController,
undefined,
{ kind: kernelConnection.kind },
ex,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ex as any,
true
);
traceError(`Failed to create notebook controller for ${kernelConnection.id}`, ex);
Expand All @@ -475,6 +476,7 @@ export class NotebookControllerManager implements INotebookControllerManager, IE
notebook: NotebookDocument;
controller: VSCodeNotebookController;
}) {
traceInfoIf(isCI, `Controller ${event.controller?.id} selected`);
// Now notify out that we have updated a notebooks controller
this._onNotebookControllerSelected.fire(event);
}
Expand Down
46 changes: 12 additions & 34 deletions src/client/datascience/notebook/vscodeNotebookController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { join } from 'path';
import {
Disposable,
EventEmitter,
ExtensionMode,
languages,
NotebookCell,
NotebookCellKind,
Expand All @@ -17,7 +16,7 @@ import {
Uri
} from 'vscode';
import { ICommandManager, IDocumentManager, IVSCodeNotebook, IWorkspaceService } from '../../common/application/types';
import { isCI, JVSC_EXTENSION_ID, PYTHON_LANGUAGE } from '../../common/constants';
import { isCI, PYTHON_LANGUAGE } from '../../common/constants';
import { disposeAllDisposables } from '../../common/helpers';
import { traceInfo, traceInfoIf } from '../../common/logger';
import {
Expand Down Expand Up @@ -49,7 +48,7 @@ import {
} from '../telemetry/telemetry';
import { KernelSocketInformation } from '../types';
import { NotebookCellLanguageService } from './cellLanguageService';
import { InteractiveWindowView, JupyterNotebookView } from './constants';
import { InteractiveWindowView } from './constants';
import { isJupyterNotebook, traceCellMessage, updateNotebookDocumentMetadata } from './helpers/helpers';

export class VSCodeNotebookController implements Disposable {
Expand Down Expand Up @@ -86,7 +85,12 @@ export class VSCodeNotebookController implements Disposable {
public isAssociatedWithDocument(doc: NotebookDocument) {
return this.associatedDocuments.has(doc);
}

public isPreferred(doc: NotebookDocument) {
return this.preferredWith.has(doc);
}
private readonly associatedDocuments = new WeakSet<NotebookDocument>();
private readonly preferredWith = new WeakSet<NotebookDocument>();
constructor(
private readonly kernelConnection: KernelConnectionMetadata,
id: string,
Expand Down Expand Up @@ -153,10 +157,10 @@ export class VSCodeNotebookController implements Disposable {
public async updateNotebookAffinity(notebook: NotebookDocument, affinity: NotebookControllerAffinity) {
traceInfo(`Setting controller affinity for ${notebook.uri.toString()} ${this.id}`);
this.controller.updateNotebookAffinity(notebook, affinity);
// Only when running tests should we force the selection of the kernel.
// Else the general VS Code behavior is for the user to select a kernel (here we make it look as though use selected it).
if (this.context.extensionMode === ExtensionMode.Test) {
await this.setAsActiveControllerForTests(notebook);

// Keep track of if this controller is preferred for this notebook
if (affinity === NotebookControllerAffinity.Preferred) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateNotebookAffinity is called on each document open if you do something like this:
Open foo.ipynb (A kernel in metadata) foo.ipynb added to A perferredWith
Change to kernel B in foo.ipynb
Execute
Close foo.ipynb
Open foo.ipynb (B kernel in metadata) foo.ipynb added to B preferredWith

Seems like that situation would be wrong then if I'm following right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not following the mistake? Because A would still be preferred? Yeah seems like map should be the other way. Map between notebook (key) and controller (value)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, trying to make sure that I'm reading it right. As I see it now PreferredWith is all the notebook documents that this controller is the preferred controller for. Given that the preferred controller could change if you execute and reopen a document you could have the same document in the preferred list for two different controllers. As documents only have one preferred (at least from our extensions perspective) that situation wouldn't be right.

this.preferredWith.add(notebook);
}
}

Expand All @@ -178,6 +182,7 @@ export class VSCodeNotebookController implements Disposable {
await Promise.all(cells.map((cell) => this.executeCell(notebook, cell)));
}
private async onDidChangeSelectedNotebooks(event: { notebook: NotebookDocument; selected: boolean }) {
traceInfoIf(isCI, `Notebook Controller base event called for ${this.id}. Selected ${event.selected} `);
if (this.associatedDocuments.has(event.notebook) && event.selected) {
// Possible it gets called again in our tests (due to hacks for testing purposes).
return;
Expand Down Expand Up @@ -408,31 +413,4 @@ export class VSCodeNotebookController implements Disposable {
await newKernel.start({ disableUI: true }).catch(noop);
}
}
/**
* In our tests, preferred controllers are setup as the active controller.
*
* This method is called on when running tests, else in the real world,
* users need to select a kernel (preferred is on top of the list).
*/
private async setAsActiveControllerForTests(notebook: NotebookDocument) {
DonJayamanne marked this conversation as resolved.
Show resolved Hide resolved
// Only when running tests should we force the selection of the kernel.
// Else the general VS Code behavior is for the user to select a kernel (here we make it look as though use selected it).
if (this.context.extensionMode !== ExtensionMode.Test || notebook.notebookType !== JupyterNotebookView) {
return;
}
traceInfoIf(isCI, `Command notebook.selectKernel executing for ${notebook.uri.toString()} ${this.id}`);
await this.commandManager.executeCommand('notebook.selectKernel', {
id: this.id,
extension: JVSC_EXTENSION_ID
});
traceInfoIf(isCI, `Command notebook.selectKernel exected for ${notebook.uri.toString()} ${this.id}`);
// Used in tests to determine when the controller has been associated with a document.
VSCodeNotebookController.kernelAssociatedWithDocument = true;

// Sometimes the selection doesn't work (after all this is a hack).
if (!this.associatedDocuments.has(notebook)) {
this.associatedDocuments.add(notebook);
this._onNotebookControllerSelected.fire({ notebook, controller: this });
}
}
}
12 changes: 2 additions & 10 deletions src/test/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,7 @@
"python.workspaceSymbols.enabled": false,
"python.testing.nosetestArgs": [],
"python.testing.pytestArgs": [],
"python.testing.unittestArgs": [
"-s=./tests",
"-p=test_*.py",
"-v",
"-s",
".",
"-p",
"*test*.py"
],
"python.testing.unittestArgs": ["-s=./tests", "-p=test_*.py", "-v", "-s", ".", "-p", "*test*.py"],
"python.sortImports.args": [],
"python.linting.lintOnSave": false,
"python.linting.enabled": true,
Expand All @@ -27,5 +19,5 @@
// Do not set this to "Microsoft", else it will result in LS being downloaded on CI
// and that slows down tests significantly. We have other tests on CI for testing
// downloading of LS with this setting enabled.
"python.languageServer": "Jedi"
"python.languageServer": "Pylance"
}
11 changes: 6 additions & 5 deletions src/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { IDisposable, IJupyterSettings } from '../client/common/types';
import { IServiceContainer, IServiceManager } from '../client/ioc/types';
import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_MULTI_ROOT_TEST, IS_PERF_TEST, IS_SMOKE_TEST } from './constants';
import { noop, sleep } from './core';
import { IS_CI_SERVER } from './ciConstants';
import { isCI } from '../client/common/constants';

const StreamZip = require('node-stream-zip');

Expand Down Expand Up @@ -513,7 +513,8 @@ export function clearPendingTimers() {
export async function waitForCondition(
condition: () => Promise<boolean>,
timeoutMs: number,
errorMessage: string | (() => string)
errorMessage: string | (() => string),
intervalTimeoutMs: number = 10
): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
const timeout = setTimeout(() => {
Expand All @@ -531,7 +532,7 @@ export async function waitForCondition(
clearTimeout(timeout);
clearInterval(timer);
resolve();
}, 10);
}, intervalTimeoutMs);
pendingTimers.push(timer);
pendingTimers.push(timeout);
});
Expand All @@ -550,7 +551,7 @@ export async function retryIfFail<T>(fn: () => Promise<T>, timeoutMs: number = 6
// Capture result, if no exceptions return that.
return result;
} catch (ex) {
lastEx = ex;
lastEx = ex as any;
}
await sleep(10);
}
Expand Down Expand Up @@ -738,7 +739,7 @@ export function arePathsSame(path1: string, path2: string) {
* If there's a failure, it will be logged (errors are swallowed).
*/
export async function captureScreenShot(fileNamePrefix: string) {
if (!IS_CI_SERVER) {
if (!isCI) {
return;
}
const name = `${fileNamePrefix}_${uuid()}`.replace(/[\W]+/g, '_');
Expand Down
10 changes: 5 additions & 5 deletions src/test/datascience/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
"python.linting.banditEnabled": false,
"python.formatting.provider": "yapf",
"python.pythonPath": "python",
"jupyter.experiments.optInto": [
"NativeNotebookEditor"
],
"jupyter.experiments.optInto": ["NativeNotebookEditor"],
// Do not set this to "Microsoft", else it will result in LS being downloaded on CI
// and that slows down tests significantly. We have other tests on CI for testing
// downloading of LS with this setting enabled.
"python.languageServer": "Jedi",
"python.languageServer": "Pylance",
// Ensure auto save is off.
"files.autoSave": "off",
//If enabled, ensure we save immediately.
Expand All @@ -27,5 +25,7 @@
"jupyter.disableJupyterAutoStart": true,
"jupyter.logging.level": "debug",
"python.logging.level": "debug",
"webview.experimental.useIframes": true
"webview.experimental.useIframes": true,
// Disable all experiments in python insiders
"python.experiments.enabled": "false"
}
10 changes: 3 additions & 7 deletions src/test/datascience/debugger.vscode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ import {
runCell,
getDebugSessionAndAdapter
} from './notebook/helper';
import { verifyViewVariables } from './variableView/variableViewHelpers';
import { ITestVariableViewProvider } from './variableView/variableViewTestInterfaces';
import { traceInfo } from '../../client/common/logger';
import { IDebuggingManager } from '../../client/debugger/types';
import { assert } from 'chai';
import { debug } from 'vscode';
import { OnMessageListener } from './vscodeTestHelpers';
import { ITestWebviewHost } from './testInterfaces';
import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes';
import { DebugProtocol } from 'vscode-debugprotocol';
import { sleep } from '../../client/common/utils/async';
import { waitForVariablesToMatch } from './variableView/variableViewHelpers';

suite('VSCode Notebook - Run By Line', function () {
let api: IExtensionTestApi;
Expand Down Expand Up @@ -89,6 +87,7 @@ suite('VSCode Notebook - Run By Line', function () {
suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables));

test('Stops at end of cell', async function () {
// Run by line seems to end up on the second line of the function, not the first
await insertCodeCell('a=1\na', { index: 0 });
const doc = vscodeNotebook.activeNotebookEditor?.document!;
const cell = doc.getCells()[0];
Expand All @@ -106,15 +105,12 @@ suite('VSCode Notebook - Run By Line', function () {

const coreVariableView = await variableViewProvider.activeVariableView;
const variableView = (coreVariableView as unknown) as ITestWebviewHost;
const onMessageListener = new OnMessageListener(variableView);

void commandManager.executeCommand(Commands.RunByLineNext, cell);
await waitForStoppedEvent(debugAdapter!);
await onMessageListener.waitForMessage(InteractiveWindowMessages.VariablesComplete);

const htmlResult = await variableView?.getHTMLById('variable-view-main-panel');
const expectedVariables = [{ name: 'a', type: 'int', length: '', value: '1' }];
verifyViewVariables(expectedVariables, htmlResult);
await waitForVariablesToMatch(expectedVariables, variableView);

await commandManager.executeCommand(Commands.RunByLineNext, cell);
await waitForCondition(
Expand Down
6 changes: 5 additions & 1 deletion src/test/datascience/interactiveWindow.vscode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ suite('Interactive window', async () => {
await waitForCondition(async () => assertHasTextOutputInVSCode(cell, 'foo'), 15_000, 'Incorrect output');
});

test('Clear output', async () => {
test('Clear output', async function () {
// Test failing after using python insiders. Not getting expected
// output
// https://github.com/microsoft/vscode-jupyter/issues/7580
this.skip();
const text = `from IPython.display import clear_output
for i in range(10):
clear_output()
Expand Down
Loading