From 6c4078bfd389c858df68005d33e4847e5aebf661 Mon Sep 17 00:00:00 2001 From: Cristian Cepeda <43882+pastuxso@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:08:18 -0500 Subject: [PATCH] Refactor Output Actions: Auto-Save, Save, Preview & Gist (#1868) * Introduced a new `save-button` component to simplify and separate save functionality from `action-button`. * WIP - Detaching Preview outputs from AutoSave * Repository sync * Sync repository * Updating tests * Repository sync * Achieve the global threshold coverage for branches. - Added shouldSkipRefreshTerminal method to handle terminal refresh conditions. - Updated refreshTerminal method to use shouldSkipRefreshTerminal. - Added unit tests for shouldSkipRefreshTerminal and refreshTerminal methods. * extract notebook outputs preview into separate command * Update shouldWriteOutputs logic to handle edge cases --- package.json | 4 +- .../components/terminal/actionButton.ts | 4 +- src/client/components/terminal/gistCell.ts | 2 +- src/client/components/terminal/index.ts | 52 ++++++++----- src/client/components/terminal/saveButton.ts | 35 +++++++++ src/client/components/terminal/shareButton.ts | 30 ++++++++ src/client/index.ts | 7 -- src/constants.ts | 2 +- src/extension/cell.ts | 25 +++---- src/extension/commands/index.ts | 28 ++++++- src/extension/extension.ts | 25 +++---- .../platformRequest/saveCellExecution.ts | 2 +- src/extension/serializer.ts | 73 ++++++++++--------- src/extension/utils.ts | 2 +- src/types.ts | 1 - tests/extension/cell.test.ts | 28 +++++++ .../extension/provider/sessionOutputs.test.ts | 2 +- tests/extension/serializer.test.ts | 20 ++--- 18 files changed, 229 insertions(+), 113 deletions(-) create mode 100644 src/client/components/terminal/saveButton.ts create mode 100644 src/client/components/terminal/shareButton.ts diff --git a/package.json b/package.json index f01ae088a..6e307d555 100644 --- a/package.json +++ b/package.json @@ -365,7 +365,7 @@ "category": "Runme", "title": "Click to open session outputs", "icon": "$(output-view-icon)", - "shortTitle": "Session Outputs" + "shortTitle": "Preview Outputs" }, { "command": "runme.resetRunnerSession", @@ -585,7 +585,7 @@ "notebook/toolbar": [ { "command": "runme.notebookSessionOutputs", - "when": "notebookType == runme && notebookMode != sessionOutputs && notebookHasRunmeOutputs", + "when": "notebookType == runme && notebookMode != sessionOutputs", "group": "navigation" }, { diff --git a/src/client/components/terminal/actionButton.ts b/src/client/components/terminal/actionButton.ts index 5a46f5864..1f0155b53 100644 --- a/src/client/components/terminal/actionButton.ts +++ b/src/client/components/terminal/actionButton.ts @@ -20,10 +20,10 @@ export class ActionButton extends LitElement { disabled: boolean = false @property({ type: Boolean, reflect: true }) - shareIcon: boolean = false + shareIcon: boolean | undefined @property({ type: Boolean, reflect: true }) - saveIcon: boolean = false + saveIcon: boolean | undefined /* eslint-disable */ static styles = css` diff --git a/src/client/components/terminal/gistCell.ts b/src/client/components/terminal/gistCell.ts index 068c8a5ab..31b450d17 100644 --- a/src/client/components/terminal/gistCell.ts +++ b/src/client/components/terminal/gistCell.ts @@ -7,7 +7,7 @@ import { GistIcon } from '../icons/gistIcon' @customElement('gist-cell') export class GistCell extends LitElement { @property({ type: String }) - text: string = 'Preview Gist' + text: string = 'Preview & Gist' @property({ type: Boolean, reflect: true }) disabled: boolean = false diff --git a/src/client/components/terminal/index.ts b/src/client/components/terminal/index.ts index 1a746b3fd..9e02f1f64 100644 --- a/src/client/components/terminal/index.ts +++ b/src/client/components/terminal/index.ts @@ -27,6 +27,8 @@ import '../copyButton' import './actionButton' import './gistCell' import './open' +import './saveButton' +import './shareButton' import { CreateCellExecutionMutation, CreateEscalationMutation, @@ -389,9 +391,6 @@ export class TerminalView extends LitElement { @property({ type: Boolean }) isAutoSaveEnabled: boolean = false - @property({ type: Boolean }) - isSessionOutputsEnabled: boolean = false - @property({ type: Boolean }) isPlatformAuthEnabled: boolean = false @@ -594,12 +593,10 @@ export class TerminalView extends LitElement { return } this.exitCode = code - - if (!this.isAutoSaveEnabled) { - return + if (features.isOn(FeatureName.SignedIn, this.featureState$) && this.isAutoSaveEnabled) { + return this.#shareCellOutput(false) } - - return this.#shareCellOutput(false) + return } } }), @@ -993,18 +990,25 @@ export class TerminalView extends LitElement { () => {}, )} ${when( - (this.exitCode === undefined || this.exitCode === 0 || !this.platformId) && - !this.isDaggerOutput && - features.isOn(FeatureName.Share, this.featureState$), + this.shouldRenderSaveButton(), () => { - return html` - ` + ` + }, + () => {}, + )} + ${when( + this.shouldRenderShareButton(), + () => { + return html` + ` }, () => {}, )} @@ -1015,7 +1019,7 @@ export class TerminalView extends LitElement { this.platformId && !this.isDaggerOutput, () => { - return html` { - return html` { - return html` ` @@ -1078,6 +1082,16 @@ export class TerminalView extends LitElement { ), ) } + + shouldRenderSaveButton() { + const isExitCodeValid = this.exitCode === undefined || this.exitCode === 0 + return !this.platformId && isExitCodeValid && !this.isDaggerOutput + } + + shouldRenderShareButton() { + const isFeatureEnabled = features.isOn(FeatureName.Share, this.featureState$) + return this.platformId && isFeatureEnabled && this.isShareReady + } } function convertXTermDimensions(dimensions: ITerminalDimensions): TerminalDimensions diff --git a/src/client/components/terminal/saveButton.ts b/src/client/components/terminal/saveButton.ts new file mode 100644 index 000000000..f166e5d84 --- /dev/null +++ b/src/client/components/terminal/saveButton.ts @@ -0,0 +1,35 @@ +import { LitElement, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import './actionButton' + +@customElement('save-button') +export class SaveButton extends LitElement { + @property({ type: Boolean, reflect: true }) + loading: boolean = false + + @property({ type: Boolean, reflect: true }) + signedIn: boolean = false + + private handleClick(e: Event) { + if (e.defaultPrevented) { + e.preventDefault() + } + + this.dispatchEvent(new CustomEvent('onClick')) + } + + render() { + let text = this.signedIn ? 'Save' : 'Save to Cloud' + + return html` + + + ` + } +} diff --git a/src/client/components/terminal/shareButton.ts b/src/client/components/terminal/shareButton.ts new file mode 100644 index 000000000..54dac10db --- /dev/null +++ b/src/client/components/terminal/shareButton.ts @@ -0,0 +1,30 @@ +import { LitElement, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import './actionButton' + +@customElement('share-button') +export class ShareButton extends LitElement { + @property({ type: Boolean, reflect: true }) + loading: boolean = false + + private handleClick(e: Event) { + if (e.defaultPrevented) { + e.preventDefault() + } + + this.dispatchEvent(new CustomEvent('onClick')) + } + + render() { + return html` + + + ` + } +} diff --git a/src/client/index.ts b/src/client/index.ts index 52756832f..75cee138a 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -136,13 +136,6 @@ export const activate: ActivationFunction = (context) => { ) } - if (payload.output.isSessionOutputsEnabled) { - terminalElement.setAttribute( - 'isSessionOutputsEnabled', - payload.output.isSessionOutputsEnabled.toString(), - ) - } - if (payload.output.isPlatformAuthEnabled) { terminalElement.setAttribute( 'isPlatformAuthEnabled', diff --git a/src/constants.ts b/src/constants.ts index 2082a6703..950d665c1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -200,7 +200,7 @@ export const NOTEBOOK_AUTOSAVE_ON = 'notebookAutoSaveOn' export const NOTEBOOK_LIFECYCLE_ID = 'notebookLifecycleId' export const NOTEBOOK_OUTPUTS_MASKED = 'notebookOutputsMasked' export const NOTEBOOK_MODE = 'notebookMode' -export const NOTEBOOK_HAS_OUTPUTS = 'notebookHasRunmeOutputs' +export const NOTEBOOK_PREVIEW_OUTPUTS = 'notebookPreviewRunmeOutputs' export const NOTEBOOK_RUN_WITH_PROMPTS = 'notebookRunWithPrompts' export const NOTEBOOK_AUTHOR_MODE_ON = 'notebookAuthorModeOn' diff --git a/src/extension/cell.ts b/src/extension/cell.ts index 943d2ff24..82b540f80 100644 --- a/src/extension/cell.ts +++ b/src/extension/cell.ts @@ -251,17 +251,16 @@ export class NotebookCellOutputManager { ? ContextState.getKey(PLATFORM_USER_SIGNED_IN) : ContextState.getKey(GITHUB_USER_SIGNED_IN) - const isSessionOutputsEnabled = getSessionOutputs() - const json: CellOutputPayload = { type: OutputType.terminal, output: { 'runme.dev/id': cellId, content: stdoutBase64, initialRows: terminalRows || terminalConfigurations.rows, - isAutoSaveEnabled: isSignedIn ? ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) : false, + isAutoSaveEnabled: isSignedIn + ? ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) + : false, isPlatformAuthEnabled: isPlatformAuthEnabled(), - isSessionOutputsEnabled, isDaggerOutput: !!daggerOutput, ...terminalConfigurations, }, @@ -525,6 +524,13 @@ export class NotebookCellOutputManager { }) } + shouldSkipRefreshTerminal() { + const isSignedIn = features.isOnInContextState(FeatureName.SignedIn) + const isForceLogin = features.isOnInContextState(FeatureName.ForceLogin) + + return !isSignedIn && isForceLogin + } + /** * Syncs a stdout output item based on the active terminal * @@ -538,15 +544,8 @@ export class NotebookCellOutputManager { * */ async refreshTerminal(terminalState: ITerminalState | undefined): Promise { - const isSignedIn = features.isOnInContextState(FeatureName.SignedIn) - const isForceLogin = features.isOnInContextState(FeatureName.ForceLogin) - - const isAutoSaveOn = ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) - - if (!isSignedIn && !isAutoSaveOn) { - return Promise.resolve() - } else if (!isSignedIn && isForceLogin) { - return Promise.resolve() + if (this.shouldSkipRefreshTerminal()) { + return } await this.withLock(async () => { diff --git a/src/extension/commands/index.ts b/src/extension/commands/index.ts index e75bb2a98..5e4fd91e5 100644 --- a/src/extension/commands/index.ts +++ b/src/extension/commands/index.ts @@ -48,6 +48,7 @@ import { ClientMessages, TELEMETRY_EVENTS, RUNME_FRONTMATTER_PARSED, + NOTEBOOK_PREVIEW_OUTPUTS, } from '../../constants' import ContextState from '../contextState' import { createGist } from '../services/github/gist' @@ -56,6 +57,7 @@ import { GetUserEnvironmentsDocument } from '../__generated-platform__/graphql' import { EnvironmentManager } from '../environment/manager' import features from '../features' import { insertCodeNotebookCell } from '../cell' +import { GrpcSerializer, SerializerBase } from '../serializer' const log = getLogger('Commands') @@ -308,6 +310,7 @@ export async function askNewRunnerSession(kernel: Kernel) { 'OK', ) if (action) { + await ContextState.addKey(NOTEBOOK_PREVIEW_OUTPUTS, false) await commands.executeCommand('workbench.action.files.save') await kernel.newRunnerEnvironment({}) await commands.executeCommand('workbench.action.files.save') @@ -324,7 +327,7 @@ export async function askAlternativeOutputsAction( metadata: { [key: string]: any }, ): Promise { const action = await window.showWarningMessage( - 'Running Session Outputs from a previous notebook session is not supported.', + 'Running Preview Outputs from a previous notebook session is not supported.', { modal: true }, ASK_ALT_OUTPUTS_ACTION.ORIGINAL, ) @@ -591,3 +594,26 @@ export async function selectEnvironment(manager: EnvironmentManager) { ) } } + +export function notebookSessionOutputs(kernel: Kernel, serializer: SerializerBase) { + return async (e: NotebookUiEvent) => { + const runnerEnv = kernel.getRunnerEnvironment() + const sessionId = runnerEnv?.getSessionId() + if (!e.ui || !sessionId) { + return + } + + await ContextState.addKey(NOTEBOOK_PREVIEW_OUTPUTS, true) + const { notebookUri } = e.notebookEditor + const outputFilePath = GrpcSerializer.getOutputsUri(notebookUri, sessionId) + + try { + await workspace.fs.stat(outputFilePath) + } catch (e) { + await commands.executeCommand('workbench.action.files.save') + } + + await serializer.saveNotebookOutputs(notebookUri) + await openFileAsRunmeNotebook(outputFilePath) + } +} diff --git a/src/extension/extension.ts b/src/extension/extension.ts index 6e41c3d44..fd9c326a9 100644 --- a/src/extension/extension.ts +++ b/src/extension/extension.ts @@ -24,6 +24,7 @@ import { import { AuthenticationProviders, NOTEBOOK_LIFECYCLE_ID, + NOTEBOOK_PREVIEW_OUTPUTS, TELEMETRY_EVENTS, WebViews, } from '../constants' @@ -36,12 +37,7 @@ import { BackgroundTaskProvider, StopBackgroundTaskProvider, } from './provider/background' -import { - getDefaultWorkspace, - bootFile, - resetNotebookSettings, - openFileAsRunmeNotebook, -} from './utils' +import { getDefaultWorkspace, bootFile, resetNotebookSettings } from './utils' import { RunmeTaskProvider } from './provider/runmeTask' import { toggleTerminal, @@ -69,6 +65,7 @@ import { createCellGistCommand, runForkCommand, selectEnvironment, + notebookSessionOutputs, } from './commands' import { WasmSerializer, GrpcSerializer, SerializerBase } from './serializer' import { RunmeLauncherProvider, RunmeTreeProvider } from './provider/launcher' @@ -244,6 +241,8 @@ export class RunmeExtension { const transientOutputs = !getSessionOutputs() + await ContextState.addKey(NOTEBOOK_PREVIEW_OUTPUTS, false) + const omitKeys: Serializer.Metadata = { ['runme.dev/name']: undefined, ['runme.dev/nameGenerated']: undefined, @@ -370,16 +369,10 @@ export class RunmeExtension { RunmeExtension.registerCommand('runme.notebookExplorerMode', () => toggleAuthorMode(true, kernel), ), - RunmeExtension.registerCommand('runme.notebookSessionOutputs', (e: NotebookUiEvent) => { - const runnerEnv = kernel.getRunnerEnvironment() - const sessionId = runnerEnv?.getSessionId() - if (!e.ui || !sessionId) { - return - } - const { notebookUri } = e.notebookEditor - const outputFilePath = GrpcSerializer.getOutputsUri(notebookUri, sessionId) - openFileAsRunmeNotebook(outputFilePath) - }), + RunmeExtension.registerCommand( + 'runme.notebookSessionOutputs', + notebookSessionOutputs(kernel, serializer), + ), RunmeExtension.registerCommand('runme.lifecycleIdentityNone', () => commands.executeCommand('runme.lifecycleIdentitySelection', RunmeIdentity.UNSPECIFIED), diff --git a/src/extension/messages/platformRequest/saveCellExecution.ts b/src/extension/messages/platformRequest/saveCellExecution.ts index df09cd8d2..804e503ab 100644 --- a/src/extension/messages/platformRequest/saveCellExecution.ts +++ b/src/extension/messages/platformRequest/saveCellExecution.ts @@ -248,7 +248,7 @@ export default async function saveCellExecution( pid, input: encodeURIComponent(cell.document.getText()), languageId: cell.document.languageId, - autoSave: ContextState.getKey(NOTEBOOK_AUTOSAVE_ON), + autoSave: autoSaveIsOn, metadata: { mimeType: annotations.mimeType, name: annotations.name, diff --git a/src/extension/serializer.ts b/src/extension/serializer.ts index 6c514bccc..df71fbef6 100644 --- a/src/extension/serializer.ts +++ b/src/extension/serializer.ts @@ -17,24 +17,23 @@ import { CancellationTokenSource, NotebookCellOutput, NotebookCellExecutionSummary, - commands, } from 'vscode' import { GrpcTransport } from '@protobuf-ts/grpc-transport' import { ulid } from 'ulidx' import { maskString } from 'data-guardian' import YAML from 'yaml' -import { Serializer } from '../types' +import { FeatureName, Serializer } from '../types' import { NOTEBOOK_AUTOSAVE_ON, - NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_LIFECYCLE_ID, NOTEBOOK_OUTPUTS_MASKED, + NOTEBOOK_PREVIEW_OUTPUTS, OutputType, RUNME_FRONTMATTER_PARSED, VSCODE_LANGUAGEID_MAP, } from '../constants' -import { ServerLifecycleIdentity, getSessionOutputs } from '../utils/configuration' +import { ServerLifecycleIdentity } from '../utils/configuration' import { DeserializeRequest, @@ -58,6 +57,7 @@ import { IProcessInfoState } from './terminal/terminalState' import ContextState from './contextState' import * as ghost from './ai/ghost' import getLogger from './logger' +import features from './features' declare var globalThis: any const DEFAULT_LANG_ID = 'text' @@ -491,8 +491,6 @@ export class GrpcSerializer extends SerializerBase { ) { super(context, kernel) - this.togglePreviewButton(GrpcSerializer.sessionOutputsEnabled()) - this.ready = new Promise((resolve) => { const disposable = server.onTransportReady(() => { disposable.dispose() @@ -516,20 +514,14 @@ export class GrpcSerializer extends SerializerBase { this.client = initParserClient(transport ?? (await this.server.transport())) } - public togglePreviewButton(state: boolean) { - return commands.executeCommand('setContext', NOTEBOOK_HAS_OUTPUTS, state) - } - protected async handleOpenNotebook(doc: NotebookDocument) { const cacheId = GrpcSerializer.getDocumentCacheId(doc.metadata) if (!cacheId) { - this.togglePreviewButton(false) return } if (GrpcSerializer.isDocumentSessionOutputs(doc.metadata)) { - this.togglePreviewButton(false) return } @@ -551,7 +543,6 @@ export class GrpcSerializer extends SerializerBase { const cacheId = GrpcSerializer.getDocumentCacheId(doc.metadata) if (!cacheId) { - this.togglePreviewButton(false) return } @@ -566,42 +557,61 @@ export class GrpcSerializer extends SerializerBase { const bytes = await cache.get(cacheId ?? '') if (!bytes) { - this.togglePreviewButton(false) return -1 } const srcDocUri = this.cacheDocUriMapping.get(cacheId ?? '') if (!srcDocUri) { - this.togglePreviewButton(false) return -1 } const runnerEnv = this.kernel.getRunnerEnvironment() const sessionId = runnerEnv?.getSessionId() if (!sessionId) { - this.togglePreviewButton(false) return -1 } - // Don't write to disk if auto-save is off - if (!ContextState.getKey(NOTEBOOK_AUTOSAVE_ON)) { - this.togglePreviewButton(false) - // But still return a valid bytes length so the cache keeps working - return bytes.length - } + const sessionFilePath = GrpcSerializer.getOutputsUri(srcDocUri, sessionId) - const sessionFile = GrpcSerializer.getOutputsUri(srcDocUri, sessionId) - if (!sessionFile) { - this.togglePreviewButton(false) - return -1 + // If preview button is clicked, save the outputs to a file + const isPreview = GrpcSerializer.isPreviewOutput() + + if (isPreview) { + await ContextState.addKey(NOTEBOOK_PREVIEW_OUTPUTS, false) } - await workspace.fs.writeFile(sessionFile, bytes) - this.togglePreviewButton(true) + if (await GrpcSerializer.shouldWriteOutputs(sessionFilePath, isPreview)) { + if (!sessionFilePath) { + return -1 + } + await workspace.fs.writeFile(sessionFilePath, bytes) + } return bytes.length } + static isPreviewOutput(): boolean { + const isPreview = ContextState.getKey(NOTEBOOK_PREVIEW_OUTPUTS) + return isPreview + } + + static async shouldWriteOutputs(sessionFilePath: Uri, isPreview: boolean): Promise { + const isAutosaveOn = ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) + const isSignedIn = features.isOnInContextState(FeatureName.SignedIn) + const sessionFileExists = await this.sessionFileExists(sessionFilePath) + + return isPreview || (isAutosaveOn && !isSignedIn) || (isAutosaveOn && sessionFileExists) + } + + static async sessionFileExists(sessionFilePath: Uri): Promise { + try { + await workspace.fs.stat(sessionFilePath) + return true + } catch (e) { + return false + } + } + public async saveNotebookOutputs(uri: Uri): Promise { let cacheId: string | undefined this.cacheDocUriMapping.forEach((docUri, cid) => { @@ -770,13 +780,6 @@ export class GrpcSerializer extends SerializerBase { return result } - static sessionOutputsEnabled() { - const isAutoSaveOn = ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) - const isSessionOutputs = getSessionOutputs() - - return isSessionOutputs && isAutoSaveOn - } - private async cacheNotebookOutputs( notebook: Notebook, cacheId: string | undefined, diff --git a/src/extension/utils.ts b/src/extension/utils.ts index 9055b4fb4..c05405377 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -653,7 +653,7 @@ export function suggestCategories(categories: string[], title: string, placehold export async function handleNotebookAutosaveSettings() { const configAutoSaveSetting = getNotebookAutoSave() const defaultSetting = configAutoSaveSetting === NotebookAutoSaveSetting.Yes - const contextSetting = ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) + const contextSetting = ContextState.getKey(NOTEBOOK_AUTOSAVE_ON) await ContextState.addKey(NOTEBOOK_AUTOSAVE_ON, contextSetting ?? defaultSetting) } diff --git a/src/types.ts b/src/types.ts index deafb95a8..4a0719ba7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -380,7 +380,6 @@ interface Payload { content?: string initialRows?: number isAutoSaveEnabled: boolean - isSessionOutputsEnabled: boolean isPlatformAuthEnabled: boolean isDaggerOutput: boolean } diff --git a/tests/extension/cell.test.ts b/tests/extension/cell.test.ts index 4ab07fa56..8bfad47e3 100644 --- a/tests/extension/cell.test.ts +++ b/tests/extension/cell.test.ts @@ -127,12 +127,39 @@ function mockNotebookController(cell: NotebookCell) { } describe('NotebookCellOutputManager', () => { + it('should skip refresh terminal when shouldSkipRefreshTerminal returns true', async () => { + const cell = mockCell() + + const { controller, createExecution } = mockNotebookController(cell) + + const outputs = new NotebookCellOutputManager(cell, controller) + outputs.shouldSkipRefreshTerminal = vi.fn().mockReturnValue(true) + await outputs.showTerminal(true) + const serialize = vi.fn().mockImplementation(() => 'terminal test output') + ;(outputs as any).terminalState = { serialize, write: vi.fn() } as any + + const exec = mockCellExecution(cell) + createExecution.mockReturnValue(exec) + + const runmeExec = await outputs.createNotebookCellExecution() + expect(runmeExec).toBeDefined() + + const spy = vi.spyOn(outputs, 'refreshTerminal') + + runmeExec!.start() + runmeExec!.end(undefined) + + expect(spy).toBeCalledTimes(1) + expect(outputs.shouldSkipRefreshTerminal).toBeCalledTimes(1) + }) + it('marks document as dirty as part of refreshing the terminal state', async () => { const cell = mockCell() const { controller, createExecution } = mockNotebookController(cell) const outputs = new NotebookCellOutputManager(cell, controller) + outputs.shouldSkipRefreshTerminal = vi.fn().mockReturnValue(false) await outputs.showTerminal(true) const serialize = vi.fn().mockImplementation(() => 'terminal test output') ;(outputs as any).terminalState = { serialize, write: vi.fn() } as any @@ -149,6 +176,7 @@ describe('NotebookCellOutputManager', () => { runmeExec!.end(undefined) expect(spy).toBeCalledTimes(1) + expect(outputs.shouldSkipRefreshTerminal).toBeCalledTimes(1) }) it('uses current execution for outputs', async () => { diff --git a/tests/extension/provider/sessionOutputs.test.ts b/tests/extension/provider/sessionOutputs.test.ts index 2504ce5fb..708a111df 100644 --- a/tests/extension/provider/sessionOutputs.test.ts +++ b/tests/extension/provider/sessionOutputs.test.ts @@ -39,7 +39,7 @@ const contextFake: ExtensionContext = { StatefulAuthProvider.initialize(contextFake) -describe('Session Outputs Cell Status Bar provider', () => { +describe('Preview Outputs Cell Status Bar provider', () => { const kernel = new Kernel({} as any) it('should register commands when initializing', () => { new SessionOutputCellStatusBarProvider(kernel) diff --git a/tests/extension/serializer.test.ts b/tests/extension/serializer.test.ts index 0836d11a6..f91bdbf51 100644 --- a/tests/extension/serializer.test.ts +++ b/tests/extension/serializer.test.ts @@ -546,17 +546,15 @@ describe('GrpcSerializer', () => { describe('#saveNotebookOutputs', () => { beforeEach(() => { - const sessionOutputsEnabled = vi.fn().mockReturnValue(true) - GrpcSerializer.sessionOutputsEnabled = sessionOutputsEnabled + const shouldDisablePreviewOutputs = vi.fn().mockReturnValue(true) + GrpcSerializer.shouldDisablePreviewOutputs = shouldDisablePreviewOutputs }) const fakeCachedBytes = new Uint8Array([1, 2, 3, 4]) const serializer: any = new GrpcSerializer(context, new Server(), new Kernel()) const togglePreviewButton = vi.fn() serializer.togglePreviewButton = togglePreviewButton - it('skips if session outputs are disabled', async () => { - const sessionOutputsEnabled = vi.fn().mockReturnValue(false) - GrpcSerializer.sessionOutputsEnabled = sessionOutputsEnabled + it('skips if preview outputs are disabled', async () => { const fixture = deepCopyFixture() serializer.plainCache.set( fixture.metadata['runme.dev/frontmatterParsed'].runme.id, @@ -567,7 +565,7 @@ describe('GrpcSerializer', () => { metadata: fixture.metadata, }) - expect(togglePreviewButton).toBeCalledWith(false) + expect(workspace.fs.writeFile).toHaveBeenCalledTimes(0) }) it('skips if notebook has zero bytes', async () => { @@ -581,7 +579,7 @@ describe('GrpcSerializer', () => { metadata: fixture.metadata, }) - expect(togglePreviewButton).toBeCalledWith(false) + expect(workspace.fs.writeFile).toHaveBeenCalledTimes(0) }) it('skips if uri mapping to cacheId is unknown', async () => { @@ -595,7 +593,7 @@ describe('GrpcSerializer', () => { metadata: fixture.metadata, }) - expect(togglePreviewButton).toBeCalledWith(false) + expect(workspace.fs.writeFile).toHaveBeenCalledTimes(0) }) it('skips if session file mapping is unknown', async () => { @@ -614,7 +612,7 @@ describe('GrpcSerializer', () => { metadata: fixture.metadata, }) - expect(togglePreviewButton).toBeCalledWith(false) + expect(workspace.fs.writeFile).toHaveBeenCalledTimes(0) }) it('skips if runner env session in unknown', async () => { @@ -635,7 +633,7 @@ describe('GrpcSerializer', () => { metadata: fixture.metadata, }) - expect(togglePreviewButton).toBeCalledWith(false) + expect(workspace.fs.writeFile).toHaveBeenCalledTimes(0) }) it('writes cached bytes to session file on serialization and save', async () => { @@ -646,7 +644,6 @@ describe('GrpcSerializer', () => { } writeableSer.cacheDocUriMapping.set(fixture.metadata['runme.dev/cacheId'], fakeSrcDocUri) ContextState.getKey = vi.fn().mockImplementation(() => true) - GrpcSerializer.sessionOutputsEnabled = vi.fn().mockReturnValue(true) GrpcSerializer.getOutputsUri = vi.fn().mockImplementation(() => fakeSrcDocUri) const result = await writeableSer.serializeNotebook( @@ -710,7 +707,6 @@ describe('GrpcSerializer', () => { const ser = new GrpcSerializer(context, new Server(), new Kernel()) ;(ser as any).cacheDocUriMapping = { get: vi.fn().mockReturnValue(fakeSrcDocUri) } ;(ser as any).client = { serialize } - GrpcSerializer.sessionOutputsEnabled = vi.fn().mockReturnValue(true) await (ser as any).cacheNotebookOutputs(fixture, 'irrelevant')