From c5c6b70e67c22e010e75c93e14b5ec6014507d00 Mon Sep 17 00:00:00 2001 From: Rhys Date: Tue, 5 Sep 2023 09:17:21 -0400 Subject: [PATCH] fix(playgrounds): inspect into strings the non-string types for playground output VSCODE-466 (#590) --- src/editors/playgroundController.ts | 9 +- .../editors/playgroundController.test.ts | 212 +++++++++--------- 2 files changed, 112 insertions(+), 109 deletions(-) diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index b2ed9dda6..84e7c2dbb 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -5,6 +5,7 @@ import { ProgressLocation } from 'vscode'; import vm from 'vm'; import os from 'os'; import transpiler from 'bson-transpilers'; +import util from 'util'; import type ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider'; import type PlaygroundSelectedCodeActionProvider from './playgroundSelectedCodeActionProvider'; @@ -110,8 +111,8 @@ export default class PlaygroundController { _isPartialRun = false; + _outputChannel: OutputChannel; private _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; - private _outputChannel: OutputChannel; private _playgroundResultViewColumn?: vscode.ViewColumn; private _playgroundResultTextDocument?: vscode.TextDocument; private _statusView: StatusView; @@ -573,7 +574,11 @@ export default class PlaygroundController { if (evaluateResponse?.outputLines?.length) { for (const line of evaluateResponse.outputLines) { - this._outputChannel.appendLine(line.content); + this._outputChannel.appendLine( + typeof line.content === 'string' + ? line.content + : util.inspect(line.content) + ); } this._outputChannel.show(true); diff --git a/src/test/suite/editors/playgroundController.test.ts b/src/test/suite/editors/playgroundController.test.ts index b56d6cb81..dfddee1ab 100644 --- a/src/test/suite/editors/playgroundController.test.ts +++ b/src/test/suite/editors/playgroundController.test.ts @@ -212,6 +212,7 @@ suite('Playground Controller Test Suite', function () { suite('playground is open', () => { let mockActiveTestEditor; + let showInformationMessageStub: SinonStub; beforeEach(() => { mockActiveTestEditor = { @@ -232,7 +233,10 @@ suite('Playground Controller Test Suite', function () { testPlaygroundController._activeTextEditor = mockActiveTestEditor as vscode.TextEditor; testPlaygroundController._selectedText = undefined; - sandbox.stub(vscode.window, 'showInformationMessage'); + showInformationMessageStub = sandbox.stub( + vscode.window, + 'showInformationMessage' + ); }); suite('user is not connected', () => { @@ -394,127 +398,121 @@ suite('Playground Controller Test Suite', function () { expectedMessage ); }); - }); - }); - suite('confirmation modal', () => { - let showInformationMessageStub: SinonStub; - - beforeEach(() => { - showInformationMessageStub = sandbox.stub( - vscode.window, - 'showInformationMessage' - ); - const mockActiveTestEditor = { - document: { - languageId: 'javascript', - uri: mockDocumentUri, - getText: () => "use('dbName');", - lineAt: () => ({ text: "use('dbName');" }), - }, - selections: [ - new vscode.Selection( - new vscode.Position(0, 0), - new vscode.Position(0, 0) - ), - ], - } as unknown as vscode.TextEditor; - testPlaygroundController._activeTextEditor = mockActiveTestEditor; - - sandbox.replace( - testPlaygroundController._connectionController, - 'getActiveConnectionName', - () => 'fakeName' - ); - sandbox.replace( - testPlaygroundController._connectionController, - 'isCurrentlyConnected', - () => true - ); - sandbox.replace( - testPlaygroundController._connectionController, - 'getActiveDataService', - () => - ({ - getMongoClientConnectionOptions: () => ({ - url: TEST_DATABASE_URI, - options: {}, - }), - } as unknown as DataService) - ); - sandbox.replace( - testPlaygroundController._connectionController, - 'getActiveConnectionId', - () => 'pineapple' - ); - }); - - test('show a confirmation message if mdb.confirmRunAll is true', async () => { - showInformationMessageStub.resolves('Yes'); + suite('output channels', () => { + let outputChannelAppendLineStub: SinonStub; + let outputChannelClearStub: SinonStub; + let outputChannelShowStub: SinonStub; + + beforeEach(function () { + outputChannelAppendLineStub = sinon.stub(); + outputChannelClearStub = sinon.stub(); + outputChannelShowStub = sinon.stub(); + + const mockOutputChannel = { + appendLine: outputChannelAppendLineStub, + clear: outputChannelClearStub, + show: outputChannelShowStub, + } as Partial as unknown as vscode.OutputChannel; + sandbox.replace( + testPlaygroundController, + '_outputChannel', + mockOutputChannel + ); + showInformationMessageStub.resolves('Yes'); + }); - const fakeEvaluateWithCancelModal = sandbox.fake.resolves({ - outputLines: [], - result: '123', + test('show the output in the vscode output channel as a string', async () => { + const outputLines = [ + 'test', + { pineapple: 'yes' }, + ['porcupine', { anObject: true }], + ].map((content) => ({ content })); + sandbox.replace( + testPlaygroundController, + '_evaluateWithCancelModal', + sandbox.stub().resolves({ + outputLines, + result: '123', + }) + ); + + expect(outputChannelClearStub).to.not.be.called; + expect(outputChannelShowStub).to.not.be.called; + expect(outputChannelAppendLineStub).to.not.be.called; + + await testPlaygroundController.runAllPlaygroundBlocks(); + + expect(outputChannelClearStub).to.be.calledOnce; + expect(outputChannelShowStub).to.be.calledOnce; + expect(outputChannelAppendLineStub.calledThrice).to.be.true; + expect(outputChannelAppendLineStub.firstCall.args[0]).to.equal( + 'test' + ); + // Make sure we're not printing anything like [object Object]. + expect(outputChannelAppendLineStub.secondCall.args[0]).to.equal( + "{ pineapple: 'yes' }" + ); + expect(outputChannelAppendLineStub.thirdCall.args[0]).to.equal( + "[ 'porcupine', { anObject: true } ]" + ); + }); }); - sandbox.replace( - testPlaygroundController, - '_evaluateWithCancelModal', - fakeEvaluateWithCancelModal - ); - - const fakeOpenPlaygroundResult = sandbox.fake(); - sandbox.replace( - testPlaygroundController, - '_openPlaygroundResult', - fakeOpenPlaygroundResult - ); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + suite('confirmation modal', () => { + beforeEach(function () { + sandbox.replace( + testPlaygroundController, + '_evaluateWithCancelModal', + sandbox.stub().resolves({ + outputLines: [], + result: '123', + }) + ); + sandbox.replace( + testPlaygroundController, + '_openPlaygroundResult', + sandbox.stub() + ); + }); - expect(result).to.be.equal(true); - expect(showInformationMessageStub).to.have.been.calledOnce; - }); + test('show a confirmation message if mdb.confirmRunAll is true', async () => { + showInformationMessageStub.resolves('Yes'); - test('do not show a confirmation message if mdb.confirmRunAll is false', async () => { - showInformationMessageStub.resolves('Yes'); + const result = + await testPlaygroundController.runAllPlaygroundBlocks(); - await vscode.workspace - .getConfiguration('mdb') - .update('confirmRunAll', false); + expect(result).to.be.equal(true); + expect(showInformationMessageStub).to.have.been.calledOnce; + }); - const fakeEvaluateWithCancelModal = sandbox.fake.resolves({ - outputLines: [], - result: '123', - }); - sandbox.replace( - testPlaygroundController, - '_evaluateWithCancelModal', - fakeEvaluateWithCancelModal - ); + test('do not show a confirmation message if mdb.confirmRunAll is false', async () => { + showInformationMessageStub.resolves('Yes'); - const fakeOpenPlaygroundResult = sandbox.fake(); - sandbox.replace( - testPlaygroundController, - '_openPlaygroundResult', - fakeOpenPlaygroundResult - ); + await vscode.workspace + .getConfiguration('mdb') + .update('confirmRunAll', false); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + const result = + await testPlaygroundController.runAllPlaygroundBlocks(); - expect(result).to.be.equal(true); - expect(showInformationMessageStub).to.not.have.been.called; - }); + expect(result).to.be.equal(true); + expect(showInformationMessageStub).to.not.have.been.called; + }); - test('do not run a playground if user selected No in the confirmation message', async () => { - showInformationMessageStub.resolves('No'); + test('do not run a playground if user selected No in the confirmation message', async () => { + showInformationMessageStub.resolves('No'); - await vscode.workspace - .getConfiguration('mdb') - .update('confirmRunAll', true); + await vscode.workspace + .getConfiguration('mdb') + .update('confirmRunAll', true); - const result = await testPlaygroundController.runAllPlaygroundBlocks(); + const result = + await testPlaygroundController.runAllPlaygroundBlocks(); - expect(result).to.be.false; + expect(result).to.be.false; + }); + }); }); }); });