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' },
|
||||
];
|
||||
|
||||
const selectableProjects = availableProjects.map(i => ({
|
||||
value: i.id,
|
||||
label: i.name,
|
||||
const selectableProjects = availableProjects.map(project => ({
|
||||
value: project.id,
|
||||
label: project.name,
|
||||
}));
|
||||
|
||||
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
|
||||
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
||||
|
||||
interface ISelectProjectInputProps {
|
||||
export interface ISelectProjectInputProps {
|
||||
disabled?: boolean;
|
||||
options: IAutocompleteBoxOption[];
|
||||
defaultValue: string[];
|
||||
@ -100,6 +100,7 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
||||
label="Projects"
|
||||
placeholder="Select one or more projects"
|
||||
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 }}>
|
||||
<FormControlLabel
|
||||
disabled={disabled}
|
||||
data-testid="select-all-projects"
|
||||
control={
|
||||
<Checkbox
|
||||
checked={disabled || isWildcardSelected}
|
||||
|
@ -11,6 +11,8 @@ export const useApiTokenForm = () => {
|
||||
const [username, setUsername] = useState('');
|
||||
const [type, setType] = useState('CLIENT');
|
||||
const [projects, setProjects] = useState<string[]>(['*']);
|
||||
const [memorizedProjects, setMemorizedProjects] =
|
||||
useState<string[]>(projects);
|
||||
const [environment, setEnvironment] = useState<string>();
|
||||
const [errors, setErrors] = useState<
|
||||
Partial<Record<ApiTokenFormErrorType, string>>
|
||||
@ -23,10 +25,12 @@ export const useApiTokenForm = () => {
|
||||
const setTokenType = (value: string) => {
|
||||
if (value === 'ADMIN') {
|
||||
setType(value);
|
||||
setMemorizedProjects(projects);
|
||||
setProjects(['*']);
|
||||
setEnvironment('*');
|
||||
} else {
|
||||
setType(value);
|
||||
setProjects(memorizedProjects);
|
||||
setEnvironment(initialEnvironment);
|
||||
}
|
||||
};
|
||||
|
@ -3,11 +3,9 @@ import 'themes/app.css';
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, BrowserRouter as Router } from 'react-router-dom';
|
||||
import { ThemeProvider, CssBaseline } from '@material-ui/core';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { StylesProvider } from '@material-ui/core/styles';
|
||||
import mainTheme from 'themes/mainTheme';
|
||||
import { MainThemeProvider } from 'themes/MainThemeProvider';
|
||||
import { App } from 'component/App';
|
||||
import { ScrollTop } from 'component/common/ScrollTop/ScrollTop';
|
||||
import AccessProvider from 'component/providers/AccessProvider/AccessProvider';
|
||||
@ -20,15 +18,12 @@ ReactDOM.render(
|
||||
<UIProvider>
|
||||
<AccessProvider>
|
||||
<Router basename={`${getBasePath()}`}>
|
||||
<ThemeProvider theme={mainTheme}>
|
||||
<StylesProvider injectFirst>
|
||||
<FeedbackCESProvider>
|
||||
<CssBaseline />
|
||||
<ScrollTop />
|
||||
<Route path="/" component={App} />
|
||||
</FeedbackCESProvider>
|
||||
</StylesProvider>
|
||||
</ThemeProvider>
|
||||
<MainThemeProvider>
|
||||
<FeedbackCESProvider>
|
||||
<ScrollTop />
|
||||
<Route path="/" component={App} />
|
||||
</FeedbackCESProvider>
|
||||
</MainThemeProvider>
|
||||
</Router>
|
||||
</AccessProvider>
|
||||
</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 { SWRConfig } from 'swr';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import theme from 'themes/mainTheme';
|
||||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { MainThemeProvider } from 'themes/MainThemeProvider';
|
||||
|
||||
export const render = (
|
||||
ui: JSX.Element,
|
||||
@ -20,12 +19,12 @@ export const render = (
|
||||
});
|
||||
};
|
||||
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
const Wrapper: FC = ({ children }) => {
|
||||
return (
|
||||
<SWRConfig value={{ provider: () => new Map() }}>
|
||||
<BrowserRouter>
|
||||
<ThemeProvider theme={theme}>{children}</ThemeProvider>
|
||||
</BrowserRouter>
|
||||
<MainThemeProvider>
|
||||
<Router>{children}</Router>
|
||||
</MainThemeProvider>
|
||||
</SWRConfig>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user