Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Context Menu to Runs view (ET-232) #9480

Merged
merged 172 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
172 commits
Select commit Hold shift + click to select a range
df7f885
feat: initial implementation of flat run actions
keita-determined May 14, 2024
7d96896
fix: minor changes
keita-determined May 22, 2024
a1db2c3
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
a49676d
feat: return `archived` for FlatRun
keita-determined May 24, 2024
738f7ce
chore: minor changes
keita-determined May 24, 2024
0afcf85
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
575a959
chore: better type
keita-determined May 28, 2024
1e59bf4
test: `FlatRunActionButton` test
keita-determined May 28, 2024
6919563
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
faf66e2
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
e796b5c
RunActionDropdown
johnkim-det Jun 3, 2024
e91c6a7
Merge branch 'main' into ET-232
johnkim-det Jun 3, 2024
6e778d7
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 3, 2024
98fc615
feat: initial implementation of flat run actions
keita-determined May 14, 2024
ad60e62
fix: minor changes
keita-determined May 22, 2024
8b8a20c
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
a6399d5
feat: return `archived` for FlatRun
keita-determined May 24, 2024
77c3343
chore: minor changes
keita-determined May 24, 2024
2f7cb66
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
43a1c21
chore: better type
keita-determined May 28, 2024
beb85d6
test: `FlatRunActionButton` test
keita-determined May 28, 2024
8019e3b
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
d538dfc
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
597e09a
chore: feedback
keita-determined Jun 3, 2024
eb7e4de
feat: basic move action
keita-determined Jun 3, 2024
c5c8ad7
feat: move warning modal
keita-determined Jun 4, 2024
ff665a1
Archive Unarchive Kill Delete
johnkim-det Jun 4, 2024
e49f74d
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 4, 2024
6b0e315
merge errors fixes and fmt
johnkim-det Jun 4, 2024
d92bc89
move modal
johnkim-det Jun 4, 2024
f67ac6d
cleanup
johnkim-det Jun 4, 2024
daf0c4a
feat: initial implementation of flat run actions
keita-determined May 14, 2024
ebe8dc2
fix: minor changes
keita-determined May 22, 2024
cb7699d
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
4ed42b4
feat: return `archived` for FlatRun
keita-determined May 24, 2024
5b0b681
chore: minor changes
keita-determined May 24, 2024
430fe3c
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
b2aa066
chore: better type
keita-determined May 28, 2024
fe955d4
test: `FlatRunActionButton` test
keita-determined May 28, 2024
f3c4c65
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
5b9be44
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
f685f88
chore: feedback
keita-determined Jun 3, 2024
a5a8f0b
feat: basic move action
keita-determined Jun 3, 2024
dcac64b
feat: move warning modal
keita-determined Jun 4, 2024
310f32f
fix: feedback
keita-determined Jun 5, 2024
fe7254f
fix: unarchive behavior
keita-determined Jun 5, 2024
79d4201
fix: minor fixes
keita-determined Jun 5, 2024
ced6c5a
tests
johnkim-det Jun 6, 2024
82c870e
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 6, 2024
9fb83b4
fmt
johnkim-det Jun 6, 2024
eb2fd3a
feat: initial implementation of flat run actions
keita-determined May 14, 2024
25c155b
fix: minor changes
keita-determined May 22, 2024
54cfd9c
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
85ab36e
feat: return `archived` for FlatRun
keita-determined May 24, 2024
4bd19c0
chore: minor changes
keita-determined May 24, 2024
272a144
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
85b3ed9
chore: better type
keita-determined May 28, 2024
46c97b4
test: `FlatRunActionButton` test
keita-determined May 28, 2024
cf536ca
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
b5755cf
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
d92a990
chore: feedback
keita-determined Jun 3, 2024
dd80170
feat: basic move action
keita-determined Jun 3, 2024
39be2e6
feat: move warning modal
keita-determined Jun 4, 2024
988ca88
fix: feedback
keita-determined Jun 5, 2024
8e5679d
fix: unarchive behavior
keita-determined Jun 5, 2024
ff27abc
fix: minor fixes
keita-determined Jun 5, 2024
f32488b
fix: archive/unarchive error text fix
keita-determined Jun 7, 2024
4e1a325
test: `FlatRunMoveModal`
keita-determined Jun 7, 2024
8acd0a1
test: ignore `bindings.py` for codecov
keita-determined Jun 7, 2024
63fd58d
test: backend test
keita-determined Jun 7, 2024
ce5817b
fixes
johnkim-det Jun 10, 2024
e267811
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 10, 2024
6ac7f12
merge error
johnkim-det Jun 10, 2024
be3d716
fmt
johnkim-det Jun 10, 2024
7517152
merge error
johnkim-det Jun 10, 2024
0549bf2
feat: initial implementation of flat run actions
keita-determined May 14, 2024
ade2a4f
fix: minor changes
keita-determined May 22, 2024
c2a2203
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
0c6dc9c
feat: return `archived` for FlatRun
keita-determined May 24, 2024
ed89b13
chore: minor changes
keita-determined May 24, 2024
9358bee
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
75c0da6
chore: better type
keita-determined May 28, 2024
d4b1cfe
test: `FlatRunActionButton` test
keita-determined May 28, 2024
3fe92db
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
fe4454d
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
9d428db
chore: feedback
keita-determined Jun 3, 2024
745089e
feat: basic move action
keita-determined Jun 3, 2024
eb917e1
feat: move warning modal
keita-determined Jun 4, 2024
be9a3e6
fix: feedback
keita-determined Jun 5, 2024
e01184a
fix: unarchive behavior
keita-determined Jun 5, 2024
786301b
fix: minor fixes
keita-determined Jun 5, 2024
1e529fa
fix: archive/unarchive error text fix
keita-determined Jun 7, 2024
fa0c8e1
test: `FlatRunMoveModal`
keita-determined Jun 7, 2024
6f1b9a0
test: ignore `bindings.py` for codecov
keita-determined Jun 7, 2024
75d317a
test: backend test
keita-determined Jun 7, 2024
494463a
chore: backend feedback
keita-determined Jun 10, 2024
0138a8e
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 10, 2024
1c0e897
update bindings after merge
johnkim-det Jun 10, 2024
7cf82f0
merge error
johnkim-det Jun 10, 2024
87cd592
fmt
johnkim-det Jun 10, 2024
4193a1a
test: test
keita-determined Jun 10, 2024
0cdae7c
fix: minor fixes
keita-determined Jun 10, 2024
ec9eee7
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 11, 2024
922f149
feat: initial implementation of flat run actions
keita-determined May 14, 2024
a0c410d
fix: minor changes
keita-determined May 22, 2024
8f700fb
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
73c65eb
feat: return `archived` for FlatRun
keita-determined May 24, 2024
9aaca82
chore: minor changes
keita-determined May 24, 2024
7ce852e
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
d1d2a32
chore: better type
keita-determined May 28, 2024
a60ef4d
test: `FlatRunActionButton` test
keita-determined May 28, 2024
b2cf2e5
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
ad47ffc
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
85b0d50
chore: feedback
keita-determined Jun 3, 2024
4d9e9b7
feat: basic move action
keita-determined Jun 3, 2024
1f7ab64
feat: move warning modal
keita-determined Jun 4, 2024
28445ee
fix: feedback
keita-determined Jun 5, 2024
778edf1
fix: unarchive behavior
keita-determined Jun 5, 2024
647f255
fix: minor fixes
keita-determined Jun 5, 2024
2f3cf74
fix: archive/unarchive error text fix
keita-determined Jun 7, 2024
971fdca
test: `FlatRunMoveModal`
keita-determined Jun 7, 2024
8af9436
test: ignore `bindings.py` for codecov
keita-determined Jun 7, 2024
fe06de1
test: backend test
keita-determined Jun 7, 2024
3847cb1
chore: backend feedback
keita-determined Jun 10, 2024
bc949cd
test: test
keita-determined Jun 10, 2024
37e3ca9
fix: minor fixes
keita-determined Jun 10, 2024
5fcd926
fix: feedback
keita-determined Jun 11, 2024
2d55b75
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 12, 2024
f510235
cleanup
johnkim-det Jun 12, 2024
b5bc28e
feat: initial implementation of flat run actions
keita-determined May 14, 2024
f00cdce
fix: minor changes
keita-determined May 22, 2024
614161f
feat: add RBAC permissions for flatRuns
keita-determined May 23, 2024
54abde4
feat: return `archived` for FlatRun
keita-determined May 24, 2024
6ad02d5
chore: minor changes
keita-determined May 24, 2024
c6b503c
test: `canActionFlatRun` test cases
keita-determined May 24, 2024
9518246
chore: better type
keita-determined May 28, 2024
e282d37
test: `FlatRunActionButton` test
keita-determined May 28, 2024
dbd8cbd
fix: fix run `archived` logic in postgres
keita-determined May 30, 2024
e3362cf
chore: run bulk action interstitial component (#9390)
ashtonG Jun 3, 2024
400548b
chore: feedback
keita-determined Jun 3, 2024
b4439e4
feat: basic move action
keita-determined Jun 3, 2024
cf184af
feat: move warning modal
keita-determined Jun 4, 2024
9d76210
fix: feedback
keita-determined Jun 5, 2024
e4b5037
fix: unarchive behavior
keita-determined Jun 5, 2024
41d7ccc
fix: minor fixes
keita-determined Jun 5, 2024
8c484de
fix: archive/unarchive error text fix
keita-determined Jun 7, 2024
dcd70a5
test: `FlatRunMoveModal`
keita-determined Jun 7, 2024
b210889
test: ignore `bindings.py` for codecov
keita-determined Jun 7, 2024
bfd65c0
test: backend test
keita-determined Jun 7, 2024
8b95fc6
test: test
keita-determined Jun 10, 2024
5a81aec
fix: minor fixes
keita-determined Jun 10, 2024
99c83d7
fix: feedback
keita-determined Jun 11, 2024
9cefef9
fix: minor fixes
keita-determined Jun 12, 2024
fa36e22
test: more test
keita-determined Jun 12, 2024
ef322d8
fix: minor fixes
keita-determined Jun 13, 2024
da72616
test: more tests
keita-determined Jun 14, 2024
0eb768b
fix typos
johnkim-det Jun 14, 2024
17b3396
Merge branch 'feature/actions-flat-run-table' into ET-232
johnkim-det Jun 17, 2024
172f40b
merge fix and fmt
johnkim-det Jun 17, 2024
41343fc
merge fixes
johnkim-det Jun 17, 2024
1416980
test fixes
johnkim-det Jun 18, 2024
87e5fd0
Merge branch 'main' into ET-232
johnkim-det Jun 28, 2024
d044c06
fmt
johnkim-det Jun 28, 2024
c2ecdc2
merge fix
johnkim-det Jun 28, 2024
94861e1
merge fix
johnkim-det Jun 28, 2024
e263699
add ExperimentActionDropdown tests
johnkim-det Jun 28, 2024
e2983d2
Merge branch 'main' into ET-232
johnkim-det Jun 28, 2024
344d709
tests
johnkim-det Jun 28, 2024
c526bb2
refactor
johnkim-det Jun 28, 2024
45bf3e9
tests
johnkim-det Jun 28, 2024
ea5ec22
lint
johnkim-det Jun 28, 2024
711ef85
feedback
johnkim-det Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { GridCell, GridCellKind } from '@glideapps/glide-data-grid';

import { ProjectExperiment } from 'types';

export const cell: GridCell = {
allowOverlay: false,
copyData: 'core-api-stage-3',
cursor: 'pointer',
data: {
kind: 'link-cell',
link: {
href: '/experiments/7261',
title: 'core-api-stage-3',
unmanaged: false,
},
navigateOn: 'click',
underlineOffset: 6,
},
kind: GridCellKind.Custom,
readonly: true,
};

export const experiment: ProjectExperiment = {
archived: false,
checkpoints: 0,
checkpointSize: 0,
description: 'Continuation of trial 49300, experiment 7229',
duration: 12,
endTime: '2024-06-27T22:35:00.745298Z',
forkedFrom: 7229,
hyperparameters: {
increment_by: {
type: 'const',
val: 1,
},
irrelevant1: {
type: 'const',
val: 1,
},
irrelevant2: {
type: 'const',
val: 1,
},
},
id: 7261,
jobId: '742ae9dc-e712-4348-9b15-a1b9f652d6d5',
labels: [],
name: 'core-api-stage-3',
notes: '',
numTrials: 1,
parentArchived: false,
progress: 0,
projectId: 1,
projectName: 'Uncategorized',
projectOwnerId: 1,
resourcePool: 'aux-pool',
searcherType: 'single',
startTime: '2024-06-27T22:34:49.194301Z',
state: 'CANCELED',
trialIds: [],
unmanaged: false,
userId: 1288,
workspaceId: 1,
workspaceName: 'Uncategorized',
};
263 changes: 263 additions & 0 deletions webui/react/src/components/ExperimentActionDropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import UIProvider, { DefaultTheme } from 'hew/Theme';
import { ConfirmationProvider } from 'hew/useConfirm';

import { handlePath } from 'routes/utils';
import {
archiveExperiment,
cancelExperiment,
deleteExperiment,
killExperiment,
patchExperiment,
pauseExperiment,
unarchiveExperiment,
} from 'services/api';
import { RunState } from 'types';

import ExperimentActionDropdown, { Action } from './ExperimentActionDropdown';
import { cell, experiment } from './ExperimentActionDropdown.test.mock';

const user = userEvent.setup();

const mockNavigatorClipboard = () => {
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
value: {
readText: vi.fn(),
writeText: vi.fn(),
},
writable: true,
});
};

