Skip to content

Commit

Permalink
Implement autostart for VNet in Connect (#40900)
Browse files Browse the repository at this point in the history
* Refactor tests that depended on workspace service state

Those tests explicitly set the entire WorkspacesService state and would
have to be modified each time we added something to the state.

There's no reason for them to know about WorkspacesService state, so this
commit refactors them out of that knowledge.

* Add useAppState

* Automatically start VNet

* subscribeWithSelector: Return unsubscribe function

The unsubscribe function will be needed in order to comply with
useSyncExternalStore API in the next commit.

* Add isInitialized to WorkspacesService

* Add useImmutableStore

* Wait for workspace init before VNet autostart

* Rename useAppState to usePersistedState

* Rename useImmutableStore to useStoreSelector
  • Loading branch information
ravicious authored Apr 26, 2024
1 parent 4db79ca commit e12c54b
Show file tree
Hide file tree
Showing 12 changed files with 630 additions and 179 deletions.
132 changes: 27 additions & 105 deletions web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,13 @@
*/

import { fireEvent, render, screen } from 'design/utils/testing';
import React from 'react';

import { TabHost } from 'teleterm/ui/TabHost/TabHost';
import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
import {
Document,
DocumentsService,
WorkspacesService,
} from 'teleterm/ui/services/workspacesService';
import { KeyboardShortcutsService } from 'teleterm/ui/services/keyboardShortcuts';
import {
MainProcessClient,
RuntimeSettings,
TabContextMenuOptions,
} from 'teleterm/mainProcess/types';
import { ClustersService } from 'teleterm/ui/services/clusters';
import AppContext from 'teleterm/ui/appContext';
import { Document } from 'teleterm/ui/services/workspacesService';
import { TabContextMenuOptions } from 'teleterm/mainProcess/types';
import { makeDocumentCluster } from 'teleterm/ui/services/workspacesService/documentsService/testHelpers';

import { getEmptyPendingAccessRequest } from '../services/workspacesService/accessRequestsService';
import { MockAppContext } from 'teleterm/ui/fixtures/mocks';

// TODO(ravicious): Remove the mock once a separate entry point for e-teleterm is created.
//
Expand All @@ -62,97 +49,32 @@ function getMockDocuments(): Document[] {
];
}

