mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-22 01:16:07 +02:00
refactor: additional unit tests for multi-project tokens - (#863)
* refactor: add theme to test renderer * feat: add tests to token list * projects list for multi-project tokens * refactor: api token form available projects map * update variable name * fix: restore selected project on token type change * fix: select project input code formatting * fix: improve code formatting after review
This commit is contained in:
parent
edf69d171d
commit
9dd88b488f
@ -49,9 +49,9 @@ const ApiTokenForm: React.FC<IApiTokenFormProps> = ({
|
|||||||
{ key: 'ADMIN', label: 'Admin', title: 'Admin API token' },
|
{ key: 'ADMIN', label: 'Admin', title: 'Admin API token' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const selectableProjects = availableProjects.map(i => ({
|
const selectableProjects = availableProjects.map(project => ({
|
||||||
value: i.id,
|
value: project.id,
|
||||||
label: i.name,
|
label: project.name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const selectableEnvs =
|
const selectableEnvs =
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen, within } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { render } from 'utils/testRenderer';
|
||||||
|
import {
|
||||||
|
ISelectProjectInputProps,
|
||||||
|
SelectProjectInput,
|
||||||
|
} from './SelectProjectInput';
|
||||||
|
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const onFocus = jest.fn();
|
||||||
|
|
||||||
|
const mockProps: ISelectProjectInputProps = {
|
||||||
|
options: [
|
||||||
|
{ label: 'Project1', value: 'project1' },
|
||||||
|
{ label: 'Project2', value: 'project2' },
|
||||||
|
],
|
||||||
|
defaultValue: ['*'],
|
||||||
|
onChange,
|
||||||
|
onFocus,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('SelectProjectInput', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
onChange.mockClear();
|
||||||
|
onFocus.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with default state', () => {
|
||||||
|
render(<SelectProjectInput {...mockProps} />);
|
||||||
|
|
||||||
|
const checkbox = screen.getByLabelText(
|
||||||
|
/all current and future projects/i
|
||||||
|
);
|
||||||
|
expect(checkbox).toBeChecked();
|
||||||
|
|
||||||
|
const selectInputContainer = screen.getByTestId('select-input');
|
||||||
|
const input = within(selectInputContainer).getByRole('textbox');
|
||||||
|
expect(input).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can toggle "ALL" checkbox', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
render(<SelectProjectInput {...mockProps} />);
|
||||||
|
|
||||||
|
await user.click(screen.getByTestId('select-all-projects'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByLabelText(/all current and future projects/i)
|
||||||
|
).not.toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Projects')).toBeEnabled();
|
||||||
|
|
||||||
|
await user.click(screen.getByTestId('select-all-projects'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByLabelText(/all current and future projects/i)
|
||||||
|
).toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Projects')).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
@ -23,7 +23,7 @@ const ALL_PROJECTS = '*';
|
|||||||
// Fix for shadow under Autocomplete - match with Select input
|
// Fix for shadow under Autocomplete - match with Select input
|
||||||
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
||||||
|
|
||||||
interface ISelectProjectInputProps {
|
export interface ISelectProjectInputProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
options: IAutocompleteBoxOption[];
|
options: IAutocompleteBoxOption[];
|
||||||
defaultValue: string[];
|
defaultValue: string[];
|
||||||
@ -100,6 +100,7 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
|||||||
label="Projects"
|
label="Projects"
|
||||||
placeholder="Select one or more projects"
|
placeholder="Select one or more projects"
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
|
data-testid="select-input"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
|||||||
<Box sx={{ mt: 1, mb: 0.25, ml: 1.5 }}>
|
<Box sx={{ mt: 1, mb: 0.25, ml: 1.5 }}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
data-testid="select-all-projects"
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={disabled || isWildcardSelected}
|
checked={disabled || isWildcardSelected}
|
||||||
|
@ -11,6 +11,8 @@ export const useApiTokenForm = () => {
|
|||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [type, setType] = useState('CLIENT');
|
const [type, setType] = useState('CLIENT');
|
||||||
const [projects, setProjects] = useState<string[]>(['*']);
|
const [projects, setProjects] = useState<string[]>(['*']);
|
||||||
|
const [memorizedProjects, setMemorizedProjects] =
|
||||||
|
useState<string[]>(projects);
|
||||||
const [environment, setEnvironment] = useState<string>();
|
const [environment, setEnvironment] = useState<string>();
|
||||||
const [errors, setErrors] = useState<
|
const [errors, setErrors] = useState<
|
||||||
Partial<Record<ApiTokenFormErrorType, string>>
|
Partial<Record<ApiTokenFormErrorType, string>>
|
||||||
@ -23,10 +25,12 @@ export const useApiTokenForm = () => {
|
|||||||
const setTokenType = (value: string) => {
|
const setTokenType = (value: string) => {
|
||||||
if (value === 'ADMIN') {
|
if (value === 'ADMIN') {
|
||||||
setType(value);
|
setType(value);
|
||||||
|
setMemorizedProjects(projects);
|
||||||
setProjects(['*']);
|
setProjects(['*']);
|
||||||
setEnvironment('*');
|
setEnvironment('*');
|
||||||
} else {
|
} else {
|
||||||
setType(value);
|
setType(value);
|
||||||
|
setProjects(memorizedProjects);
|
||||||
setEnvironment(initialEnvironment);
|
setEnvironment(initialEnvironment);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,11 +3,9 @@ import 'themes/app.css';
|
|||||||
|
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Route, BrowserRouter as Router } from 'react-router-dom';
|
import { Route, BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { ThemeProvider, CssBaseline } from '@material-ui/core';
|
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { StylesProvider } from '@material-ui/core/styles';
|
import { MainThemeProvider } from 'themes/MainThemeProvider';
|
||||||
import mainTheme from 'themes/mainTheme';
|
|
||||||
import { App } from 'component/App';
|
import { App } from 'component/App';
|
||||||
import { ScrollTop } from 'component/common/ScrollTop/ScrollTop';
|
import { ScrollTop } from 'component/common/ScrollTop/ScrollTop';
|
||||||
import AccessProvider from 'component/providers/AccessProvider/AccessProvider';
|
import AccessProvider from 'component/providers/AccessProvider/AccessProvider';
|
||||||
@ -20,15 +18,12 @@ ReactDOM.render(
|
|||||||
<UIProvider>
|
<UIProvider>
|
||||||
<AccessProvider>
|
<AccessProvider>
|
||||||
<Router basename={`${getBasePath()}`}>
|
<Router basename={`${getBasePath()}`}>
|
||||||
<ThemeProvider theme={mainTheme}>
|
<MainThemeProvider>
|
||||||
<StylesProvider injectFirst>
|
|
||||||
<FeedbackCESProvider>
|
<FeedbackCESProvider>
|
||||||
<CssBaseline />
|
|
||||||
<ScrollTop />
|
<ScrollTop />
|
||||||
<Route path="/" component={App} />
|
<Route path="/" component={App} />
|
||||||
</FeedbackCESProvider>
|
</FeedbackCESProvider>
|
||||||
</StylesProvider>
|
</MainThemeProvider>
|
||||||
</ThemeProvider>
|
|
||||||
</Router>
|
</Router>
|
||||||
</AccessProvider>
|
</AccessProvider>
|
||||||
</UIProvider>
|
</UIProvider>
|
||||||
|
12
frontend/src/themes/MainThemeProvider.tsx
Normal file
12
frontend/src/themes/MainThemeProvider.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import { CssBaseline, StylesProvider, ThemeProvider } from '@material-ui/core';
|
||||||
|
import mainTheme from './mainTheme';
|
||||||
|
|
||||||
|
export const MainThemeProvider: FC = ({ children }) => (
|
||||||
|
<ThemeProvider theme={mainTheme}>
|
||||||
|
<StylesProvider injectFirst>
|
||||||
|
<CssBaseline />
|
||||||
|
{children}
|
||||||
|
</StylesProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
@ -1,9 +1,8 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { render as rtlRender, RenderOptions } from '@testing-library/react';
|
import { render as rtlRender, RenderOptions } from '@testing-library/react';
|
||||||
import { SWRConfig } from 'swr';
|
import { SWRConfig } from 'swr';
|
||||||
import { ThemeProvider } from '@material-ui/core/styles';
|
import { MainThemeProvider } from 'themes/MainThemeProvider';
|
||||||
import theme from 'themes/mainTheme';
|
|
||||||
import React from 'react';
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
export const render = (
|
export const render = (
|
||||||
ui: JSX.Element,
|
ui: JSX.Element,
|
||||||
@ -20,12 +19,12 @@ export const render = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const Wrapper: React.FC = ({ children }) => {
|
const Wrapper: FC = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<SWRConfig value={{ provider: () => new Map() }}>
|
<SWRConfig value={{ provider: () => new Map() }}>
|
||||||
<BrowserRouter>
|
<MainThemeProvider>
|
||||||
<ThemeProvider theme={theme}>{children}</ThemeProvider>
|
<Router>{children}</Router>
|
||||||
</BrowserRouter>
|
</MainThemeProvider>
|
||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user