From a7cb1cac179b86300801b0d06e03410007cb57b9 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Thu, 30 Oct 2025 13:00:06 +0100 Subject: [PATCH 1/3] feat: Add a command for adding chart blocks. --- package.json | 51 +++++ package.nls.json | 3 +- src/commands.ts | 1 + .../deepnoteNotebookCommandListener.ts | 65 ++++++- ...epnoteNotebookCommandListener.unit.test.ts | 184 +++++++++++++++++- src/platform/common/constants.ts | 1 + 6 files changed, 301 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3d3b59ec40..7b7ac852ef 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,12 @@ "category": "Deepnote", "icon": "$(graph)" }, + { + "command": "deepnote.addChartBlock", + "title": "%deepnote.commands.addChartBlock.title%", + "category": "Deepnote", + "icon": "$(graph-line)" + }, { "command": "deepnote.addInputTextBlock", "title": "%deepnote.commands.addInputTextBlock.title%", @@ -850,6 +856,51 @@ "group": "navigation@1", "when": "notebookType == 'deepnote'" }, + { + "command": "deepnote.addChartBlock", + "group": "navigation@2", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addBigNumberChartBlock", + "group": "navigation@3", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputTextBlock", + "group": "navigation@4", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputTextareaBlock", + "group": "navigation@5", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputSelectBlock", + "group": "navigation@6", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputSliderBlock", + "group": "navigation@7", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputCheckboxBlock", + "group": "navigation@8", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputDateBlock", + "group": "navigation@9", + "when": "notebookType == 'deepnote'" + }, + { + "command": "deepnote.addInputDateRangeBlock", + "group": "navigation@10", + "when": "notebookType == 'deepnote'" + }, { "command": "jupyter.restartkernel", "group": "navigation/execute@5", diff --git a/package.nls.json b/package.nls.json index 1d6006666e..504e2565cf 100644 --- a/package.nls.json +++ b/package.nls.json @@ -255,7 +255,8 @@ "deepnote.commands.importNotebook.title": "Import Notebook", "deepnote.commands.importJupyterNotebook.title": "Import Jupyter Notebook", "deepnote.commands.addSqlBlock.title": "Add SQL Block", - "deepnote.commands.addBigNumberChartBlock.title": "Add Big Number Chart Block", + "deepnote.commands.addBigNumberChartBlock.title": "Add Big Number Block", + "deepnote.commands.addChartBlock.title": "Add Chart Block", "deepnote.commands.addInputTextBlock.title": "Add Input Text Block", "deepnote.commands.addInputTextareaBlock.title": "Add Input Textarea Block", "deepnote.commands.addInputSelectBlock.title": "Add Input Select Block", diff --git a/src/commands.ts b/src/commands.ts index 40192a8f6f..3cbc992794 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -188,6 +188,7 @@ export interface ICommandNameArgumentTypeMapping { [DSCommands.ContinueEditSessionInCodespace]: []; [DSCommands.AddSqlBlock]: []; [DSCommands.AddBigNumberChartBlock]: []; + [DSCommands.AddChartBlock]: []; [DSCommands.AddInputTextBlock]: []; [DSCommands.AddInputTextareaBlock]: []; [DSCommands.AddInputSelectBlock]: []; diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts index da121db36a..97eaf63493 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts @@ -75,7 +75,10 @@ export function getInputBlockMetadata(blockType: InputBlockType, variableName: s export function safeParseDeepnoteVariableNameFromContentJson(content: string): string | undefined { try { - const variableNameResult = z.string().safeParse(JSON.parse(content)['deepnote_variable_name']); + const parsed = JSON.parse(content); + // Chart blocks use 'variable' key, other blocks use 'deepnote_variable_name' + const variableName = parsed['variable'] ?? parsed['deepnote_variable_name']; + const variableNameResult = z.string().safeParse(variableName); return variableNameResult.success ? variableNameResult.data : undefined; } catch (error) { logger.error('Error parsing deepnote variable name from content JSON', error); @@ -83,7 +86,7 @@ export function safeParseDeepnoteVariableNameFromContentJson(content: string): s } } -export function getNextDeepnoteVariableName(cells: NotebookCell[], prefix: 'df' | 'query' | 'input'): string { +export function getNextDeepnoteVariableName(cells: NotebookCell[], prefix: 'df' | 'query' | 'input' | 'chart'): string { const deepnoteVariableNames = cells.reduce((acc, cell) => { const contentValue = safeParseDeepnoteVariableNameFromContentJson(cell.document.getText()); @@ -147,6 +150,7 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation this.disposableRegistry.push( commands.registerCommand(Commands.AddBigNumberChartBlock, () => this.addBigNumberChartBlock()) ); + this.disposableRegistry.push(commands.registerCommand(Commands.AddChartBlock, () => this.addChartBlock())); this.disposableRegistry.push( commands.registerCommand(Commands.AddInputTextBlock, () => this.addInputBlock('input-text')) ); @@ -261,6 +265,63 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation await commands.executeCommand('notebook.cell.edit'); } + public async addChartBlock(): Promise { + const editor = window.activeNotebookEditor; + + if (!editor) { + throw new Error(l10n.t('No active notebook editor found')); + } + + const document = editor.notebook; + const selection = editor.selection; + + const insertIndex = selection ? selection.end : document.cellCount; + const deepnoteVariableName = getNextDeepnoteVariableName(document.getCells(), 'df'); + + const defaultVisualizationSpec = { + mark: 'line', + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + data: { values: [] }, + encoding: { + x: { field: 'x', type: 'quantitative' }, + y: { field: 'y', type: 'quantitative' } + } + }; + + const cellContent = { + variable: deepnoteVariableName, + spec: defaultVisualizationSpec, + filters: [] + }; + + const metadata = { + __deepnotePocket: { + type: 'visualization' + } + }; + + const result = await chainWithPendingUpdates(document, (edit) => { + const newCell = new NotebookCellData(NotebookCellKind.Code, JSON.stringify(cellContent, null, 2), 'json'); + + newCell.metadata = metadata; + + const nbEdit = NotebookEdit.insertCells(insertIndex, [newCell]); + + edit.set(document.uri, [nbEdit]); + }); + + if (result !== true) { + throw new Error(l10n.t('Failed to insert chart block')); + } + + const notebookRange = new NotebookRange(insertIndex, insertIndex + 1); + + editor.revealRange(notebookRange, NotebookEditorRevealType.Default); + editor.selection = notebookRange; + + await commands.executeCommand('notebook.cell.edit'); + } + public async addInputBlock(blockType: InputBlockType): Promise { const editor = window.activeNotebookEditor; if (!editor) { diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts index 49404b0739..92cd3588e1 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts @@ -102,7 +102,7 @@ suite('DeepnoteNotebookCommandListener', () => { const TEST_INPUTS: Array<{ description: string; cells: NotebookCell[]; - prefix: 'df' | 'query' | 'input'; + prefix: 'df' | 'query' | 'input' | 'chart'; expected: string; }> = [ // Tests with 'input' prefix @@ -975,5 +975,187 @@ suite('DeepnoteNotebookCommandListener', () => { ); }); }); + + suite('addChartBlock', () => { + test('should add chart block at the end when no selection exists', async () => { + // Setup mocks + const { editor, document } = createMockEditor([], undefined); + const { chainStub, executeCommandStub, getCapturedNotebookEdits } = + mockNotebookUpdateAndExecute(editor); + + // Call the method + await commandListener.addChartBlock(); + + const capturedNotebookEdits = getCapturedNotebookEdits(); + + // Verify chainWithPendingUpdates was called + assert.isTrue(chainStub.calledOnce, 'chainWithPendingUpdates should be called once'); + assert.equal(chainStub.firstCall.args[0], document, 'Should be called with correct document'); + + // Verify the edits were captured + assert.isNotNull(capturedNotebookEdits, 'Notebook edits should be captured'); + assert.isDefined(capturedNotebookEdits, 'Notebook edits should be defined'); + + const editsArray = capturedNotebookEdits!; + assert.equal(editsArray.length, 1, 'Should have one notebook edit'); + + const notebookEdit = editsArray[0] as any; + assert.equal(notebookEdit.newCells.length, 1, 'Should insert one cell'); + + const newCell = notebookEdit.newCells[0]; + assert.equal(newCell.kind, NotebookCellKind.Code, 'Should be a code cell'); + assert.equal(newCell.languageId, 'json', 'Should have json language'); + + // Verify cell content is valid JSON with correct structure + const content = JSON.parse(newCell.value); + assert.equal(content.variable, 'df_1', 'Should have correct variable name'); + assert.property(content, 'spec', 'Should have spec property'); + assert.property(content, 'filters', 'Should have filters property'); + + // Verify the spec has the correct Vega-Lite structure + assert.equal(content.spec.mark, 'line', 'Should be a line chart'); + assert.equal( + content.spec.$schema, + 'https://vega.github.io/schema/vega-lite/v5.json', + 'Should have Vega-Lite schema' + ); + assert.deepStrictEqual(content.spec.data, { values: [] }, 'Should have empty data array'); + assert.property(content.spec, 'encoding', 'Should have encoding property'); + assert.property(content.spec.encoding, 'x', 'Should have x encoding'); + assert.property(content.spec.encoding, 'y', 'Should have y encoding'); + + // Verify metadata structure + assert.property(newCell.metadata, '__deepnotePocket', 'Should have __deepnotePocket metadata'); + assert.equal(newCell.metadata.__deepnotePocket.type, 'visualization', 'Should have visualization type'); + + // Verify reveal and selection were set + assert.isTrue((editor.revealRange as sinon.SinonStub).calledOnce, 'Should reveal the new cell range'); + const revealCall = (editor.revealRange as sinon.SinonStub).firstCall; + assert.equal(revealCall.args[0].start, 0, 'Should reveal correct range start'); + assert.equal(revealCall.args[0].end, 1, 'Should reveal correct range end'); + assert.equal(revealCall.args[1], 0, 'Should use NotebookEditorRevealType.Default (value 0)'); + + // Verify notebook.cell.edit command was executed + assert.isTrue( + executeCommandStub.calledWith('notebook.cell.edit'), + 'Should execute notebook.cell.edit command' + ); + }); + + test('should add chart block after selection when selection exists', async () => { + // Setup mocks + const existingCells = [createMockCell('{}'), createMockCell('{}')]; + const selection = new NotebookRange(0, 1); + const { editor } = createMockEditor(existingCells, selection); + const { chainStub, getCapturedNotebookEdits } = mockNotebookUpdateAndExecute(editor); + + // Call the method + await commandListener.addChartBlock(); + + const capturedNotebookEdits = getCapturedNotebookEdits(); + + // Verify chainWithPendingUpdates was called + assert.isTrue(chainStub.calledOnce, 'chainWithPendingUpdates should be called once'); + + // Verify a cell was inserted + assert.isNotNull(capturedNotebookEdits, 'Notebook edits should be captured'); + const notebookEdit = capturedNotebookEdits![0] as any; + assert.equal(notebookEdit.newCells.length, 1, 'Should insert one cell'); + assert.equal(notebookEdit.newCells[0].languageId, 'json', 'Should be JSON cell'); + }); + + test('should generate correct variable name when existing chart variables exist', async () => { + // Setup mocks with existing df variables + const existingCells = [ + createMockCell('{ "deepnote_variable_name": "df_1" }'), + createMockCell('{ "variable": "df_2" }') + ]; + const { editor } = createMockEditor(existingCells, undefined); + const { getCapturedNotebookEdits } = mockNotebookUpdateAndExecute(editor); + + // Call the method + await commandListener.addChartBlock(); + + const capturedNotebookEdits = getCapturedNotebookEdits(); + const notebookEdit = capturedNotebookEdits![0] as any; + const newCell = notebookEdit.newCells[0]; + + // Verify variable name is df_3 + const content = JSON.parse(newCell.value); + assert.equal(content.variable, 'df_3', 'Should generate next variable name'); + }); + + test('should ignore other variable types when generating chart variable name', async () => { + // Setup mocks with input and df variables + const existingCells = [ + createMockCell('{ "deepnote_variable_name": "input_10" }'), + createMockCell('{ "deepnote_variable_name": "df_5" }'), + createMockCell('{ "variable": "df_2" }') + ]; + const { editor } = createMockEditor(existingCells, undefined); + const { getCapturedNotebookEdits } = mockNotebookUpdateAndExecute(editor); + + // Call the method + await commandListener.addChartBlock(); + + const capturedNotebookEdits = getCapturedNotebookEdits(); + const notebookEdit = capturedNotebookEdits![0] as any; + const newCell = notebookEdit.newCells[0]; + + // Verify variable name is df_6 (uses highest df_ number) + const content = JSON.parse(newCell.value); + assert.equal(content.variable, 'df_6', 'Should consider all df variables'); + }); + + test('should insert at correct position in the middle of notebook', async () => { + // Setup mocks + const existingCells = [createMockCell('{}'), createMockCell('{}'), createMockCell('{}')]; + const selection = new NotebookRange(1, 2); + const { editor } = createMockEditor(existingCells, selection); + const { chainStub, getCapturedNotebookEdits } = mockNotebookUpdateAndExecute(editor); + + // Call the method + await commandListener.addChartBlock(); + + const capturedNotebookEdits = getCapturedNotebookEdits(); + + // Verify chainWithPendingUpdates was called + assert.isTrue(chainStub.calledOnce, 'chainWithPendingUpdates should be called once'); + + // Verify a cell was inserted + assert.isNotNull(capturedNotebookEdits, 'Notebook edits should be captured'); + const notebookEdit = capturedNotebookEdits![0] as any; + assert.equal(notebookEdit.newCells.length, 1, 'Should insert one cell'); + assert.equal(notebookEdit.newCells[0].languageId, 'json', 'Should be JSON cell'); + }); + + test('should throw error when no active editor exists', async () => { + // Setup: no active editor + Object.defineProperty(window, 'activeNotebookEditor', { + value: undefined, + configurable: true, + writable: true + }); + + // Call the method and expect rejection + await assert.isRejected(commandListener.addChartBlock(), Error, 'No active notebook editor found'); + }); + + test('should throw error when chainWithPendingUpdates fails', async () => { + // Setup mocks + const { editor } = createMockEditor([], undefined); + Object.defineProperty(window, 'activeNotebookEditor', { + value: editor, + configurable: true, + writable: true + }); + + // Mock chainWithPendingUpdates to return false + sandbox.stub(notebookUpdater, 'chainWithPendingUpdates').resolves(false); + + // Call the method and expect rejection + await assert.isRejected(commandListener.addChartBlock(), Error, 'Failed to insert chart block'); + }); + }); }); }); diff --git a/src/platform/common/constants.ts b/src/platform/common/constants.ts index c6010d716f..59e2b63e1f 100644 --- a/src/platform/common/constants.ts +++ b/src/platform/common/constants.ts @@ -227,6 +227,7 @@ export namespace Commands { export const ManageIntegrations = 'deepnote.manageIntegrations'; export const AddSqlBlock = 'deepnote.addSqlBlock'; export const AddBigNumberChartBlock = 'deepnote.addBigNumberChartBlock'; + export const AddChartBlock = 'deepnote.addChartBlock'; export const AddInputTextBlock = 'deepnote.addInputTextBlock'; export const AddInputTextareaBlock = 'deepnote.addInputTextareaBlock'; export const AddInputSelectBlock = 'deepnote.addInputSelectBlock'; From 3a757ba1dde504412933cbc2879b102681b7774c Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Thu, 30 Oct 2025 13:11:38 +0100 Subject: [PATCH 2/3] hardcode the variable name --- .../deepnote/deepnoteNotebookCommandListener.ts | 5 ++--- .../deepnoteNotebookCommandListener.unit.test.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts index 97eaf63493..9c42f1e867 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts @@ -86,7 +86,7 @@ export function safeParseDeepnoteVariableNameFromContentJson(content: string): s } } -export function getNextDeepnoteVariableName(cells: NotebookCell[], prefix: 'df' | 'query' | 'input' | 'chart'): string { +export function getNextDeepnoteVariableName(cells: NotebookCell[], prefix: 'df' | 'query' | 'input'): string { const deepnoteVariableNames = cells.reduce((acc, cell) => { const contentValue = safeParseDeepnoteVariableNameFromContentJson(cell.document.getText()); @@ -276,7 +276,6 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation const selection = editor.selection; const insertIndex = selection ? selection.end : document.cellCount; - const deepnoteVariableName = getNextDeepnoteVariableName(document.getCells(), 'df'); const defaultVisualizationSpec = { mark: 'line', @@ -289,7 +288,7 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation }; const cellContent = { - variable: deepnoteVariableName, + variable: 'df_1', spec: defaultVisualizationSpec, filters: [] }; diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts index 92cd3588e1..7f16e51d55 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts @@ -102,7 +102,7 @@ suite('DeepnoteNotebookCommandListener', () => { const TEST_INPUTS: Array<{ description: string; cells: NotebookCell[]; - prefix: 'df' | 'query' | 'input' | 'chart'; + prefix: 'df' | 'query' | 'input'; expected: string; }> = [ // Tests with 'input' prefix @@ -1064,7 +1064,7 @@ suite('DeepnoteNotebookCommandListener', () => { assert.equal(notebookEdit.newCells[0].languageId, 'json', 'Should be JSON cell'); }); - test('should generate correct variable name when existing chart variables exist', async () => { + test('should use hardcoded variable name df_1', async () => { // Setup mocks with existing df variables const existingCells = [ createMockCell('{ "deepnote_variable_name": "df_1" }'), @@ -1080,13 +1080,13 @@ suite('DeepnoteNotebookCommandListener', () => { const notebookEdit = capturedNotebookEdits![0] as any; const newCell = notebookEdit.newCells[0]; - // Verify variable name is df_3 + // Verify variable name is always df_1 const content = JSON.parse(newCell.value); - assert.equal(content.variable, 'df_3', 'Should generate next variable name'); + assert.equal(content.variable, 'df_1', 'Should always use df_1'); }); - test('should ignore other variable types when generating chart variable name', async () => { - // Setup mocks with input and df variables + test('should always use df_1 regardless of existing variables', async () => { + // Setup mocks with various existing variables const existingCells = [ createMockCell('{ "deepnote_variable_name": "input_10" }'), createMockCell('{ "deepnote_variable_name": "df_5" }'), @@ -1102,9 +1102,9 @@ suite('DeepnoteNotebookCommandListener', () => { const notebookEdit = capturedNotebookEdits![0] as any; const newCell = notebookEdit.newCells[0]; - // Verify variable name is df_6 (uses highest df_ number) + // Verify variable name is always df_1 const content = JSON.parse(newCell.value); - assert.equal(content.variable, 'df_6', 'Should consider all df variables'); + assert.equal(content.variable, 'df_1', 'Should always use df_1'); }); test('should insert at correct position in the middle of notebook', async () => { From 6b3b58129ba22e6d52a3cc36fbfc3fc754f89bc1 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Thu, 30 Oct 2025 13:27:12 +0100 Subject: [PATCH 3/3] better errrors --- .../deepnote/deepnoteNotebookCommandListener.ts | 6 +++--- .../deepnoteNotebookCommandListener.unit.test.ts | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts index 9c42f1e867..0b8a0f751a 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.ts @@ -17,6 +17,7 @@ import { IExtensionSyncActivationService } from '../../platform/activation/types import { IDisposableRegistry } from '../../platform/common/types'; import { Commands } from '../../platform/common/constants'; import { chainWithPendingUpdates } from '../../kernels/execution/notebookUpdater'; +import { WrappedError } from '../../platform/errors/types'; import { DeepnoteBigNumberMetadataSchema, DeepnoteTextInputMetadataSchema, @@ -269,12 +270,11 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation const editor = window.activeNotebookEditor; if (!editor) { - throw new Error(l10n.t('No active notebook editor found')); + throw new WrappedError(l10n.t('No active notebook editor found')); } const document = editor.notebook; const selection = editor.selection; - const insertIndex = selection ? selection.end : document.cellCount; const defaultVisualizationSpec = { @@ -310,7 +310,7 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation }); if (result !== true) { - throw new Error(l10n.t('Failed to insert chart block')); + throw new WrappedError(l10n.t('Failed to insert chart block')); } const notebookRange = new NotebookRange(insertIndex, insertIndex + 1); diff --git a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts index 7f16e51d55..aeed7fca66 100644 --- a/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts +++ b/src/notebooks/deepnote/deepnoteNotebookCommandListener.unit.test.ts @@ -21,6 +21,7 @@ import { import { IDisposable } from '../../platform/common/types'; import * as notebookUpdater from '../../kernels/execution/notebookUpdater'; import { createMockedNotebookDocument } from '../../test/datascience/editor-integration/helpers'; +import { WrappedError } from '../../platform/errors/types'; suite('DeepnoteNotebookCommandListener', () => { let commandListener: DeepnoteNotebookCommandListener; @@ -1138,7 +1139,11 @@ suite('DeepnoteNotebookCommandListener', () => { }); // Call the method and expect rejection - await assert.isRejected(commandListener.addChartBlock(), Error, 'No active notebook editor found'); + await assert.isRejected( + commandListener.addChartBlock(), + WrappedError, + 'No active notebook editor found' + ); }); test('should throw error when chainWithPendingUpdates fails', async () => { @@ -1154,7 +1159,7 @@ suite('DeepnoteNotebookCommandListener', () => { sandbox.stub(notebookUpdater, 'chainWithPendingUpdates').resolves(false); // Call the method and expect rejection - await assert.isRejected(commandListener.addChartBlock(), Error, 'Failed to insert chart block'); + await assert.isRejected(commandListener.addChartBlock(), WrappedError, 'Failed to insert chart block'); }); }); });