Skip to content

Commit

Permalink
fix: workspace actions
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksii Orel <[email protected]>
  • Loading branch information
olexii4 committed Oct 22, 2024
1 parent ee53494 commit 1fdba55
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ export class RecentItemWorkspaceActions extends React.PureComponent<Props> {
context={context}
isPlain
menuAppendTo={menuAppendTo}
onAction={async () => {
// no-op
}}
position="right"
toggle="kebab-toggle"
workspace={item.workspace}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,14 @@ export class WorkspaceActionsDropdown extends React.PureComponent<Props, State>
};
}

private get hasKebabToggle(): boolean {
return this.props.toggle === 'kebab-toggle';
}

private buildToggle(): React.ReactElement {
const { isDisabled = false, toggle, workspace } = this.props;
const { isDisabled = false, workspace } = this.props;

if (toggle === 'kebab-toggle') {
if (this.hasKebabToggle) {
return (
<KebabToggle
aria-label="Actions"
Expand Down Expand Up @@ -157,14 +161,21 @@ export class WorkspaceActionsDropdown extends React.PureComponent<Props, State>
);
};

return [
const items = [
getItem(WorkspaceAction.OPEN_IDE, isTerminating),
getItem(WorkspaceAction.START_DEBUG_AND_OPEN_LOGS, isTerminating || isStopped === false),
getItem(WorkspaceAction.START_IN_BACKGROUND, isTerminating || isStopped === false),
getItem(WorkspaceAction.START_DEBUG_AND_OPEN_LOGS, isTerminating || !isStopped),
getItem(WorkspaceAction.START_IN_BACKGROUND, isTerminating || !isStopped),
getItem(WorkspaceAction.RESTART_WORKSPACE, isTerminating || isStopped),
getItem(WorkspaceAction.STOP_WORKSPACE, isTerminating || isStopped),
getItem(WorkspaceAction.DELETE_WORKSPACE, isTerminating),
];

// The 'Workspace Details' action is available only with kebab-toggle because this actions widget is used on the workspace details page without kebab-toggle
if (this.hasKebabToggle) {
items.push(getItem(WorkspaceAction.WORKSPACE_DETAILS, false));
}

return items;
}

render(): React.ReactElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { Location } from 'react-router-dom';

import { WorkspaceActionsDeleteConfirmation } from '@/contexts/WorkspaceActions/DeleteConfirmation';
import { lazyInject } from '@/inversify.config';
import { buildIdeLoaderLocation, toHref } from '@/services/helpers/location';
import {
buildIdeLoaderLocation,
buildWorkspaceDetailsLocation,
toHref,
} from '@/services/helpers/location';
import { LoaderTab, WorkspaceAction } from '@/services/helpers/types';
import { TabManager } from '@/services/tabManager';
import { Workspace } from '@/services/workspace-adapter';
Expand Down Expand Up @@ -67,6 +71,14 @@ class WorkspaceActionsProvider extends React.Component<Props, State> {
this.tabManager.open(link);
}

/**
* replace the current tab with the given location
*/
private replaceLocation(location: Location): void {
const link = toHref(location);
this.tabManager.replace(link);
}

private async deleteWorkspace(workspace: Workspace): Promise<void> {
if (this.deleting.has(workspace.uid)) {
console.warn(`Workspace "${workspace.name}" is being deleted.`);
Expand Down Expand Up @@ -114,6 +126,10 @@ class WorkspaceActionsProvider extends React.Component<Props, State> {
this.handleLocation(buildIdeLoaderLocation(workspace));
break;
}
case WorkspaceAction.WORKSPACE_DETAILS: {
this.replaceLocation(buildWorkspaceDetailsLocation(workspace));
break;
}
case WorkspaceAction.START_DEBUG_AND_OPEN_LOGS: {
await this.props.startWorkspace(workspace, {
'debug-workspace-start': true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import {
WorkspaceActionsConsumer,
} from '@/contexts/WorkspaceActions';
import WorkspaceActionsProvider from '@/contexts/WorkspaceActions/Provider';
import { container } from '@/inversify.config';
import getComponentRenderer, { screen } from '@/services/__mocks__/getComponentRenderer';
import { WorkspaceAction } from '@/services/helpers/types';
import { TabManager } from '@/services/tabManager';
import { AppThunk } from '@/store';
import { DevWorkspaceBuilder } from '@/store/__mocks__/devWorkspaceBuilder';
import { FakeStoreBuilder } from '@/store/__mocks__/storeBuilder';
Expand Down Expand Up @@ -67,6 +69,10 @@ const wantDelete = ['1234', '5678'] as WantDelete;

const mockHandleAction: jest.Mock = jest.fn();

const mockWindowReplace = jest.fn();

const mockTabManagerOpen = jest.fn();

describe('WorkspaceActionsProvider', () => {
let store: Store;
let user: UserEvent;
Expand All @@ -92,6 +98,18 @@ describe('WorkspaceActionsProvider', () => {
jest.useFakeTimers();

user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });

class MockTabManager extends TabManager {
public open(url: string): void {
mockTabManagerOpen(url);
}
public replace(url: string): void {
mockWindowReplace(url);
}
}

container.snapshot();
container.rebind(TabManager).to(MockTabManager).inSingletonScope();
});

afterEach(() => {
Expand Down Expand Up @@ -138,8 +156,6 @@ describe('WorkspaceActionsProvider', () => {
});

describe('handle actions', () => {
window.open = jest.fn();

describe('delete workspace', () => {
test('succeeded (with debouncing)', async () => {
console.warn = jest.fn();
Expand Down Expand Up @@ -209,10 +225,24 @@ describe('WorkspaceActionsProvider', () => {
// make sure all timers are executed
await jest.advanceTimersByTimeAsync(1000);

expect(window.open).toHaveBeenCalledTimes(1);
expect(window.open).toHaveBeenCalledWith(
expect.stringContaining('/ide/user-che/wksp-1234'),
expect.stringContaining('/ide/user-che/wksp-1234'),
expect(mockTabManagerOpen).toHaveBeenCalledTimes(1);
expect(mockTabManagerOpen).toHaveBeenCalledWith('http://localhost/#/ide/user-che/wksp-1234');
});

test('open workspace details', async () => {
renderComponent(store, WorkspaceAction.WORKSPACE_DETAILS, '1234');

// get and click start button
const handleActionBtn = screen.getByTestId('test-component-handle-action');

await user.click(handleActionBtn);

// make sure all timers are executed
await jest.advanceTimersByTimeAsync(1000);

expect(mockWindowReplace).toHaveBeenCalledTimes(1);
expect(mockWindowReplace).toHaveBeenCalledWith(
'http://localhost/#/workspace/user-che/wksp-1234',
);
});

Expand All @@ -235,9 +265,8 @@ describe('WorkspaceActionsProvider', () => {
{ 'debug-workspace-start': true },
);

expect(window.open).toHaveBeenCalledWith(
expect.stringContaining('/ide/user-che/wksp-5678?tab=Logs'),
expect.stringContaining('/ide/user-che/wksp-5678?tab=Logs'),
expect(mockTabManagerOpen).toHaveBeenCalledWith(
'http://localhost/#/ide/user-che/wksp-5678?tab=Logs',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

import { Location } from 'react-router-dom';

import { toHref } from '@/services/helpers/location';
import { buildWorkspaceDetailsLocation, toHref } from '@/services/helpers/location';
import { constructWorkspace } from '@/services/workspace-adapter';
import { DevWorkspaceBuilder } from '@/store/__mocks__/devWorkspaceBuilder';

describe('location', () => {
test('toHref', () => {
Expand All @@ -25,4 +27,12 @@ describe('location', () => {
};
expect(toHref(location)).toEqual(`${window.location.origin}/#/foo?bar=baz`);
});

test('buildWorkspaceDetailsLocation', () => {
const devWorkspaceBuilder = new DevWorkspaceBuilder()
.withName('wksp')
.withNamespace('che-user');
const workspace = constructWorkspace(devWorkspaceBuilder.build());
expect(buildWorkspaceDetailsLocation(workspace).pathname).toEqual(`/workspace/che-user/wksp`);
});
});
8 changes: 8 additions & 0 deletions packages/dashboard-frontend/src/services/helpers/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export function buildWorkspacesLocation(): Location {
return _buildLocationObject(ROUTE.WORKSPACES);
}

export function buildWorkspaceDetailsLocation(workspace: Workspace): Location {
const path = ROUTE.WORKSPACE_DETAILS.replace(':namespace', workspace.namespace).replace(
':workspaceName',
workspace.name,
);
return _buildLocationObject(path);
}

export function buildUserPreferencesLocation(tab?: UserPreferencesTab): Location {
let pathAndQuery: string;
if (!tab) {
Expand Down
1 change: 1 addition & 0 deletions packages/dashboard-frontend/src/services/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export enum WorkspaceAction {
START_DEBUG_AND_OPEN_LOGS = 'Open in Debug mode',
START_IN_BACKGROUND = 'Start in background',
STOP_WORKSPACE = 'Stop Workspace',
WORKSPACE_DETAILS = 'Workspace Details',
}

export enum UserPreferencesTab {
Expand Down

0 comments on commit 1fdba55

Please sign in to comment.