vi.mock('routes/utils', () => ({
handlePath: vi.fn(),
serverAddress: () => 'http://localhost',
}));

vi.mock('services/api', () => ({
archiveExperiment: vi.fn(),
cancelExperiment: vi.fn(),
deleteExperiment: vi.fn(),
getWorkspaces: vi.fn(() => Promise.resolve({ workspaces: [] })),
killExperiment: vi.fn(),
patchExperiment: vi.fn(),
pauseExperiment: vi.fn(),
unarchiveExperiment: vi.fn(),
}));

const mocks = vi.hoisted(() => {
return {
canCreateExperiment: vi.fn(),
canDeleteExperiment: vi.fn(),
canModifyExperiment: vi.fn(),
canModifyExperimentMetadata: vi.fn(),
canMoveExperiment: vi.fn(),
canViewExperimentArtifacts: vi.fn(),
};
});

vi.mock('hooks/usePermissions', () => {
const usePermissions = vi.fn(() => {
return {
canCreateExperiment: mocks.canCreateExperiment,
canDeleteExperiment: mocks.canDeleteExperiment,
canModifyExperiment: mocks.canModifyExperiment,
canModifyExperimentMetadata: mocks.canModifyExperimentMetadata,
canMoveExperiment: mocks.canMoveExperiment,
canViewExperimentArtifacts: mocks.canViewExperimentArtifacts,
};
});
return {
default: usePermissions,
};
});

