Skip to content

Commit

Permalink
Merge pull request #1302 from kubeshop/andreiv1992/test/more-tests-ar…
Browse files Browse the repository at this point in the history
…ound-projects

Tests for create project & save resource
  • Loading branch information
andreivinaga authored Feb 8, 2022
2 parents aec0290 + d0be844 commit 673dd33
Show file tree
Hide file tree
Showing 33 changed files with 553 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/monokle-ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: monokle-ui-tests
on:
push:
branches:
- olelensmar/test/playwright-baseline
- andreiv1992/test/more-tests-around-projects

workflow_dispatch:

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@ Don't hesitate to provide any feedback you might have to help us prioritize and
[Code of Conduct](https://github.com/kubeshop/.github/blob/main/CODE_OF_CONDUCT.md)
- Fork/Clone the repo and make sure you can run it as shown above
- Check out the [Development](docs/development.md) document for how to build and run Monokle from its source
- Check out the [Testing](docs/testing.md) document for how to build and run Monokle tests
- Check out the [Architecture](docs/architecture.md) document to get a high-level understanding of how Monokle works
- Check out the Roadmap above and open [issues](https://github.com/kubeshop/monokle/issues) here on GitHub
46 changes: 46 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Testing

Monokle tests are written with Playwright: https://playwright.dev/docs/api/class-electron

## Run tests

First create a build to run the tests

`npm run electron:build`

To run specific tests

`npm run ui-test -- tests/<filename>.test.ts`

To run all the tests

`npm run ui-test`

## Write & extend tests

To start writing tests first create a build(tests are run against the build which will be published), any changes made to the source code, adding identifiers, changing logic will need a new build for those changes to be in the tests

`npm run electron:build`

The `startApp()` function should be called and that will start a new monokle instance with the `automation` flag set. More examples of tests can be found in the `./tests` folder
The `automation` flag is used to change some handlers which cannot be automated by playwright since they are open by the specific OS's

Models should contain most of the logic, we can think of models a mirror for some components in the app with some logic and identifiers for certain elements. Having most of the logic in the models can help with reusing most of the logic we have around tests and more lightweight tests.

## Overriding OS actions

To change handlers such as `dialog.showOpenDialogSync` the function `getChannelName('channel-name')` should be called when creating a new handler on `ipcRenderer`, this will create different handler just for automation.
To add a new handler in automation something like this is required:
```
const name = 'some-value';
const chanel = getChannelName('channel-name', true);
await electronApp.evaluate(({ ipcMain }, params) => {
ipcMain.handle(params.chanel, () => {
return [params.name];
});
}, { chanel, name });
```

Examples:
- [override in electron](src/components/atoms/FileExplorer/FileExplorer.tsx)
- [override in tests](tests/models/projectsDropdown.ts)
5 changes: 5 additions & 0 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {AnyPlugin} from '@models/plugin';
import {AnyExtension, DownloadPluginResult, DownloadTemplatePackResult, DownloadTemplateResult, UpdateExtensionsResult} from '@models/extension';
import {KustomizeCommandOptions} from '@redux/thunks/previewKustomization';
import { convertRecentFilesToRecentProjects, setProjectsRootFolder } from './utils';
import {StartupFlags} from '@utils/startupFlag';

Object.assign(console, ElectronLog.functions);

Expand Down Expand Up @@ -314,6 +315,10 @@ export const createWindow = (givenPath?: string) => {
});

dispatch(setAppRehydrating(true));
if (process.argv.includes(StartupFlags.AUTOMATION)) {
win.webContents.send('set-automation');
}

dispatch(setUserDirs({
homeDir: userHomeDir,
tempDir: userTempDir,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
"release:patch": "standard-version --release-as patch",
"release:minor": "standard-version --release-as minor",
"release:major": "standard-version --release-as major",
"ui-test": "xvfb-maybe npx playwright test",
"ui-test": "xvfb-maybe npx playwright test --workers=1",
"custom-script": "npx ts-node src/scripts/index.ts"
},
"build": {
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {PlaywrightTestConfig} from '@playwright/test';

const config: PlaywrightTestConfig = {
testDir: './tests',
timeout: 60000,
timeout: 80000,
expect: {
toMatchSnapshot: {threshold: 0.2},
},
Expand Down
13 changes: 12 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {useFileExplorer} from '@hooks/useFileExplorer';
import {setMainProcessEnv} from '@utils/env';
import {getFileStats} from '@utils/files';
import {useWindowSize} from '@utils/hooks';
import {StartupFlag} from '@utils/startupFlag';

import AppContext from './AppContext';

Expand Down Expand Up @@ -153,6 +154,17 @@ const App = () => {
};
}, [onOpenProjectFolderFromMainThread]);

const onSetAutomation = useCallback(() => {
StartupFlag.getInstance().automationFlag = true;
}, []);

useEffect(() => {
ipcRenderer.on('set-automation', onSetAutomation);
return () => {
ipcRenderer.removeListener('set-automation', onSetAutomation);
};
}, [onSetAutomation]);

const onSetMainProcessEnv = useCallback((_: any, args: any) => {
setMainProcessEnv(args.mainProcessEnv);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -241,7 +253,6 @@ const App = () => {
{isCreateProjectModalVisible && <CreateProjectModal />}
{isLocalResourceDiffModalVisible && <LocalResourceDiffModal />}
{isNewResourceWizardVisible && <NewResourceWizard />}
{isNewResourceWizardVisible && <NewResourceWizard />}
{isQuickSearchActionsVisible && <QuickSearchActions />}
{isRenameEntityModalVisible && <RenameEntityModal />}
{isRenameResourceModalVisible && <RenameResourceModal />}
Expand Down
3 changes: 2 additions & 1 deletion src/components/atoms/FileExplorer/FileExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {useEffect} from 'react';

import log from 'loglevel';

import {getChannelName} from '@utils/ipc';
import {FileExplorerOptions} from './FileExplorerOptions';

export type FileExplorerProps = {
Expand All @@ -29,7 +30,7 @@ const FileExplorer = (props: FileExplorerProps) => {
}
});
} else {
ipcRenderer.invoke('select-file', options).then(files => {
ipcRenderer.invoke(getChannelName('select-file'), options).then(files => {
if (files) {
onSelect(files);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ const SaveResourceToFileFolderModal: React.FC = () => {

return (
<Modal
className="save-resource"
title={resourcesIds.length === 1 ? 'Save resource' : `Save resources (${resourcesIds.length})`}
visible={isVisible}
onCancel={() => dispatch(closeSaveResourcesToFileFolderModal())}
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/ActionsPane/ActionsPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ const ActionsPane = (props: {contentHeight: string}) => {

{isSelectedResourceUnsaved() && (
<Tooltip mouseEnterDelay={TOOLTIP_DELAY} title={SaveUnsavedResourceTooltip}>
<S.SaveButton type="primary" size="small" onClick={onSaveHandler}>
<S.SaveButton id="save-button" type="primary" size="small" onClick={onSaveHandler}>
Save
</S.SaveButton>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ const CreateProjectModal: React.FC = () => {
Back
</Button>
),
<Button key="submit" type="primary" disabled={!isSubmitEnabled} onClick={() => createProjectForm.submit()}>
<Button id="empty-project-save" key="submit" type="primary" disabled={!isSubmitEnabled} onClick={() => createProjectForm.submit()}>
{uiState.fromTemplate && formStep === FormSteps.STEP_ONE ? 'Next: Select a Template' : 'Create Project'}
</Button>,
]}
Expand Down
4 changes: 2 additions & 2 deletions src/components/organisms/FileTreePane/FileTreePane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,8 @@ const FileTreePane = () => {
) : tree ? (
<S.TreeContainer>
<S.RootFolderText>
<div>{fileMap[ROOT_FILE_ENTRY].filePath}</div>
<div>{Object.values(fileMap).filter(f => !f.children).length} files</div>
<div id="file-explorer-project-name">{fileMap[ROOT_FILE_ENTRY].filePath}</div>
<div id="file-explorer-count">{Object.values(fileMap).filter(f => !f.children).length} files</div>
</S.RootFolderText>
<S.TreeDirectoryTree
// height is needed to enable Tree's virtual scroll ToDo: Do constants based on the hights of app title and pane title, or get height of parent.
Expand Down
1 change: 1 addition & 0 deletions src/components/organisms/NavigatorPane/NavigatorPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const NavPane: React.FC = () => {
</MonoPaneTitle>
<S.TitleBarRightButtons>
<S.PlusButton
id="create-resource-button"
$disabled={!isFolderOpen || isInPreviewMode}
$highlighted={highlightedItems.createResource}
className={highlightedItems.createResource ? 'animated-highlight' : ''}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ const NewResourceWizard = () => {
icon: <InfoCircleOutlined />,
}}
>
<Input maxLength={63} placeholder="Enter resource name" />
<Input id="resource-name-input" maxLength={63} placeholder="Enter resource name" />
</Form.Item>

<Form.Item
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/PageHeader/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const PageHeader = () => {

<S.Header noborder="true">
<S.Row noborder="true">
<S.Logo onClick={showGetStartingPage} src={MonokleKubeshopLogo} alt="Monokle" />
<S.Logo id="monokle-logo-header" onClick={showGetStartingPage} src={MonokleKubeshopLogo} alt="Monokle" />

<S.ProjectClusterSelectionContainer>
<ProjectSelection />
Expand Down
5 changes: 3 additions & 2 deletions src/components/organisms/PageHeader/ProjectSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,12 @@ const ProjectSelection = () => {
<S.ProjectMenu>
<S.ProjectsMenuContainer>
<Tooltip mouseEnterDelay={TOOLTIP_DELAY} title={SearchProjectTooltip} placement="bottomRight">
<S.Search placeholder="Search" value={searchText} onChange={handleProjectSearch} />
<S.Search id='project-search' placeholder="Search" value={searchText} onChange={handleProjectSearch} />
</Tooltip>
<S.ProjectsMenuActionsContainer>
<Tooltip mouseEnterDelay={TOOLTIP_DELAY} title={NewProjectFromFolderTooltip} placement="bottomRight">
<S.ProjectFolderOpenOutlined
id="open-new-project"
onClick={() => {
setIsDropdownMenuVisible(false);
openFileExplorer();
Expand Down Expand Up @@ -240,7 +241,7 @@ const ProjectSelection = () => {
}

return (
<S.ProjectContainer>
<S.ProjectContainer id="projects-dropdown-container">
<Dropdown
arrow
disabled={previewLoader.isLoading || isInPreviewMode}
Expand Down
1 change: 1 addition & 0 deletions src/components/organisms/PaneManager/PaneManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ const PaneManager = () => {
placement="right"
>
<MenuButton
id="file-explorer"
isSelected={leftMenuSelection === 'file-explorer'}
isActive={Boolean(activeProject) && leftActive}
shouldWatchSelectedPath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ const StartProjectPane = () => {
<div>
<S.ActionTitle>How would you like to begin?</S.ActionTitle>
<div style={{display: 'flex'}}>
<S.ActionContainer onClick={handleOpenFolderExplorer}>
<S.ActionContainer id="select-existing-folder" onClick={handleOpenFolderExplorer}>
<S.IconWrapper>
<S.FolderOpenOutlined src={SelectAnExistingFolder} />
</S.IconWrapper>
<S.ActionText>Select an existing folder</S.ActionText>
</S.ActionContainer>
<S.ActionContainer onClick={() => handleCreateProject(false)}>
<S.ActionContainer id="create-empty-project" onClick={() => handleCreateProject(false)}>
<S.IconWrapper>
<S.FolderAddOutlined src={SelectAnEmptyProject} />
</S.IconWrapper>
<S.ActionText>Create an empty project</S.ActionText>
</S.ActionContainer>
<S.ActionContainer onClick={() => handleCreateProject(true)}>
<S.ActionContainer id="start-from-template" onClick={() => handleCreateProject(true)}>
<S.IconWrapper>
<S.FormatPainterOutlined src={StartFromTemplate} />
</S.IconWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ function K8sResourceSectionEmptyDisplay() {
<>
{activeResources.length === 0 ? (
<StyledContainer>
<StyledTitle>Get started:</StyledTitle>
<StyledLink onClick={() => handleClick(HighlightItems.CREATE_RESOURCE)}>Create a resource</StyledLink>
<StyledLink onClick={() => handleClick(HighlightItems.BROWSE_TEMPLATES)}>Browse templates</StyledLink>
<StyledTitle id="get-started-title">Get started:</StyledTitle>
<StyledLink id="create-resource-link" onClick={() => handleClick(HighlightItems.CREATE_RESOURCE)}>Create a resource</StyledLink>
<StyledLink id="browse-template-link" onClick={() => handleClick(HighlightItems.BROWSE_TEMPLATES)}>Browse templates</StyledLink>
{isKubeConfigPathValid ? (
<StyledLink onClick={() => handleClick(HighlightItems.CONNECT_TO_CLUSTER)}>Connect to a cluster</StyledLink>
<StyledLink id="connect-to-cluster-link" onClick={() => handleClick(HighlightItems.CONNECT_TO_CLUSTER)}>Connect to a cluster</StyledLink>
) : (
<StyledLink onClick={handleClusterConfigure}>Configure a cluster</StyledLink>
<StyledLink id="configure-cluster-link" onClick={handleClusterConfigure}>Configure a cluster</StyledLink>
)}
</StyledContainer>
) : (
Expand Down
12 changes: 12 additions & 0 deletions src/utils/ipc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {StartupFlag, StartupFlags} from './startupFlag';

export function getChannelName(
name: string,
hasAutomationFlag = StartupFlag.getInstance().hasAutomationFlag,
) {
if (hasAutomationFlag) {
return `${name}-${StartupFlags.AUTOMATION}`;
}

return name;
}
29 changes: 29 additions & 0 deletions src/utils/startupFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export enum StartupFlags {
AUTOMATION = 'automation',
}

export class StartupFlag {

private static _instance: StartupFlag;

private _automationFlag = false;

// eslint-disable-next-line no-useless-constructor,no-empty-function
private constructor() {}

public static getInstance(): StartupFlag {
if (!StartupFlag._instance) {
StartupFlag._instance = new StartupFlag();
}

return StartupFlag._instance;
}

get hasAutomationFlag(): boolean {
return this._automationFlag;
}

set automationFlag(value: boolean) {
this._automationFlag = value;
}
}
Loading

0 comments on commit 673dd33

Please sign in to comment.