diff --git a/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.tsx b/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.tsx index 369baf8308..4bba3fc799 100644 --- a/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.tsx +++ b/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.tsx @@ -49,9 +49,9 @@ const ApiTokenForm: React.FC = ({ { 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 = diff --git a/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.test.tsx b/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.test.tsx new file mode 100644 index 0000000000..39b8f65849 --- /dev/null +++ b/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.test.tsx @@ -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(); + + 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(); + + 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(); + }); +}); diff --git a/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.tsx b/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.tsx index b7a0c53e06..54f72f0261 100644 --- a/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.tsx +++ b/src/component/admin/apiToken/ApiTokenForm/SelectProjectInput/SelectProjectInput.tsx @@ -23,7 +23,7 @@ const ALL_PROJECTS = '*'; // Fix for shadow under Autocomplete - match with Select input const CustomPaper = ({ ...props }) => ; -interface ISelectProjectInputProps { +export interface ISelectProjectInputProps { disabled?: boolean; options: IAutocompleteBoxOption[]; defaultValue: string[]; @@ -100,6 +100,7 @@ export const SelectProjectInput: VFC = ({ label="Projects" placeholder="Select one or more projects" onFocus={onFocus} + data-testid="select-input" /> ); @@ -108,6 +109,7 @@ export const SelectProjectInput: VFC = ({ { const [username, setUsername] = useState(''); const [type, setType] = useState('CLIENT'); const [projects, setProjects] = useState(['*']); + const [memorizedProjects, setMemorizedProjects] = + useState(projects); const [environment, setEnvironment] = useState(); const [errors, setErrors] = useState< Partial> @@ -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); } }; diff --git a/src/index.tsx b/src/index.tsx index 651340d586..f6d992092b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -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( - - - - - - - - - + + + + + + diff --git a/src/themes/MainThemeProvider.tsx b/src/themes/MainThemeProvider.tsx new file mode 100644 index 0000000000..c23e4763e5 --- /dev/null +++ b/src/themes/MainThemeProvider.tsx @@ -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 }) => ( + + + + {children} + + +); diff --git a/src/utils/testRenderer.tsx b/src/utils/testRenderer.tsx index 3609aba746..3e2add6003 100644 --- a/src/utils/testRenderer.tsx +++ b/src/utils/testRenderer.tsx @@ -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 ( new Map() }}> - - {children} - + + {children} + ); };