const setup = (link?: string, state?: RunState, archived?: boolean) => {
const onComplete = vi.fn();
const onVisibleChange = vi.fn();
render(
<UIProvider theme={DefaultTheme.Light}>
<ConfirmationProvider>
<ExperimentActionDropdown
cell={cell}
experiment={{
...experiment,
archived: archived === undefined ? experiment.archived : archived,
state: state === undefined ? experiment.state : state,
}}
isContextMenu
link={link}
makeOpen
onComplete={onComplete}
onVisibleChange={onVisibleChange}>
<div />
</ExperimentActionDropdown>
</ConfirmationProvider>
</UIProvider>,
);
return {
onComplete,
onVisibleChange,
};
};

describe('ExperimentActionDropdown', () => {
it('should provide Copy Data option', async () => {
setup();
mockNavigatorClipboard();
await user.click(screen.getByText(Action.Copy));
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(cell.copyData);
});

it('should provide Link option', async () => {
const link = 'https://www.google.com/';
setup(link);
await user.click(screen.getByText(Action.NewTab));
const tabClick = vi.mocked(handlePath).mock.calls[0];
expect(tabClick[0]).toMatchObject({ type: 'click' });
expect(tabClick[1]).toMatchObject({
path: link,
popout: 'tab',
});
await user.click(screen.getByText(Action.NewWindow));
const windowClick = vi.mocked(handlePath).mock.calls[1];
expect(windowClick[0]).toMatchObject({ type: 'click' });
expect(windowClick[1]).toMatchObject({
path: link,
popout: 'window',
});
});

it('should provide Delete option', async () => {
mocks.canDeleteExperiment.mockImplementation(() => true);
setup();
await user.click(screen.getByText(Action.Delete));
await user.click(screen.getByRole('button', { name: Action.Delete }));
expect(vi.mocked(deleteExperiment)).toBeCalled();
});

it('should hide Delete option without permissions', () => {
mocks.canDeleteExperiment.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.Delete)).not.toBeInTheDocument();
});

