Change:
From
The core might have heuristics of if/when should be called, maybe this test can be turned into a unit test first?
Was just looking at this. That might make sense too, but it also seems to make sense to test this as an API contract.
Is it going to make a difference if I move off the deprecated WorkspaceEdit api?
The issue you have mentioned with the editor service, I wonder if that could be in play here because this failure is just implying that the cell did not get rendered and initially request statusbar items. This test also doesn't make sense because it's just awaiting the same promise twice

Discussion:
}); }); <del> (vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('statusbar', () => { const emitter = new vscode.EventEmitter<vscode.NotebookCell>(); const onDidCallProvide = emitter.event; const suiteDisposables: vscode.Disposable[] = []; suiteTeardown(async function () { assertNoRpc(); await revertAllDirty(); await closeAllEditors(); disposeAll(suiteDisposables); suiteDisposables.length = 0; }); suiteSetup(() => { suiteDisposables.push(vscode.notebooks.registerNotebookCellStatusBarItemProvider('notebookCoreTest', { async provideCellStatusBarItems(cell: vscode.NotebookCell, _token: vscode.CancellationToken): Promise<vscode.NotebookCellStatusBarItem[]> { emitter.fire(cell); return []; } })); suiteDisposables.push(vscode.workspace.registerNotebookContentProvider('notebookCoreTest', apiTestContentProvider)); }); test('provideCellStatusBarItems called on metadata change', async function () { const provideCalled = asPromise(onDidCallProvide); const notebook = await openRandomNotebookDocument(); await vscode.window.showNotebookDocument(notebook); await provideCalled; const edit = new vscode.WorkspaceEdit(); edit.set(notebook.uri, [vscode.NotebookEdit.updateCellMetadata(0, { inputCollapsed: true })]); await vscode.workspace.applyEdit(edit); await provideCalled; }); }); </del> suite('Notebook & LiveShare', function () { const suiteDisposables: vscode.Disposable[] = [];

<ins> /*--------------------------------------------------------------------------------------------- *  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 { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ContributedStatusBarItemController } from 'vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/contributedStatusBarItemController'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, INotebookCellStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; suite('Notebook Statusbar', () => { const testDisposables = new DisposableStore(); teardown(() => { testDisposables.clear(); }); test('Calls item provider', async function () { await withTestNotebook( [ ['var b = 1;', 'javascript', CellKind.Code, [], {}], ['# header a', 'markdown', CellKind.Markup, [], {}], ], async (editor, viewModel, accessor) => { const cellStatusbarSvc = accessor.get(INotebookCellStatusBarService); testDisposables.add(accessor.createInstance(ContributedStatusBarItemController, editor)); const provider = testDisposables.add(new class extends Disposable implements INotebookCellStatusBarItemProvider { private provideCalls = 0; private _onProvideCalled = this._register(new Emitter<number>()); public onProvideCalled = this._onProvideCalled.event; public _onDidChangeStatusBarItems = this._register(new Emitter<void>()); public onDidChangeStatusBarItems = this._onDidChangeStatusBarItems.event; async provideCellStatusBarItems(_uri: URI, index: number, _token: CancellationToken) { if (index === 0) { this.provideCalls++; this._onProvideCalled.fire(this.provideCalls); } return { items: [] }; } viewType = editor.textModel.viewType; }); const providePromise1 = asPromise(provider.onProvideCalled); testDisposables.add(cellStatusbarSvc.registerCellStatusBarItemProvider(provider)); assert.strictEqual(await providePromise1, 1, 'should call provider on registration'); const providePromise2 = asPromise(provider.onProvideCalled); const cell0 = editor.textModel.cells[0]; cell0.metadata = { ...cell0.metadata, ...{ newMetadata: true } }; assert.strictEqual(await providePromise2, 2, 'should call provider on registration'); const providePromise3 = asPromise(provider.onProvideCalled); cell0.language = 'newlanguage'; assert.strictEqual(await providePromise3, 3, 'should call provider on registration'); const providePromise4 = asPromise(provider.onProvideCalled); provider._onDidChangeStatusBarItems.fire(); assert.strictEqual(await providePromise4, 4, 'should call provider on registration'); }); }); }); async function asPromise<T>(event: Event<T>, timeout = 5000): Promise<T> { const error = new Error('asPromise TIMEOUT reached'); return new Promise<T>((resolve, reject) => { const handle = setTimeout(() => { sub.dispose(); reject(error); }, timeout); const sub = event(e => { clearTimeout(handle); sub.dispose(); resolve(e); }); }); } </ins>

import { NotImplementedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; <ins> import { ResourceMap } from 'vs/base/common/map'; </ins> import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; <del> import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; </del> import { ILanguageService } from 'vs/editor/common/languages/language'; <ins> import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; </ins> import { LanguageService } from 'vs/editor/common/services/languageService'; import { IModelService } from 'vs/editor/common/services/model'; import { ModelService } from 'vs/editor/common/services/modelService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; <ins> import { TestClipboardService } from 'vs/platform/clipboard/test/common/testClipboardService'; </ins> import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; <ins> import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; </ins> import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { ILogService, NullLogService } from 'vs/platform/log/common/log';

import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { CellFindMatchWithIndex, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; <ins> import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/services/notebookCellStatusBarServiceImpl'; </ins> import { ListViewInfoAccessor, NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; <ins> import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; </ins> import { CellKind, CellUri, INotebookDiffEditorModel, INotebookEditorModel, INotebookSearchOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellExecutionState, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, INotebookCellExecution, INotebookExecutionStateService, INotebookFailStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; <ins> import { IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; </ins> import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; <del> import { ResourceMap } from 'vs/base/common/map'; import { TestClipboardService } from 'vs/platform/clipboard/test/common/testClipboardService'; import { IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; </del> export class TestCell extends NotebookCellTextModel { constructor(

instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(true)); instantiationService.stub(INotebookExecutionStateService, new TestNotebookExecutionStateService()); instantiationService.stub(IKeybindingService, new MockKeybindingService()); <ins> instantiationService.stub(INotebookCellStatusBarService, new NotebookCellStatusBarService()); </ins> return instantiationService; }

override cellAt(index: number) { return viewModel.cellAt(index)!; } override getCellIndex(cell: ICellViewModel) { return viewModel.getCellIndex(cell); } override getCellsInRange(range?: ICellRange) { return viewModel.getCellsInRange(range); } <ins> override getCellByHandle(handle: number) { return viewModel.getCellByHandle(handle); } </ins> override getNextVisibleCellIndex(index: number) { return viewModel.getNextVisibleCellIndex(index); } getControl() { return this; } override get onDidChangeSelection() { return viewModel.onDidChangeSelection as Event<any>; }

return findMatches; } override deltaCellDecorations() { return []; } <ins> override onDidChangeVisibleRanges = Event.None; override visibleRanges: ICellRange[] = [{ start: 0, end: 100 }]; </ins> }; return { editor: notebookEditor, viewModel };