From d8b67cacda0efca13f83454bdd65c44c94394370 Mon Sep 17 00:00:00 2001 From: Matt Seddon Date: Thu, 24 Aug 2023 19:32:06 +1000 Subject: [PATCH 1/3] use new signal file to select a new dvclive only experiment --- extension/src/experiments/index.ts | 27 +++++++++++--- extension/src/experiments/model/index.test.ts | 24 +++++++------ extension/src/experiments/model/index.ts | 36 ++++++++++++++++--- .../experiments/model/status/collect.test.ts | 24 ++++++++----- .../src/experiments/model/status/collect.ts | 18 +++++++--- extension/src/fileSystem/index.ts | 15 ++++++++ extension/src/plots/index.ts | 12 ++++++- 7 files changed, 124 insertions(+), 32 deletions(-) diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index 18c01d09a0..7ff753b151 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -43,7 +43,11 @@ import { createTypedAccumulator } from '../util/object' import { pickPaths } from '../path/selection/quickPick' import { Toast } from '../vscode/toast' import { ConfigKey, getConfigValue, setUserConfigValue } from '../vscode/config' -import { checkSignalFile, pollSignalFileForProcess } from '../fileSystem' +import { + checkSignalFile, + getDetailFromFile, + pollSignalFileForProcess +} from '../fileSystem' import { DVCLIVE_ONLY_RUNNING_SIGNAL_FILE } from '../cli/dvc/constants' import { Response } from '../vscode/response' import { Pipeline } from '../pipeline' @@ -599,6 +603,15 @@ export class Experiments extends BaseRepository { return this.webviewMessages.sendWebviewMessage() } + public checkDvcLiveOnly(fetched: string[]) { + const updated = this.experiments.checkDvcLiveOnly(fetched) + if (!updated) { + return + } + + this.notifyChanged() + } + protected sendInitialWebviewData() { return this.webviewMessages.sendWebviewMessage() } @@ -683,9 +696,13 @@ export class Experiments extends BaseRepository { } private async checkSignalFile() { - const dvcLiveOnly = await checkSignalFile(this.dvcLiveOnlySignalFile) + const running = await checkSignalFile(this.dvcLiveOnlySignalFile) - if (dvcLiveOnly && !this.dvcLiveOnlyCleanupInitialized) { + if (!running) { + return { running } + } + + if (!this.dvcLiveOnlyCleanupInitialized) { this.dvcLiveOnlyCleanupInitialized = true void pollSignalFileForProcess(this.dvcLiveOnlySignalFile, () => { this.dvcLiveOnlyCleanupInitialized = false @@ -694,7 +711,9 @@ export class Experiments extends BaseRepository { } }) } - return dvcLiveOnly + const expName = getDetailFromFile(this.dvcLiveOnlySignalFile, 'exp_name') + + return { expName, running } } private async informMaxSelected() { diff --git a/extension/src/experiments/model/index.test.ts b/extension/src/experiments/model/index.test.ts index a20c6999af..62cdd261b4 100644 --- a/extension/src/experiments/model/index.test.ts +++ b/extension/src/experiments/model/index.test.ts @@ -39,10 +39,10 @@ beforeEach(() => { const DEFAULT_DATA: [ string, - boolean, + { running: boolean; expName?: string }, { branch: string; sha: string }[], { [branch: string]: number } -] = ['', false, [], { main: 2000 }] +] = ['', { running: false }, [], { main: 2000 }] type TransformAndSetInputs = [ExpShowOutput, ...typeof DEFAULT_DATA] @@ -52,7 +52,7 @@ describe('ExperimentsModel', () => { model.transformAndSetLocal( outputFixture, gitLogFixture, - false, + { running: false }, rowOrderFixture, { main: 6 } ) @@ -65,7 +65,7 @@ describe('ExperimentsModel', () => { model.transformAndSetLocal( survivalOutputFixture, '', - false, + { running: false }, [ { branch: 'main', sha: '3d5adcb974bb2c85917a5d61a489b933adaa2b7f' }, { branch: 'main', sha: 'a49e03966a1f9f1299ec222ebc4bed8625d2c54d' }, @@ -107,7 +107,9 @@ describe('ExperimentsModel', () => { } ) - model.transformAndSetLocal(dvcLiveOnly, '', true, [], { main: 2000 }) + model.transformAndSetLocal(dvcLiveOnly, '', { running: true }, [], { + main: 2000 + }) // eslint-disable-next-line @typescript-eslint/no-explicit-any const runningWorkspace = (model as any).workspace expect(runningWorkspace?.executor).toStrictEqual(EXPERIMENT_WORKSPACE_ID) @@ -203,7 +205,7 @@ describe('ExperimentsModel', () => { model.transformAndSetLocal( deeplyNestedOutputFixture, '', - false, + { running: false }, [{ branch: 'main', sha: '53c3851f46955fa3e2b8f6e1c52999acc8c9ea77' }], { main: 10 } ) @@ -217,7 +219,7 @@ describe('ExperimentsModel', () => { model.transformAndSetLocal( dataTypesOutputFixture, '', - false, + { running: false }, [{ branch: 'main', sha: '53c3851f46955fa3e2b8f6e1c52999acc8c9ea77' }], { main: 10 } ) @@ -483,7 +485,7 @@ describe('ExperimentsModel', () => { const runningExperimentData: TransformAndSetInputs = [ outputFixture, gitLogFixture, - false, + { running: false }, [], { main: 2000 @@ -504,7 +506,7 @@ describe('ExperimentsModel', () => { } as ExpWithError ], gitLogFixture, - false, + { running: false }, [], { main: 2000 @@ -586,7 +588,7 @@ describe('ExperimentsModel', () => { error: { msg: errorMsg, type: 'caught error' } } ] - model.transformAndSetLocal(data, gitLogFixture, false, [], { + model.transformAndSetLocal(data, gitLogFixture, { running: false }, [], { main: 6 }) @@ -595,7 +597,7 @@ describe('ExperimentsModel', () => { model.transformAndSetLocal( outputFixture, gitLogFixture, - false, + { running: false }, rowOrderFixture, { main: 6 } ) diff --git a/extension/src/experiments/model/index.ts b/extension/src/experiments/model/index.ts index e16fdfeede..8d0b1790ea 100644 --- a/extension/src/experiments/model/index.ts +++ b/extension/src/experiments/model/index.ts @@ -29,7 +29,12 @@ import { WORKSPACE_BRANCH } from '../webview/contract' import { reorderListSubset } from '../../util/array' -import { Executor, ExpShowOutput, ExecutorStatus } from '../../cli/dvc/contract' +import { + Executor, + ExpShowOutput, + ExecutorStatus, + EXPERIMENT_WORKSPACE_ID +} from '../../cli/dvc/contract' import { flattenMapValues } from '../../util/map' import { ModelWithPersistence } from '../../persistence/model' import { PersistenceKey } from '../../persistence/constants' @@ -81,6 +86,8 @@ export class ExperimentsModel extends ModelWithPersistence { private running: RunningExperiment[] = [] private startedRunning: Set = new Set() + private dvcLiveOnlyExpName: string | undefined + constructor(dvcRoot: string, workspaceState: Memento) { super(dvcRoot, workspaceState) @@ -126,7 +133,7 @@ export class ExperimentsModel extends ModelWithPersistence { public transformAndSetLocal( expShow: ExpShowOutput, gitLog: string, - dvcLiveOnly: boolean, + dvcLiveOnly: { running: boolean; expName?: string }, rowOrder: { branch: string; sha: string }[], availableNbCommits: { [branch: string]: number } ) { @@ -137,7 +144,11 @@ export class ExperimentsModel extends ModelWithPersistence { hasCheckpoints, runningExperiments, workspace - } = collectExperiments(expShow, gitLog, dvcLiveOnly) + } = collectExperiments(expShow, gitLog, dvcLiveOnly.running) + + if (dvcLiveOnly.expName) { + this.dvcLiveOnlyExpName = dvcLiveOnly.expName + } const { hasMoreCommits, isShowingMoreCommits } = collectAddRemoveCommitsDetails(availableNbCommits, (branch: string) => @@ -528,6 +539,22 @@ export class ExperimentsModel extends ModelWithPersistence { return this.availableBranchesToSelect } + public checkDvcLiveOnly(fetched: string[]) { + if (!this.dvcLiveOnlyExpName) { + return false + } + if ( + fetched.includes(this.dvcLiveOnlyExpName) && + this.coloredStatus[EXPERIMENT_WORKSPACE_ID] === + this.coloredStatus[this.dvcLiveOnlyExpName] + ) { + this.coloredStatus[EXPERIMENT_WORKSPACE_ID] = UNSELECTED + this.dvcLiveOnlyExpName = undefined + this.persistStatus() + return true + } + } + private findIndexByPath(pathToRemove: string) { return this.currentSorts.findIndex(({ path }) => path === pathToRemove) } @@ -590,7 +617,8 @@ export class ExperimentsModel extends ModelWithPersistence { this.experimentsByCommit, this.coloredStatus, this.availableColors, - this.startedRunning + this.startedRunning, + this.dvcLiveOnlyExpName ) this.startedRunning = new Set() diff --git a/extension/src/experiments/model/status/collect.test.ts b/extension/src/experiments/model/status/collect.test.ts index c0580c33c5..c15c0e7259 100644 --- a/extension/src/experiments/model/status/collect.test.ts +++ b/extension/src/experiments/model/status/collect.test.ts @@ -27,7 +27,8 @@ describe('collectColoredStatus', () => { new Map(), {}, copyOriginalColors(), - new Set() + new Set(), + undefined ) expect(availableColors).toStrictEqual(colors) @@ -48,7 +49,8 @@ describe('collectColoredStatus', () => { new Map(), {}, copyOriginalColors(), - new Set() + new Set(), + undefined ) expect(availableColors).toStrictEqual(colors) @@ -74,7 +76,8 @@ describe('collectColoredStatus', () => { exp7: colors[6] }, [], - new Set(['exp8']) + new Set(['exp8']), + undefined ) expect(availableColors).toStrictEqual([]) @@ -107,7 +110,8 @@ describe('collectColoredStatus', () => { exp8: UNSELECTED }, copyOriginalColors().slice(3), - new Set(['exp1']) + new Set(['exp1']), + undefined ) expect(coloredStatus).toStrictEqual({ exp1: colors[0] }) @@ -129,7 +133,8 @@ describe('collectColoredStatus', () => { exp9: colors[1] }, unassignedColors, - new Set() + new Set(), + undefined ) expect(coloredStatus).toStrictEqual({ @@ -157,7 +162,8 @@ describe('collectColoredStatus', () => { new Map(), { exp9: colors[0] }, unassignColors, - new Set() + new Set(), + undefined ) expect(availableColors).toStrictEqual(unassignColors) @@ -190,7 +196,8 @@ describe('collectColoredStatus', () => { exp9: colors[5] }, copyOriginalColors().slice(6), - new Set(['exp1', 'exp2', 'exp3']) + new Set(['exp1', 'exp2', 'exp3']), + undefined ) expect(availableColors).toStrictEqual([]) @@ -228,7 +235,8 @@ describe('collectColoredStatus', () => { workspace: UNSELECTED }, colors, - new Set() + new Set(), + undefined ) expect(coloredStatus).toStrictEqual({ 'exp-1': selectedColor, diff --git a/extension/src/experiments/model/status/collect.ts b/extension/src/experiments/model/status/collect.ts index 584ee5a52c..af7fa59f7e 100644 --- a/extension/src/experiments/model/status/collect.ts +++ b/extension/src/experiments/model/status/collect.ts @@ -10,19 +10,28 @@ import { hasKey } from '../../../util/object' import { Experiment, isQueued, RunningExperiment } from '../../webview/contract' import { definedAndNonEmpty, reorderListSubset } from '../../../util/array' import { flattenMapValues } from '../../../util/map' -import { Executor } from '../../../cli/dvc/contract' +import { Executor, EXPERIMENT_WORKSPACE_ID } from '../../../cli/dvc/contract' const canAssign = ( coloredStatus: ColoredStatus, unassignedColors: Color[] ): boolean => canSelect(coloredStatus) && definedAndNonEmpty(unassignedColors) -const collectStatus = (acc: ColoredStatus, experiment: Experiment): void => { +const collectStatus = ( + acc: ColoredStatus, + experiment: Experiment, + dvcLiveOnlyExpName: string | undefined +): void => { const { id, executorStatus: status } = experiment if (!id || isQueued(status) || hasKey(acc, id)) { return } + if (acc[EXPERIMENT_WORKSPACE_ID] && id === dvcLiveOnlyExpName) { + acc[id] = acc[EXPERIMENT_WORKSPACE_ID] + return + } + acc[id] = UNSELECTED } @@ -89,7 +98,8 @@ export const collectColoredStatus = ( experimentsByCommit: Map, previousStatus: ColoredStatus, unassignedColors: Color[], - startedRunning: Set + startedRunning: Set, + dvcLiveOnlyExpName: string | undefined ): { coloredStatus: ColoredStatus; availableColors: Color[] } => { const flatExperimentsByCommit = flattenMapValues(experimentsByCommit) let availableColors = unassignColors( @@ -115,7 +125,7 @@ export const collectColoredStatus = ( ...workspaceAndCommits, ...flatExperimentsByCommit ]) { - collectStatus(coloredStatus, experiment) + collectStatus(coloredStatus, experiment, dvcLiveOnlyExpName) } return { diff --git a/extension/src/fileSystem/index.ts b/extension/src/fileSystem/index.ts index 314406100b..4391b41bb5 100644 --- a/extension/src/fileSystem/index.ts +++ b/extension/src/fileSystem/index.ts @@ -279,6 +279,21 @@ export const getPidFromFile = async ( return pid } +export const getDetailFromFile = ( + path: string, + key: string +): string | undefined => { + if (!exists(path)) { + return + } + + const contents = readFileSync(path).toString() + + try { + return (JSON.parse(contents) as { [key: string]: string })[key] + } catch {} +} + export const checkSignalFile = async (path: string): Promise => { return !!(await getPidFromFile(path)) } diff --git a/extension/src/plots/index.ts b/extension/src/plots/index.ts index 8dc77ac473..4cb94bb3b4 100644 --- a/extension/src/plots/index.ts +++ b/extension/src/plots/index.ts @@ -189,7 +189,17 @@ export class Plots extends BaseRepository { private async sendPlots() { await this.isReady() - return this.webviewMessages.sendWebviewMessage() + await this.webviewMessages.sendWebviewMessage() + + const fetchedRevs = [] + for (const { id, fetched } of this.plots.getSelectedRevisionDetails()) { + if (!fetched) { + continue + } + fetchedRevs.push(id) + } + + return this.experiments.checkDvcLiveOnly(fetchedRevs) } private createWebviewMessageHandler( From 8d81889e449782f54f071b696c767b75054be210 Mon Sep 17 00:00:00 2001 From: Matt Seddon Date: Fri, 25 Aug 2023 19:11:19 +1000 Subject: [PATCH 2/3] improve naming and tests --- extension/src/experiments/index.ts | 12 ++++-- extension/src/experiments/model/index.ts | 16 +++++--- .../experiments/model/status/collect.test.ts | 41 +++++++++++++++++++ .../src/experiments/model/status/collect.ts | 20 +++++++-- extension/src/fileSystem/index.test.ts | 41 ++++++++++++++++++- extension/src/fileSystem/index.ts | 9 ++-- extension/src/plots/index.ts | 9 +++- 7 files changed, 129 insertions(+), 19 deletions(-) diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index 7ff753b151..68a33a663c 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -45,7 +45,7 @@ import { Toast } from '../vscode/toast' import { ConfigKey, getConfigValue, setUserConfigValue } from '../vscode/config' import { checkSignalFile, - getDetailFromFile, + getEntryFromJsonFile, pollSignalFileForProcess } from '../fileSystem' import { DVCLIVE_ONLY_RUNNING_SIGNAL_FILE } from '../cli/dvc/constants' @@ -603,8 +603,12 @@ export class Experiments extends BaseRepository { return this.webviewMessages.sendWebviewMessage() } - public checkDvcLiveOnly(fetched: string[]) { - const updated = this.experiments.checkDvcLiveOnly(fetched) + public hasDvcLiveOnlyRunning() { + return this.experiments.hasDvcLiveOnlyRunning() + } + + public checkWorkspaceDuplicated(fetched: string[]) { + const updated = this.experiments.checkWorkspaceDuplicated(fetched) if (!updated) { return } @@ -711,7 +715,7 @@ export class Experiments extends BaseRepository { } }) } - const expName = getDetailFromFile(this.dvcLiveOnlySignalFile, 'exp_name') + const expName = getEntryFromJsonFile(this.dvcLiveOnlySignalFile, 'exp_name') return { expName, running } } diff --git a/extension/src/experiments/model/index.ts b/extension/src/experiments/model/index.ts index 8d0b1790ea..e573579c24 100644 --- a/extension/src/experiments/model/index.ts +++ b/extension/src/experiments/model/index.ts @@ -539,15 +539,21 @@ export class ExperimentsModel extends ModelWithPersistence { return this.availableBranchesToSelect } - public checkDvcLiveOnly(fetched: string[]) { + public hasDvcLiveOnlyRunning() { + return !!this.dvcLiveOnlyExpName + } + + public checkWorkspaceDuplicated(fetched: string[]) { if (!this.dvcLiveOnlyExpName) { return false } - if ( - fetched.includes(this.dvcLiveOnlyExpName) && + + const newExperimentFetched = fetched.includes(this.dvcLiveOnlyExpName) + const workspaceSelectionDuplicated = this.coloredStatus[EXPERIMENT_WORKSPACE_ID] === - this.coloredStatus[this.dvcLiveOnlyExpName] - ) { + this.coloredStatus[this.dvcLiveOnlyExpName] + + if (newExperimentFetched && workspaceSelectionDuplicated) { this.coloredStatus[EXPERIMENT_WORKSPACE_ID] = UNSELECTED this.dvcLiveOnlyExpName = undefined this.persistStatus() diff --git a/extension/src/experiments/model/status/collect.test.ts b/extension/src/experiments/model/status/collect.test.ts index c15c0e7259..f21db36dc1 100644 --- a/extension/src/experiments/model/status/collect.test.ts +++ b/extension/src/experiments/model/status/collect.test.ts @@ -248,4 +248,45 @@ describe('collectColoredStatus', () => { colors.filter(color => color !== selectedColor) ) }) + + it("should duplicate the workspace's color when a new experiment is provided that has the same name as found in the DVCLive only signal file", () => { + const colors = copyOriginalColors() + const selectedColor = colors[2] + const { availableColors, coloredStatus } = collectColoredStatus( + [ + { + executor: null, + executorStatus: ExecutorStatus.SUCCESS, + id: 'exp-1' + }, + { + id: EXPERIMENT_WORKSPACE_ID + }, + { id: 'main' }, + { + executor: null, + executorStatus: ExecutorStatus.SUCCESS, + id: 'exp-2' + } + ] as Experiment[], + new Map(), + { + 'exp-1': UNSELECTED, + workspace: selectedColor + }, + colors, + new Set(), + 'exp-2' + ) + expect(coloredStatus).toStrictEqual({ + [EXPERIMENT_WORKSPACE_ID]: selectedColor, + 'exp-1': UNSELECTED, + 'exp-2': selectedColor, + main: UNSELECTED + }) + + expect(availableColors).toStrictEqual( + colors.filter(color => color !== selectedColor) + ) + }) }) diff --git a/extension/src/experiments/model/status/collect.ts b/extension/src/experiments/model/status/collect.ts index af7fa59f7e..1baa940cf2 100644 --- a/extension/src/experiments/model/status/collect.ts +++ b/extension/src/experiments/model/status/collect.ts @@ -17,6 +17,18 @@ const canAssign = ( unassignedColors: Color[] ): boolean => canSelect(coloredStatus) && definedAndNonEmpty(unassignedColors) +const isWorkspaceSelected = (acc: ColoredStatus) => + !!acc[EXPERIMENT_WORKSPACE_ID] + +const isFinishedDvcLiveOnlyExp = ( + id: string, + dvcLiveOnlyExpName: string | undefined +): boolean => id === dvcLiveOnlyExpName + +const duplicateWorkspaceColor = (acc: ColoredStatus, id: string) => { + acc[id] = acc[EXPERIMENT_WORKSPACE_ID] +} + const collectStatus = ( acc: ColoredStatus, experiment: Experiment, @@ -27,9 +39,11 @@ const collectStatus = ( return } - if (acc[EXPERIMENT_WORKSPACE_ID] && id === dvcLiveOnlyExpName) { - acc[id] = acc[EXPERIMENT_WORKSPACE_ID] - return + if ( + isWorkspaceSelected(acc) && + isFinishedDvcLiveOnlyExp(id, dvcLiveOnlyExpName) + ) { + return duplicateWorkspaceColor(acc, id) } acc[id] = UNSELECTED diff --git a/extension/src/fileSystem/index.test.ts b/extension/src/fileSystem/index.test.ts index 17644bb84e..47425140d2 100644 --- a/extension/src/fileSystem/index.test.ts +++ b/extension/src/fileSystem/index.test.ts @@ -20,13 +20,15 @@ import { writeCsv, writeTsv, isPathInProject, - getPidFromFile + getPidFromFile, + getEntryFromJsonFile } from '.' import { dvcDemoPath } from '../test/util' import { DOT_DVC } from '../cli/dvc/constants' import { ScriptCommand } from '../pipeline' import { processExists } from '../process/execution' +jest.mock('../common/logger') jest.mock('../cli/dvc/reader') jest.mock('../process/execution') jest.mock('fs-extra', () => { @@ -518,3 +520,40 @@ describe('getPidFromFile', () => { expect(pid).toBe(3676) }) }) + +describe('getEntryFromJsonFile', () => { + it('should return undefined if the file does not exist', () => { + const undef = getEntryFromJsonFile('no-file', 'no-keys') + + expect(mockedReadFileSync).toHaveBeenCalledTimes(1) + expect(undef).toBeUndefined() + }) + + it('should return undefined for a file containing a string', () => { + mockedReadFileSync.mockReturnValueOnce(Buffer.from('string')) + const undef = getEntryFromJsonFile(__filename, 'no-keys') + + expect(mockedReadFileSync).toHaveBeenCalledTimes(1) + expect(undef).toBeUndefined() + }) + + it('should return undefined it the file does not contain the key', () => { + mockedReadFileSync.mockReturnValueOnce( + JSON.stringify({ 'other-key': '3676' }) + ) + mockedProcessExists.mockResolvedValueOnce(true) + const undef = getEntryFromJsonFile(__filename, 'key') + + expect(mockedReadFileSync).toHaveBeenCalledTimes(1) + expect(undef).toBeUndefined() + }) + + it('should return the value if the file contains the key', () => { + mockedReadFileSync.mockReturnValueOnce(JSON.stringify({ key: 'value' })) + mockedProcessExists.mockResolvedValueOnce(true) + const value = getEntryFromJsonFile(__filename, 'key') + + expect(mockedReadFileSync).toHaveBeenCalledTimes(1) + expect(value).toStrictEqual('value') + }) +}) diff --git a/extension/src/fileSystem/index.ts b/extension/src/fileSystem/index.ts index 4391b41bb5..fe81f5901b 100644 --- a/extension/src/fileSystem/index.ts +++ b/extension/src/fileSystem/index.ts @@ -279,18 +279,17 @@ export const getPidFromFile = async ( return pid } -export const getDetailFromFile = ( +export const getEntryFromJsonFile = ( path: string, key: string ): string | undefined => { - if (!exists(path)) { + const json = loadJson(path) + if (!json) { return } - const contents = readFileSync(path).toString() - try { - return (JSON.parse(contents) as { [key: string]: string })[key] + return (json as { [key: string]: string })[key] } catch {} } diff --git a/extension/src/plots/index.ts b/extension/src/plots/index.ts index 4cb94bb3b4..812b0add28 100644 --- a/extension/src/plots/index.ts +++ b/extension/src/plots/index.ts @@ -190,6 +190,13 @@ export class Plots extends BaseRepository { await this.isReady() await this.webviewMessages.sendWebviewMessage() + return this.checkDvcLiveOnlyDuplicate() + } + + private checkDvcLiveOnlyDuplicate() { + if (!this.experiments.hasDvcLiveOnlyRunning()) { + return + } const fetchedRevs = [] for (const { id, fetched } of this.plots.getSelectedRevisionDetails()) { @@ -199,7 +206,7 @@ export class Plots extends BaseRepository { fetchedRevs.push(id) } - return this.experiments.checkDvcLiveOnly(fetchedRevs) + return this.experiments.checkWorkspaceDuplicated(fetchedRevs) } private createWebviewMessageHandler( From 5b6342c35b7d432ecb21a7b3239a0cb91185aa41 Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Sat, 26 Aug 2023 05:28:39 +1000 Subject: [PATCH 3/3] fix typo Co-authored-by: Julie G <43496356+julieg18@users.noreply.github.com> --- extension/src/fileSystem/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/src/fileSystem/index.test.ts b/extension/src/fileSystem/index.test.ts index 47425140d2..7b811dc8ea 100644 --- a/extension/src/fileSystem/index.test.ts +++ b/extension/src/fileSystem/index.test.ts @@ -537,7 +537,7 @@ describe('getEntryFromJsonFile', () => { expect(undef).toBeUndefined() }) - it('should return undefined it the file does not contain the key', () => { + it('should return undefined if the file does not contain the key', () => { mockedReadFileSync.mockReturnValueOnce( JSON.stringify({ 'other-key': '3676' }) )