it('should provide Kill option', async () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup(undefined, RunState.Paused, undefined);
await user.click(screen.getByText(Action.Kill));
await user.click(screen.getByRole('button', { name: Action.Kill }));
expect(vi.mocked(killExperiment)).toBeCalled();
});

it('should hide Kill option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup(undefined, RunState.Paused, undefined);
expect(screen.queryByText(Action.Kill)).not.toBeInTheDocument();
});

it('should provide Archive option', async () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup();
await user.click(screen.getByText(Action.Archive));
expect(vi.mocked(archiveExperiment)).toBeCalled();
});

it('should hide Archive option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.Archive)).not.toBeInTheDocument();
});

it('should provide Unarchive option', async () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup(undefined, undefined, true);
await user.click(screen.getByText(Action.Unarchive));
expect(vi.mocked(unarchiveExperiment)).toBeCalled();
});

it('should hide Unarchive option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup(undefined, undefined, true);
expect(screen.queryByText(Action.Unarchive)).not.toBeInTheDocument();
});

it('should provide Move option', () => {
mocks.canMoveExperiment.mockImplementation(() => true);
setup();
expect(screen.getByText(Action.Move)).toBeInTheDocument();
});

it('should hide Move option without permissions', () => {
mocks.canMoveExperiment.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.Move)).not.toBeInTheDocument();
});

