Files
vscode/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts
Connor Peet 8730f56c09 eng: add io warmup for webkit tests in ci (#193711)
Should fix the issue that @roblourens and @Yoyokrazy were hitting with snapshot tests on macOS WebKit in CI. Not pretty, but I'd rather do this than spend a bunch of time chasing down something that certainly seems to be a browser issue.
2023-09-21 11:03:20 -07:00

390 lines
16 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { mock } from 'vs/base/test/common/mock';
import { assertSnapshot } from 'vs/base/test/common/snapshot';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { NotebookCellOutline } from 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline';
import { INotebookEditor, INotebookEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry';
import { NotebookStickyLine, computeContent } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { createNotebookCellList, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor';
import { OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
suite('NotebookEditorStickyScroll', () => {
let disposables: DisposableStore;
let instantiationService: TestInstantiationService;
const domNode: HTMLElement = document.createElement('div');
teardown(() => {
disposables.dispose();
});
ensureNoDisposablesAreLeakedInTestSuite();
setup(() => {
disposables = new DisposableStore();
instantiationService = setupInstantiationService(disposables);
});
function getOutline(editor: any) {
if (!editor.hasModel()) {
assert.ok(false, 'MUST have active text editor');
}
const outline = instantiationService.createInstance(NotebookCellOutline, new class extends mock<INotebookEditorPane>() {
override getControl() {
return editor;
}
override onDidChangeModel: Event<void> = Event.None;
}, OutlineTarget.QuickPick);
return outline;
}
function nbStickyTestHelper(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[], disposables: Pick<DisposableStore, 'add'>) {
const output = computeContent(domNode, notebookEditor, notebookCellList, notebookOutlineEntries);
for (const stickyLine of output.values()) {
disposables.add(stickyLine.line);
}
return createStickyTestElement(output.values());
}
function createStickyTestElement(stickyLines: IterableIterator<{ line: NotebookStickyLine; rendered: boolean }>) {
const outputElements = [];
for (const stickyLine of stickyLines) {
if (stickyLine.rendered) {
outputElements.unshift(stickyLine.line.element.innerText);
}
}
return outputElements;
}
test('test0: should render empty, scrollTop at 0', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}],
['## header aa', 'markdown', CellKind.Markup, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['# header b', 'markdown', CellKind.Markup, [], {}],
['var c = 2;', 'javascript', CellKind.Code, [], {}]
],
async (editor, viewModel) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 8 }, () => false),
editorViewStates: Array.from({ length: 8 }, () => null),
cellTotalHeights: Array.from({ length: 8 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = disposables.add(createNotebookCellList(instantiationService, disposables));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(0);
editor.visibleRanges = [{ start: 0, end: 8 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, disposables);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
test('test1: should render 0->1, visible range 3->8', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 100
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
['# header b', 'markdown', CellKind.Markup, [], {}], // 300
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 350
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 8 }, () => false),
editorViewStates: Array.from({ length: 8 }, () => null),
cellTotalHeights: Array.from({ length: 8 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(175);
editor.visibleRanges = [{ start: 3, end: 8 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
test('test2: should render 0, visible range 6->9 so collapsing next 2 against following section', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300
['# header b', 'markdown', CellKind.Markup, [], {}], // 350
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 400
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 9 }, () => false),
editorViewStates: Array.from({ length: 9 }, () => null),
cellTotalHeights: Array.from({ length: 9 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(325); // room for a single header
editor.visibleRanges = [{ start: 6, end: 9 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
test('test3: should render 0->1, collapsing against equivalent level header', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
['### header aab', 'markdown', CellKind.Markup, [], {}],// 200
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 350
['# header b', 'markdown', CellKind.Markup, [], {}], // 400
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 450
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 10 }, () => false),
editorViewStates: Array.from({ length: 10 }, () => null),
cellTotalHeights: Array.from({ length: 10 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(175); // room for a single header
editor.visibleRanges = [{ start: 3, end: 10 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
// outdated/improper behavior
test.skip('test4: should render 0, scrolltop halfway through cell 0', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}],
['## header aa', 'markdown', CellKind.Markup, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['# header b', 'markdown', CellKind.Markup, [], {}],
['var c = 2;', 'javascript', CellKind.Code, [], {}]
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 8 }, () => false),
editorViewStates: Array.from({ length: 8 }, () => null),
cellTotalHeights: Array.from({ length: 8 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(50);
editor.visibleRanges = [{ start: 0, end: 8 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
// outdated/improper behavior
test.skip('test5: should render 0->2, scrolltop halfway through cell 2', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}],
['## header aa', 'markdown', CellKind.Markup, [], {}],
['### header aaa', 'markdown', CellKind.Markup, [], {}],
['#### header aaaa', 'markdown', CellKind.Markup, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['# header b', 'markdown', CellKind.Markup, [], {}],
['var c = 2;', 'javascript', CellKind.Code, [], {}]
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 10 }, () => false),
editorViewStates: Array.from({ length: 10 }, () => null),
cellTotalHeights: Array.from({ length: 10 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(125);
editor.visibleRanges = [{ start: 2, end: 10 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
// outdated/improper behavior
test.skip('test6: should render 6->7, scrolltop halfway through cell 7', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}],
['## header aa', 'markdown', CellKind.Markup, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['var b = 1;', 'javascript', CellKind.Code, [], {}],
['# header b', 'markdown', CellKind.Markup, [], {}],
['## header bb', 'markdown', CellKind.Markup, [], {}],
['### header bbb', 'markdown', CellKind.Markup, [], {}],
['var c = 2;', 'javascript', CellKind.Code, [], {}]
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 10 }, () => false),
editorViewStates: Array.from({ length: 10 }, () => null),
cellTotalHeights: Array.from({ length: 10 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(375);
editor.visibleRanges = [{ start: 7, end: 10 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
// waiting on behavior push to fix this.
test('test7: should render 0->1, collapsing against next section', async function () {
await withTestNotebook(
[
['# header a', 'markdown', CellKind.Markup, [], {}], //0
['## header aa', 'markdown', CellKind.Markup, [], {}], //50
['### header aaa', 'markdown', CellKind.Markup, [], {}], //100
['#### header aaaa', 'markdown', CellKind.Markup, [], {}], //150
['var b = 1;', 'javascript', CellKind.Code, [], {}], //200
['var b = 1;', 'javascript', CellKind.Code, [], {}], //250
['var b = 1;', 'javascript', CellKind.Code, [], {}], //300
['var b = 1;', 'javascript', CellKind.Code, [], {}], //350
['# header b', 'markdown', CellKind.Markup, [], {}], //400
['## header bb', 'markdown', CellKind.Markup, [], {}], //450
['### header bbb', 'markdown', CellKind.Markup, [], {}],
['var c = 2;', 'javascript', CellKind.Code, [], {}]
],
async (editor, viewModel, ds) => {
viewModel.restoreEditorViewState({
editingCells: Array.from({ length: 12 }, () => false),
editorViewStates: Array.from({ length: 12 }, () => null),
cellTotalHeights: Array.from({ length: 12 }, () => 50),
cellLineNumberStates: {},
collapsedInputCells: {},
collapsedOutputCells: {},
});
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
cellList.attachViewModel(viewModel);
cellList.layout(400, 100);
editor.setScrollTop(350);
editor.visibleRanges = [{ start: 7, end: 12 }];
const outline = getOutline(editor);
const notebookOutlineEntries = outline.entries;
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
await assertSnapshot(resultingMap);
outline.dispose();
});
});
});