function getTestSetup({ documents }: { documents: Document[] }) {
const keyboardShortcutsService: Partial<KeyboardShortcutsService> = {
subscribeToEvents() {},
unsubscribeFromEvents() {},
// @ts-expect-error we don't provide entire config
getShortcutsConfig() {
return {
closeTab: 'Command-W',
newTab: 'Command-T',
openSearchBar: 'Command-K',
openConnections: 'Command-P',
openClusters: 'Command-E',
openProfiles: 'Command-I',
};
},
};

const mainProcessClient: Partial<MainProcessClient> = {
openTabContextMenu: jest.fn(),
getRuntimeSettings: () => ({}) as RuntimeSettings,
};
const rootClusterUri = '/clusters/test_uri';

const docsService: Partial<DocumentsService> = {
getDocuments(): Document[] {
return documents;
},
getActive() {
return documents[0];
},
close: jest.fn(),
open: jest.fn(),
add: jest.fn(),
closeOthers: jest.fn(),
closeToRight: jest.fn(),
openNewTerminal: jest.fn(),
swapPosition: jest.fn(),
createClusterDocument: jest.fn(),
duplicatePtyAndActivate: jest.fn(),
};

const clustersService: Partial<ClustersService> = {
subscribe: jest.fn(),
unsubscribe: jest.fn(),
findRootClusterByResource: jest.fn(),
findCluster: jest.fn(),
findGateway: jest.fn(),
};
function getTestSetup({ documents }: { documents: Document[] }) {
const appContext = new MockAppContext();
jest.spyOn(appContext.mainProcessClient, 'openTabContextMenu');

appContext.workspacesService.setState(draft => {
draft.rootClusterUri = rootClusterUri;
draft.workspaces[rootClusterUri] = {
documents,
location: documents[0]?.uri,
localClusterUri: rootClusterUri,
accessRequests: undefined,
};
});

const workspacesService: Partial<WorkspacesService> = {
isDocumentActive(documentUri: string) {
return documentUri === documents[0].uri;
},
getRootClusterUri() {
return '/clusters/test_uri';
},
getWorkspaces() {
return {};
},
getActiveWorkspace() {
return {
accessRequests: {
assumed: {},
isBarCollapsed: false,
pending: getEmptyPendingAccessRequest(),
},
documents,
location: undefined,
localClusterUri: '/clusters/test_uri',
};
},
// @ts-expect-error - using mocks
getActiveWorkspaceDocumentService() {
return docsService;
},
useState: jest.fn(),
state: {
workspaces: {},
rootClusterUri: '/clusters/test_uri',
},
};
const docsService =
appContext.workspacesService.getActiveWorkspaceDocumentService();

const appContext: AppContext = {
// @ts-expect-error - using mocks
keyboardShortcutsService,
// @ts-expect-error - using mocks
mainProcessClient,
// @ts-expect-error - using mocks
clustersService,
// @ts-expect-error - using mocks
workspacesService,
};
jest.spyOn(docsService, 'add');
jest.spyOn(docsService, 'open');
jest.spyOn(docsService, 'close');
jest.spyOn(docsService, 'swapPosition');
jest.spyOn(docsService, 'closeOthers');
jest.spyOn(docsService, 'closeToRight');
jest.spyOn(docsService, 'duplicatePtyAndActivate');

const utils = render(
<MockAppContextProvider appContext={appContext}>
Expand All @@ -163,7 +85,7 @@ function getTestSetup({ documents }: { documents: Document[] }) {
return {
...utils,
docsService,
mainProcessClient,
mainProcessClient: appContext.mainProcessClient,
};
}

Expand Down
96 changes: 32 additions & 64 deletions web/packages/teleterm/src/ui/TabHost/useTabShortcuts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, { PropsWithChildren } from 'react';
import { PropsWithChildren } from 'react';
import renderHook from 'design/utils/renderHook';

import { useTabShortcuts } from 'teleterm/ui/TabHost/useTabShortcuts';
import {
Document,
DocumentsService,
} from 'teleterm/ui/services/workspacesService/documentsService';
import { Document } from 'teleterm/ui/services/workspacesService/documentsService';
import {
KeyboardShortcutEvent,
KeyboardShortcutEventSubscriber,
KeyboardShortcutsService,
} from 'teleterm/ui/services/keyboardShortcuts';
import AppContextProvider from 'teleterm/ui/appContextProvider';
import { WorkspacesService } from 'teleterm/ui/services/workspacesService';
import AppContext from 'teleterm/ui/appContext';

import { makeDocumentCluster } from 'teleterm/ui/services/workspacesService/documentsService/testHelpers';

import { getEmptyPendingAccessRequest } from '../services/workspacesService/accessRequestsService';
import { MockAppContext } from 'teleterm/ui/fixtures/mocks';

function getMockDocuments(): Document[] {
return [
Expand Down Expand Up @@ -95,69 +87,45 @@ function getMockDocuments(): Document[] {
];
}

const rootClusterUri = '/clusters/test_uri';

function getTestSetup({ documents }: { documents: Document[] }) {
const appContext = new MockAppContext();

let eventEmitter: KeyboardShortcutEventSubscriber;
const keyboardShortcutsService: Partial<KeyboardShortcutsService> = {
subscribeToEvents(subscriber: KeyboardShortcutEventSubscriber) {
jest
.spyOn(appContext.keyboardShortcutsService, 'subscribeToEvents')
.mockImplementation((subscriber: KeyboardShortcutEventSubscriber) => {
eventEmitter = subscriber;
},
unsubscribeFromEvents() {
});
jest
.spyOn(appContext.keyboardShortcutsService, 'unsubscribeFromEvents')
.mockImplementation(() => {
eventEmitter = null;
},
};
});

// @ts-expect-error - using mocks
const docsService: DocumentsService = {
getDocuments(): Document[] {
return documents;
},
getActive() {
return documents[0];
},
close: jest.fn(),
open: jest.fn(),
add: jest.fn(),
closeOthers: jest.fn(),
closeToRight: jest.fn(),
openNewTerminal: jest.fn(),
swapPosition: jest.fn(),
duplicatePtyAndActivate: jest.fn(),
};
appContext.workspacesService.setState(draft => {
draft.rootClusterUri = rootClusterUri;
draft.workspaces[rootClusterUri] = {
documents,
location: documents[0]?.uri,
localClusterUri: rootClusterUri,
accessRequests: undefined,
};
});

const workspacesService: Partial<WorkspacesService> = {
getActiveWorkspaceDocumentService() {
return docsService;
},
getActiveWorkspace() {
return {
accessRequests: {
assumed: {},
isBarCollapsed: false,
pending: getEmptyPendingAccessRequest(),
},
localClusterUri: '/clusters/test_uri',
documents: [],
location: '/docs/1',
};
},
useState: jest.fn(),
state: {
workspaces: {},
rootClusterUri: '/clusters/test_uri',
},
};
const docsService =
appContext.workspacesService.getActiveWorkspaceDocumentService();

jest.spyOn(docsService, 'open');
jest.spyOn(docsService, 'close');
jest.spyOn(docsService, 'add');

const appContext: AppContext = {
// @ts-expect-error - using mocks
keyboardShortcutsService,
// @ts-expect-error - using mocks
workspacesService,
};
renderHook(
() =>
useTabShortcuts({
documentsService: docsService,
localClusterUri: workspacesService.getActiveWorkspace().localClusterUri,
localClusterUri: rootClusterUri,
}),
{
wrapper: (props: PropsWithChildren) => (
Expand All @@ -171,7 +139,7 @@ function getTestSetup({ documents }: { documents: Document[] }) {
return {
emitKeyboardShortcutEvent: eventEmitter,
docsService,
keyboardShortcutsService,
keyboardShortcutsService: appContext.keyboardShortcutsService,
};
}

Expand Down
Loading

0 comments on commit e12c54b

Please sign in to comment.