it('should provide Edit option', async () => {
mocks.canModifyExperimentMetadata.mockImplementation(() => true);
setup();
await user.click(screen.getByText(Action.Edit));
await user.type(screen.getByRole('textbox', { name: 'Name' }), 'edit');
await user.click(screen.getByText('Save'));
expect(vi.mocked(patchExperiment)).toBeCalled();
});

it('should hide Edit option without permissions', () => {
mocks.canModifyExperimentMetadata.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.Edit)).not.toBeInTheDocument();
});

it('should provide Pause option', async () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup(undefined, RunState.Running);
await user.click(screen.getByText(Action.Pause));
expect(vi.mocked(pauseExperiment)).toBeCalled();
});

it('should hide Pause option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup(undefined, RunState.Running);
expect(screen.queryByText(Action.Pause)).not.toBeInTheDocument();
});

it('should provide Cancel option', async () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup(undefined, RunState.Running);
await user.click(screen.getByText(Action.Cancel));
expect(vi.mocked(cancelExperiment)).toBeCalled();
});

it('should hide Cancel option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup(undefined, RunState.Running);
expect(screen.queryByText(Action.Cancel)).not.toBeInTheDocument();
});

it('should provide Retain Logs option', () => {
mocks.canModifyExperiment.mockImplementation(() => true);
setup();
expect(screen.queryByText(Action.RetainLogs)).toBeInTheDocument();
});

it('should hide Retain Logs option without permissions', () => {
mocks.canModifyExperiment.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.RetainLogs)).not.toBeInTheDocument();
});

it('should provide Tensor Board option', () => {
mocks.canViewExperimentArtifacts.mockImplementation(() => true);
setup();
expect(screen.getByText(Action.OpenTensorBoard)).toBeInTheDocument();
});

it('should hide Tensor Board option without permissions', () => {
mocks.canViewExperimentArtifacts.mockImplementation(() => false);
setup();
expect(screen.queryByText(Action.OpenTensorBoard)).not.toBeInTheDocument();
});
});
Loading
Loading