diff --git a/extension/src/experiments/commands/register.ts b/extension/src/experiments/commands/register.ts index 2b0d55acbd..dc32fc11ff 100644 --- a/extension/src/experiments/commands/register.ts +++ b/extension/src/experiments/commands/register.ts @@ -257,7 +257,7 @@ const registerExperimentQuickPickCommands = ( internalCommands.registerExternalCommand( RegisteredCommands.EXPERIMENT_SELECT, (context: Context) => - experiments.selectExperiments(getDvcRootFromContext(context)) + experiments.selectExperimentsToPlot(getDvcRootFromContext(context)) ) internalCommands.registerExternalCommand( diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index 0e428a0a4d..deb902419d 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -30,13 +30,18 @@ import { starredSort } from './model/sortBy/constants' import { pickSortsToRemove, pickSortToAdd } from './model/sortBy/quickPick' import { ColumnsModel } from './columns/model' import { ExperimentsData } from './data' -import { Experiment, ColumnType, TableData } from './webview/contract' +import { + Experiment, + ColumnType, + TableData, + isRunning +} from './webview/contract' import { WebviewMessages } from './webview/messages' import { DecorationProvider } from './model/decorationProvider' import { starredFilter } from './model/filterBy/constants' import { ResourceLocator } from '../resourceLocator' import { AvailableCommands, InternalCommands } from '../commands/internal' -import { EXPERIMENT_WORKSPACE_ID, ExpShowOutput } from '../cli/dvc/contract' +import { ExpShowOutput } from '../cli/dvc/contract' import { ViewKey } from '../webview/constants' import { BaseRepository } from '../webview/repository' import { Title } from '../vscode/title' @@ -217,15 +222,6 @@ export class Experiments extends BaseRepository { public toggleExperimentStatus( id: string ): Color | typeof UNSELECTED | undefined { - if ( - this.experiments.isRunningInWorkspace(id) && - !this.experiments.isSelected(id) - ) { - return this.experiments.isSelected(EXPERIMENT_WORKSPACE_ID) - ? undefined - : this.toggleExperimentStatus(EXPERIMENT_WORKSPACE_ID) - } - const selected = this.experiments.isSelected(id) if (!selected && !this.experiments.canSelect()) { return @@ -353,8 +349,10 @@ export class Experiments extends BaseRepository { return this.experiments.getWorkspaceCommitsAndExperiments() } - public async selectExperiments() { - const experiments = this.experiments.getWorkspaceCommitsAndExperiments() + public async selectExperimentsToPlot() { + const experiments = this.experiments + .getWorkspaceCommitsAndExperiments() + .filter(({ status }) => !isRunning(status)) const selected = await pickExperimentsToPlot( experiments, diff --git a/extension/src/experiments/model/index.test.ts b/extension/src/experiments/model/index.test.ts index 0e801c5a20..c67bde6495 100644 --- a/extension/src/experiments/model/index.test.ts +++ b/extension/src/experiments/model/index.test.ts @@ -18,7 +18,8 @@ import survivalRowsFixture from '../../test/fixtures/expShow/survival/rows' import { ExperimentStatus, - EXPERIMENT_WORKSPACE_ID + EXPERIMENT_WORKSPACE_ID, + Executor } from '../../cli/dvc/contract' import { PersistenceKey } from '../../persistence/constants' @@ -220,6 +221,68 @@ describe('ExperimentsModel', () => { expect(experimentsModel.getSelectedRevisions()).toHaveLength(7) }) + it('should swap an experiment running in the workspace for the workspace and not select an experiment running in the queue', () => { + const params = { + params: { + 'params.yaml': { + data: { + lr: 0.000000000000001 + } + } + } + } + const data = generateTestExpShowOutput( + {}, + { + experiments: [ + { + data: params, + executor: { + local: null, + name: Executor.WORKSPACE, + state: ExperimentStatus.RUNNING + }, + name: 'unselectable-nuffy', + rev: EXPERIMENT_WORKSPACE_ID + }, + {}, + {}, + {}, + { + data: params, + executor: { + local: null, + name: Executor.DVC_TASK, + state: ExperimentStatus.RUNNING + }, + name: 'unselectable-task', + rev: EXPERIMENT_WORKSPACE_ID + } + ], + rev: 'testBranch' + } + ) + + const model = new ExperimentsModel('', buildMockMemento()) + model.transformAndSet(data, false, '') + + expect(model.getSelectedRevisions().map(({ id }) => id)).toStrictEqual([ + EXPERIMENT_WORKSPACE_ID + ]) + + model.setSelected([]) + expect(model.getSelectedRevisions().map(({ id }) => id)).toStrictEqual([]) + + model.setSelected(model.getCombinedList()) + expect(model.getSelectedRevisions().map(({ id }) => id)).toStrictEqual([ + EXPERIMENT_WORKSPACE_ID, + 'testBranch', + 'exp-2', + 'exp-3', + 'exp-4' + ]) + }) + it('should fetch commit params', () => { const model = new ExperimentsModel('', buildMockMemento()) model.transformAndSet(outputFixture, false, '') diff --git a/extension/src/experiments/model/index.ts b/extension/src/experiments/model/index.ts index daeb8bb26f..ad8ea3e035 100644 --- a/extension/src/experiments/model/index.ts +++ b/extension/src/experiments/model/index.ts @@ -5,26 +5,26 @@ import { collectExperiments } from './collect' import { collectColoredStatus, collectFinishedRunningExperiments, - collectSelected, + collectSelectable, + collectSelectedColors, collectStartedRunningExperiments } from './status/collect' import { Color, copyOriginalColors } from './status/colors' -import { - canSelect, - limitToMaxSelected, - ColoredStatus, - tooManySelected, - UNSELECTED -} from './status' +import { canSelect, ColoredStatus, UNSELECTED } from './status' import { collectFlatExperimentParams } from './modify/collect' import { Experiment, isQueued, + isRunning, isRunningInQueue, RunningExperiment } from '../webview/contract' import { definedAndNonEmpty, reorderListSubset } from '../../util/array' -import { EXPERIMENT_WORKSPACE_ID, ExpShowOutput } from '../../cli/dvc/contract' +import { + EXPERIMENT_WORKSPACE_ID, + Executor, + ExpShowOutput +} from '../../cli/dvc/contract' import { flattenMapValues } from '../../util/map' import { ModelWithPersistence } from '../../persistence/model' import { PersistenceKey } from '../../persistence/constants' @@ -136,14 +136,16 @@ export class ExperimentsModel extends ModelWithPersistence { } public toggleStatus(id: string) { - if ( - isQueued( - this.getExperimentsAndQueued().find( - ({ id: queuedId }) => queuedId === id - )?.status - ) - ) { - return + const experiment = this.getExperimentsAndQueued().find( + ({ id: expId }) => expId === id + ) + + if (experiment && isRunning(experiment.status)) { + return this.preventSelectionOfRunningExperiment(experiment) + } + + if (isQueued(experiment?.status)) { + return UNSELECTED } const current = this.coloredStatus[id] @@ -166,17 +168,6 @@ export class ExperimentsModel extends ModelWithPersistence { return this.running.length > 0 } - public isRunningInWorkspace(id: string) { - if (id === EXPERIMENT_WORKSPACE_ID) { - return false - } - - return this.running.some( - ({ id: runningId, executor }) => - executor === EXPERIMENT_WORKSPACE_ID && runningId === id - ) - } - public hasCheckpoints() { return this.checkpoints } @@ -255,12 +246,12 @@ export class ExperimentsModel extends ModelWithPersistence { } public setSelected(selectedExperiments: Experiment[]) { - if (tooManySelected(selectedExperiments)) { - selectedExperiments = limitToMaxSelected(selectedExperiments) - } + const possibleToSelect = collectSelectable(selectedExperiments, { + ...this.workspace + }) - const { availableColors, coloredStatus } = collectSelected( - selectedExperiments.filter(({ status }) => !isQueued(status)), + const { availableColors, coloredStatus } = collectSelectedColors( + possibleToSelect, this.getWorkspaceCommitsAndExperiments(), this.coloredStatus, this.availableColors @@ -505,6 +496,24 @@ export class ExperimentsModel extends ModelWithPersistence { ) } + private preventSelectionOfRunningExperiment( + experiment: Experiment + ): Color | undefined | typeof UNSELECTED { + if (isRunningInQueue(experiment)) { + return UNSELECTED + } + + const { executor, id } = experiment + if ( + executor === Executor.WORKSPACE && + id !== EXPERIMENT_WORKSPACE_ID && + !this.isSelected(id) && + !this.isSelected(EXPERIMENT_WORKSPACE_ID) + ) { + return this.toggleStatus(EXPERIMENT_WORKSPACE_ID) + } + } + private persistSorts() { return this.persist(PersistenceKey.EXPERIMENTS_SORT_BY, this.currentSorts) } diff --git a/extension/src/experiments/model/status/collect.ts b/extension/src/experiments/model/status/collect.ts index 54377201b2..91fdfb8389 100644 --- a/extension/src/experiments/model/status/collect.ts +++ b/extension/src/experiments/model/status/collect.ts @@ -1,15 +1,22 @@ -import { canSelect, ColoredStatus, UNSELECTED } from '.' +import { + canSelect, + ColoredStatus, + limitToMaxSelected, + tooManySelected, + UNSELECTED +} from '.' import { Color, copyOriginalColors } from './colors' import { hasKey } from '../../../util/object' import { Experiment, isQueued, isRunning, + isRunningInQueue, RunningExperiment } from '../../webview/contract' import { definedAndNonEmpty, reorderListSubset } from '../../../util/array' import { flattenMapValues } from '../../../util/map' -import { EXPERIMENT_WORKSPACE_ID } from '../../../cli/dvc/contract' +import { Executor, EXPERIMENT_WORKSPACE_ID } from '../../../cli/dvc/contract' const canAssign = ( coloredStatus: ColoredStatus, @@ -187,7 +194,50 @@ const assignSelected = ( return { availableColors, coloredStatus } } -export const collectSelected = ( +const cannotSelect = ( + ids: Set, + { executor, id, status }: Experiment +): boolean => + isQueued(status) || isRunningInQueue({ executor, status }) || ids.has(id) + +const shouldSelectWorkspace = ({ executor, status }: Experiment): boolean => + executor === Executor.WORKSPACE && isRunning(status) + +const collectWorkspace = ( + acc: Experiment[], + collectedIds: Set, + workspace: Experiment +) => { + if (!collectedIds.has(EXPERIMENT_WORKSPACE_ID)) { + acc.push(workspace) + } + collectedIds.add(EXPERIMENT_WORKSPACE_ID) +} + +export const collectSelectable = ( + selectedExperiments: Experiment[], + workspace: Experiment +): Experiment[] => { + const acc: Experiment[] = [] + const collectedIds = new Set() + + for (const experiment of selectedExperiments) { + if (cannotSelect(collectedIds, experiment)) { + continue + } + + if (shouldSelectWorkspace(experiment)) { + collectWorkspace(acc, collectedIds, workspace) + continue + } + acc.push(experiment) + collectedIds.add(experiment.id) + } + + return tooManySelected(acc) ? limitToMaxSelected(acc) : acc +} + +export const collectSelectedColors = ( selectedExperiments: Experiment[], experiments: Experiment[], previousStatus: ColoredStatus, @@ -217,7 +267,10 @@ export const collectStartedRunningExperiments = ( const acc = new Set() for (const { id: runningId, executor } of nowRunning) { - if (previouslyRunning.some(({ id }) => id === runningId)) { + if ( + executor === Executor.DVC_TASK || + previouslyRunning.some(({ id }) => id === runningId) + ) { continue } acc.add( diff --git a/extension/src/experiments/workspace.ts b/extension/src/experiments/workspace.ts index a276c38374..0f8f256382 100644 --- a/extension/src/experiments/workspace.ts +++ b/extension/src/experiments/workspace.ts @@ -142,12 +142,12 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews< return this.getRepository(dvcRoot).removeSorts() } - public async selectExperiments(overrideRoot?: string) { + public async selectExperimentsToPlot(overrideRoot?: string) { const dvcRoot = await this.getDvcRoot(overrideRoot) if (!dvcRoot) { return } - return this.getRepository(dvcRoot).selectExperiments() + return this.getRepository(dvcRoot).selectExperimentsToPlot() } public async selectColumns(overrideRoot?: string) { diff --git a/extension/src/plots/webview/messages.ts b/extension/src/plots/webview/messages.ts index c4d513ba16..06b08c9e20 100644 --- a/extension/src/plots/webview/messages.ts +++ b/extension/src/plots/webview/messages.ts @@ -220,7 +220,7 @@ export class WebviewMessages { } private selectExperimentsFromWebview() { - void this.experiments.selectExperiments() + void this.experiments.selectExperimentsToPlot() sendTelemetryEvent( EventName.VIEWS_PLOTS_SELECT_EXPERIMENTS, undefined, diff --git a/extension/src/test/fixtures/expShow/base/rows.ts b/extension/src/test/fixtures/expShow/base/rows.ts index 0b1a806b98..d46011420a 100644 --- a/extension/src/test/fixtures/expShow/base/rows.ts +++ b/extension/src/test/fixtures/expShow/base/rows.ts @@ -150,7 +150,7 @@ const data: Commit[] = [ 'c3961d777cfbd7727f9fde4851896006' ) }, - displayColor: colorsList[1], + displayColor: undefined, description: '[exp-e7a67]', executor: Executor.DVC_TASK, id: 'exp-e7a67', @@ -179,7 +179,7 @@ const data: Commit[] = [ } }, status: ExperimentStatus.RUNNING, - selected: true, + selected: false, sha: '4fb124aebddb2adf1545030907687fa9a4c80e70', starred: false, Created: '2020-12-29T15:31:52' diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index 3a697ecebe..8b09d548da 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -924,17 +924,25 @@ suite('Experiments Test Suite', () => { await experiments.isReady() - const experimentToToggle = 'exp-e7a67' - const queuedExperiment = '90aea7f2482117a55dfcadcdb901aaa6610fbbc9' + const idToToggle = 'test-branch' + const runningInQueueId = 'exp-e7a67' + const queuedId = '90aea7f' const isExperimentSelected = (expId: string): boolean => !!experimentsModel.getCombinedList().find(({ id }) => id === expId) ?.selected - expect(isExperimentSelected(experimentToToggle), 'experiment is selected') - .to.be.true expect( - isExperimentSelected(queuedExperiment), + isExperimentSelected(idToToggle), + 'experiment is not initially selected' + ).to.be.false + + expect( + isExperimentSelected(runningInQueueId), + 'experiment running in the queue cannot be selected' + ).to.be.false + expect( + isExperimentSelected(queuedId), 'queued experiment cannot be selected' ).to.be.false @@ -945,27 +953,52 @@ suite('Experiments Test Suite', () => { const toggleSpy = spy(experimentsModel, 'toggleStatus') mockMessageReceived.fire({ - payload: experimentToToggle, + payload: idToToggle, type: MessageFromWebviewType.TOGGLE_EXPERIMENT }) - expect(toggleSpy).to.be.calledWith(experimentToToggle) + expect(toggleSpy).to.be.calledWith(idToToggle) toggleSpy.resetHistory() expect( - isExperimentSelected(experimentToToggle), + isExperimentSelected(idToToggle), + 'experiment has been toggled to selected' + ).to.be.true + + mockMessageReceived.fire({ + payload: idToToggle, + type: MessageFromWebviewType.TOGGLE_EXPERIMENT + }) + + expect(toggleSpy).to.be.calledWith(idToToggle) + toggleSpy.resetHistory() + + expect( + isExperimentSelected(idToToggle), 'experiment has been toggled to unselected' ).to.be.false mockMessageReceived.fire({ - payload: queuedExperiment, + payload: runningInQueueId, + type: MessageFromWebviewType.TOGGLE_EXPERIMENT + }) + + expect(toggleSpy).to.be.calledWith(runningInQueueId) + + expect( + isExperimentSelected(runningInQueueId), + 'experiment running the queue cannot be selected' + ).to.be.false + + mockMessageReceived.fire({ + payload: queuedId, type: MessageFromWebviewType.TOGGLE_EXPERIMENT }) - expect(toggleSpy).to.be.calledWith(queuedExperiment) + expect(toggleSpy).to.be.calledWith(queuedId) expect( - isExperimentSelected(queuedExperiment), + isExperimentSelected(queuedId), 'queued experiment cannot be selected' ).to.be.false }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -1180,9 +1213,10 @@ suite('Experiments Test Suite', () => { const webview = await experiments.showWebview() const mockMessageReceived = getMessageReceivedEmitter(webview) - const queuedId = '90aea7f2482117a55dfcadcdb901aaa6610fbbc9' - const expectedIds = ['exp-e7a67', 'test-branch'] - const mockExperimentIds = [...expectedIds, queuedId] + const queuedId = '90aea7f' + const runningInQueueId = 'exp-e7a67' + const expectedIds = ['main', 'test-branch'] + const mockExperimentIds = [...expectedIds, queuedId, runningInQueueId] stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) @@ -1202,7 +1236,7 @@ suite('Experiments Test Suite', () => { mockExperimentIds.sort() expect( selectExperimentIds, - 'should exclude queued experiments from selection' + 'should exclude queued experiments and experiments running in the queue from selection' ).to.deep.equal(expectedIds) }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -1223,14 +1257,15 @@ suite('Experiments Test Suite', () => { const webview = await experiments.showWebview() const mockMessageReceived = getMessageReceivedEmitter(webview) - const mockExperimentIds = ['exp-e7a67', 'test-branch'] + const runningInQueueId = 'exp-e7a67' + const mockExperimentIds = ['main', 'test-branch'] stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) const tableChangePromise = experimentsUpdatedEvent(experiments) mockMessageReceived.fire({ - payload: mockExperimentIds, + payload: [...mockExperimentIds, runningInQueueId], type: MessageFromWebviewType.SET_EXPERIMENTS_AND_OPEN_PLOTS }) @@ -1752,11 +1787,11 @@ suite('Experiments Test Suite', () => { ).to.deep.equal({ '489fd8b': 0, '55d492c': 0, - 'exp-e7a67': colors[1], + 'exp-e7a67': 0, 'exp-f13bca': 0, main: 0, 'test-branch': 0, - workspace: colors[0] + [EXPERIMENT_WORKSPACE_ID]: colors[0] }) expect( @@ -1840,7 +1875,7 @@ suite('Experiments Test Suite', () => { 'both filters should be removed from memento after removeFilters is run against them' ).to.deep.equal([]) - testRepository.toggleExperimentStatus('exp-e7a67') + testRepository.toggleExperimentStatus('exp-f13bca') expect( mockMemento.get('experimentsStatus:test'), 'the correct statuses have been recorded in the memento' @@ -1848,10 +1883,10 @@ suite('Experiments Test Suite', () => { '489fd8b': 0, '55d492c': 0, 'exp-e7a67': 0, - 'exp-f13bca': 0, + 'exp-f13bca': colors[1], main: 0, 'test-branch': 0, - workspace: colors[0] + [EXPERIMENT_WORKSPACE_ID]: colors[0] }) }) @@ -1862,7 +1897,7 @@ suite('Experiments Test Suite', () => { 'experimentsFilterBy:test': filterMapEntries, 'experimentsSortBy:test': sortDefinitions, 'experimentsStatus:test': { - 'exp-83425': colors[0], + [EXPERIMENT_WORKSPACE_ID]: colors[0], 'exp-e7a67': colors[5], 'exp-f13bca': 0, 'test-branch': colors[1] @@ -1902,16 +1937,12 @@ suite('Experiments Test Suite', () => { ).to.deep.equal([ { displayColor: colors[0], - id: 'exp-83425' + id: EXPERIMENT_WORKSPACE_ID }, { displayColor: colors[1], id: 'test-branch' }, - { - displayColor: colors[2], - id: EXPERIMENT_WORKSPACE_ID - }, { displayColor: colors[5], id: 'exp-e7a67' diff --git a/extension/src/test/suite/experiments/model/tree.test.ts b/extension/src/test/suite/experiments/model/tree.test.ts index b8692f7592..b88b3be0e3 100644 --- a/extension/src/test/suite/experiments/model/tree.test.ts +++ b/extension/src/test/suite/experiments/model/tree.test.ts @@ -38,6 +38,8 @@ import { EXPERIMENT_WORKSPACE_ID } from '../../../../cli/dvc/contract' import { DvcReader } from '../../../../cli/dvc/reader' import { copyOriginalColors } from '../../../../experiments/model/status/colors' import { Revision } from '../../../../plots/webview/contract' +import { BaseWebview } from '../../../../webview' +import { Experiment } from '../../../../experiments/webview/contract' suite('Experiments Tree Test Suite', () => { const disposable = getTimeSafeDisposer() @@ -61,17 +63,21 @@ suite('Experiments Tree Test Suite', () => { it('should be able to toggle whether an experiment is shown in the plots webview with dvc.views.experiments.toggleStatus', async () => { const mockNow = getMockNow() - const { plots, messageSpy, plotsModel, experiments } = await buildPlots( - disposable - ) + const { plots, messageSpy, plotsModel, experimentsModel } = + await buildPlots(disposable) + messageSpy.restore() + const mockShow = stub(BaseWebview.prototype, 'show') + + experimentsModel.setSelected([ + { id: EXPERIMENT_WORKSPACE_ID }, + { id: 'main' }, + { id: 'test-branch' }, + { id: 'exp-f13bca' } + ] as Experiment[]) const expectedRevisions: { displayColor: string; id: string }[] = [] const [{ id }] = plotsModel.getSelectedRevisionDetails() - const mockCheckForFinishedWorkspaceExperiment = stub( - experiments, - 'checkForFinishedWorkspaceExperiment' - ) for (const { id, @@ -86,7 +92,7 @@ suite('Experiments Tree Test Suite', () => { let updateCall = 1 while (expectedRevisions.length > 0) { - const { selectedRevisions } = getFirstArgOfLastCall(messageSpy) + const { selectedRevisions } = mockShow.lastCall.firstArg expect( (selectedRevisions as Revision[]).map(({ displayColor, id }) => ({ @@ -95,16 +101,19 @@ suite('Experiments Tree Test Suite', () => { })), 'a message is sent with colors for the currently selected experiments' ).to.deep.equal(expectedRevisions) - messageSpy.resetHistory() + mockShow.resetHistory() + mockShow.resetBehavior() const { id } = expectedRevisions.pop() as { id: string } - bypassProcessManagerDebounce(mockNow, updateCall) const messageSent = new Promise(resolve => - mockCheckForFinishedWorkspaceExperiment.callsFake(() => + mockShow.callsFake(() => { resolve(undefined) - ) + return Promise.resolve(true) + }) ) + + bypassProcessManagerDebounce(mockNow, updateCall) const unSelected = await commands.executeCommand( RegisteredCommands.EXPERIMENT_TOGGLE, { @@ -116,16 +125,22 @@ suite('Experiments Tree Test Suite', () => { expect(unSelected).to.equal(UNSELECTED) await messageSent - mockCheckForFinishedWorkspaceExperiment.resetBehavior() } expect( - messageSpy, + mockShow, "when there are no experiments selected we don't send any template plots" ).to.be.calledWithMatch({ template: null }) - messageSpy.resetHistory() + mockShow.resetHistory() + + const messageSent = new Promise(resolve => + mockShow.callsFake(() => { + resolve(undefined) + return Promise.resolve(true) + }) + ) bypassProcessManagerDebounce(mockNow, updateCall) const selected = await commands.executeCommand( @@ -136,6 +151,8 @@ suite('Experiments Tree Test Suite', () => { } ) + await messageSent + expect(selected, 'the experiment is now selected').to.equal( copyOriginalColors()[0] ) diff --git a/extension/src/test/suite/plots/index.test.ts b/extension/src/test/suite/plots/index.test.ts index 63f5a1f377..6e4c4bb8a7 100644 --- a/extension/src/test/suite/plots/index.test.ts +++ b/extension/src/test/suite/plots/index.test.ts @@ -496,7 +496,7 @@ suite('Plots Test Suite', () => { const mockSelectExperiments = stub( experiments, - 'selectExperiments' + 'selectExperimentsToPlot' ).resolves(undefined) const webview = await plots.showWebview() diff --git a/extension/src/test/suite/plots/util.ts b/extension/src/test/suite/plots/util.ts index 5079aeec75..29765707b5 100644 --- a/extension/src/test/suite/plots/util.ts +++ b/extension/src/test/suite/plots/util.ts @@ -100,6 +100,7 @@ export const buildPlots = async ( data, errorsModel, experiments, + experimentsModel, messageSpy, mockGetModifiedTime, mockPlotsDiff,