mirror of
https://github.com/ansible/awx.git
synced 2026-01-20 06:01:25 -03:30
Merge pull request #8761 from marshmalien/setting-github-edit-forms
Add all github category setting edit forms
Reviewed-by: John Hill <johill@redhat.com>
https://github.com/unlikelyzero
This commit is contained in:
commit
5bb59246af
@ -86,6 +86,7 @@ function ActivityStreamDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/activity_stream/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -78,6 +78,7 @@ function AzureADDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/azure/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -6,6 +6,8 @@ import { PageSection, Card } from '@patternfly/react-core';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
import GitHubDetail from './GitHubDetail';
|
||||
import GitHubEdit from './GitHubEdit';
|
||||
import GitHubOrgEdit from './GitHubOrgEdit';
|
||||
import GitHubTeamEdit from './GitHubTeamEdit';
|
||||
|
||||
function GitHub({ i18n }) {
|
||||
const baseURL = '/settings/github';
|
||||
@ -29,9 +31,15 @@ function GitHub({ i18n }) {
|
||||
<Route path={`${baseURL}/:category/details`}>
|
||||
<GitHubDetail />
|
||||
</Route>
|
||||
<Route path={`${baseURL}/:category/edit`}>
|
||||
<Route path={`${baseURL}/default/edit`}>
|
||||
<GitHubEdit />
|
||||
</Route>
|
||||
<Route path={`${baseURL}/organization/edit`}>
|
||||
<GitHubOrgEdit />
|
||||
</Route>
|
||||
<Route path={`${baseURL}/team/edit`}>
|
||||
<GitHubTeamEdit />
|
||||
</Route>
|
||||
<Route key="not-found" path={`${baseURL}/*`}>
|
||||
<ContentError isNotFound>
|
||||
<Link to={`${baseURL}/default/details`}>
|
||||
|
||||
@ -5,33 +5,94 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../testUtils/enzymeHelpers';
|
||||
import GitHub from './GitHub';
|
||||
import { SettingsAPI } from '../../../api';
|
||||
import { SettingsProvider } from '../../../contexts/Settings';
|
||||
import mockAllOptions from '../shared/data.allSettingOptions.json';
|
||||
import GitHub from './GitHub';
|
||||
|
||||
jest.mock('../../../api/models/Settings');
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {},
|
||||
});
|
||||
|
||||
describe('<GitHub />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.readCategory.mockResolvedValueOnce({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_CALLBACK_URL:
|
||||
'https://towerhost/sso/complete/github/',
|
||||
SOCIAL_AUTH_GITHUB_KEY: 'mock github key',
|
||||
SOCIAL_AUTH_GITHUB_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: null,
|
||||
},
|
||||
});
|
||||
SettingsAPI.readCategory.mockResolvedValueOnce({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL:
|
||||
'https://towerhost/sso/complete/github-org/',
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: null,
|
||||
},
|
||||
});
|
||||
SettingsAPI.readCategory.mockResolvedValueOnce({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL:
|
||||
'https://towerhost/sso/complete/github-team/',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: 'team_id',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should render github details', async () => {
|
||||
test('should render github default details', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/github/'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<GitHub />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHub />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('GitHubDetail').length).toBe(1);
|
||||
expect(wrapper.find('Detail[label="GitHub OAuth2 Key"]').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should redirect to github organization category details', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/github/organization'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHub />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('GitHubDetail').length).toBe(1);
|
||||
expect(
|
||||
wrapper.find('Detail[label="GitHub Organization OAuth2 Key"]').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('should render github edit', async () => {
|
||||
@ -39,9 +100,14 @@ describe('<GitHub />', () => {
|
||||
initialEntries: ['/settings/github/default/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<GitHub />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHub />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('GitHubEdit').length).toBe(1);
|
||||
@ -52,9 +118,14 @@ describe('<GitHub />', () => {
|
||||
initialEntries: ['/settings/github/foo/bar'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<GitHub />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHub />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -114,6 +114,7 @@ function GitHubDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to={`${baseURL}/${category}/edit`}
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -1,25 +1,141 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
import { CardBody, CardActionsRow } from '../../../../components/Card';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
import { CardBody } from '../../../../components/Card';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { FormColumnLayout } from '../../../../components/FormLayout';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import { RevertAllAlert, RevertFormActionGroup } from '../../shared';
|
||||
import {
|
||||
EncryptedField,
|
||||
InputField,
|
||||
ObjectField,
|
||||
} from '../../shared/SharedFields';
|
||||
import { formatJson } from '../../shared/settingUtils';
|
||||
import useModal from '../../../../util/useModal';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
|
||||
function GitHubEdit() {
|
||||
const history = useHistory();
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const { PUT: options } = useSettings();
|
||||
|
||||
const { isLoading, error, request: fetchGithub, result: github } = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('github');
|
||||
const mergedData = {};
|
||||
Object.keys(data).forEach(key => {
|
||||
if (!options[key]) {
|
||||
return;
|
||||
}
|
||||
mergedData[key] = options[key];
|
||||
mergedData[key].value = data[key];
|
||||
});
|
||||
return mergedData;
|
||||
}, [options]),
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGithub();
|
||||
}, [fetchGithub]);
|
||||
|
||||
const { error: submitError, request: submitForm } = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/github/details');
|
||||
},
|
||||
[history]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: formatJson(
|
||||
form.SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP
|
||||
),
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: formatJson(form.SOCIAL_AUTH_GITHUB_TEAM_MAP),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push('/settings/github/details');
|
||||
};
|
||||
|
||||
const initialValues = fields =>
|
||||
Object.keys(fields).reduce((acc, key) => {
|
||||
if (fields[key].type === 'list' || fields[key].type === 'nested object') {
|
||||
const emptyDefault = fields[key].type === 'list' ? '[]' : '{}';
|
||||
acc[key] = fields[key].value
|
||||
? JSON.stringify(fields[key].value, null, 2)
|
||||
: emptyDefault;
|
||||
} else {
|
||||
acc[key] = fields[key].value ?? '';
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
function GitHubEdit({ i18n }) {
|
||||
return (
|
||||
<CardBody>
|
||||
{i18n._(t`Edit form coming soon :)`)}
|
||||
<CardActionsRow>
|
||||
<Button
|
||||
aria-label={i18n._(t`Cancel`)}
|
||||
component={Link}
|
||||
to="/settings/github/details"
|
||||
>
|
||||
{i18n._(t`Cancel`)}
|
||||
</Button>
|
||||
</CardActionsRow>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && github && (
|
||||
<Formik initialValues={initialValues(github)} onSubmit={handleSubmit}>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<InputField
|
||||
name="SOCIAL_AUTH_GITHUB_KEY"
|
||||
config={github.SOCIAL_AUTH_GITHUB_KEY}
|
||||
/>
|
||||
<EncryptedField
|
||||
name="SOCIAL_AUTH_GITHUB_SECRET"
|
||||
config={github.SOCIAL_AUTH_GITHUB_SECRET}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
onRevert={toggleModal}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<RevertAllAlert
|
||||
onClose={closeModal}
|
||||
onRevertAll={handleRevertAll}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
)}
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n()(GitHubEdit);
|
||||
export default GitHubEdit;
|
||||
|
||||
@ -1,16 +1,173 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import GitHubEdit from './GitHubEdit';
|
||||
|
||||
jest.mock('../../../../api/models/Settings');
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_CALLBACK_URL: 'https://foo/complete/github/',
|
||||
SOCIAL_AUTH_GITHUB_KEY: 'mock github key',
|
||||
SOCIAL_AUTH_GITHUB_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: {
|
||||
Default: {
|
||||
users: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('<GitHubEdit />', () => {
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
wrapper = mountWithContexts(<GitHubEdit />);
|
||||
});
|
||||
let history;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
history = createMemoryHistory({
|
||||
initialEntries: ['/settings/github/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubEdit />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('GitHubEdit').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should display expected form fields', async () => {
|
||||
expect(wrapper.find('FormGroup[label="GitHub OAuth2 Key"]').length).toBe(1);
|
||||
expect(wrapper.find('FormGroup[label="GitHub OAuth2 Secret"]').length).toBe(
|
||||
1
|
||||
);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub OAuth2 Organization Map"]').length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub OAuth2 Team Map"]').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('button[aria-label="Revert all to default"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(1);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('RevertAllAlert button[aria-label="Confirm revert all"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
.find(
|
||||
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_SECRET"] button[aria-label="Revert"]'
|
||||
)
|
||||
.invoke('onClick')();
|
||||
wrapper.find('input#SOCIAL_AUTH_GITHUB_KEY').simulate('change', {
|
||||
target: { value: 'new key', name: 'SOCIAL_AUTH_GITHUB_KEY' },
|
||||
});
|
||||
wrapper
|
||||
.find('CodeMirrorInput#SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||
.invoke('onChange')('{\n"Default":{\n"users":\nfalse\n}\n}');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_KEY: 'new key',
|
||||
SOCIAL_AUTH_GITHUB_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: {
|
||||
Default: {
|
||||
users: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should navigate to github default detail on successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/github/details');
|
||||
});
|
||||
|
||||
test('should navigate to github default detail when cancel is clicked', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/github/details');
|
||||
});
|
||||
|
||||
test('should display error message on unsuccessful submission', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
data: { detail: 'An error occurred' },
|
||||
},
|
||||
};
|
||||
SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error));
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should display ContentError on throw', async () => {
|
||||
SettingsAPI.readCategory.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error())
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubEdit />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,147 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
import { CardBody } from '../../../../components/Card';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { FormColumnLayout } from '../../../../components/FormLayout';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import { RevertAllAlert, RevertFormActionGroup } from '../../shared';
|
||||
import {
|
||||
EncryptedField,
|
||||
InputField,
|
||||
ObjectField,
|
||||
} from '../../shared/SharedFields';
|
||||
import { formatJson } from '../../shared/settingUtils';
|
||||
import useModal from '../../../../util/useModal';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
|
||||
function GitHubOrgEdit() {
|
||||
const history = useHistory();
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const { PUT: options } = useSettings();
|
||||
|
||||
const { isLoading, error, request: fetchGithub, result: github } = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('github-org');
|
||||
const mergedData = {};
|
||||
Object.keys(data).forEach(key => {
|
||||
if (!options[key]) {
|
||||
return;
|
||||
}
|
||||
mergedData[key] = options[key];
|
||||
mergedData[key].value = data[key];
|
||||
});
|
||||
return mergedData;
|
||||
}, [options]),
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGithub();
|
||||
}, [fetchGithub]);
|
||||
|
||||
const { error: submitError, request: submitForm } = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/github/organization/details');
|
||||
},
|
||||
[history]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: formatJson(
|
||||
form.SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP
|
||||
),
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: formatJson(
|
||||
form.SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push('/settings/github/organization/details');
|
||||
};
|
||||
|
||||
const initialValues = fields =>
|
||||
Object.keys(fields).reduce((acc, key) => {
|
||||
if (fields[key].type === 'list' || fields[key].type === 'nested object') {
|
||||
const emptyDefault = fields[key].type === 'list' ? '[]' : '{}';
|
||||
acc[key] = fields[key].value
|
||||
? JSON.stringify(fields[key].value, null, 2)
|
||||
: emptyDefault;
|
||||
} else {
|
||||
acc[key] = fields[key].value ?? '';
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && github && (
|
||||
<Formik initialValues={initialValues(github)} onSubmit={handleSubmit}>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<InputField
|
||||
name="SOCIAL_AUTH_GITHUB_ORG_KEY"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_KEY}
|
||||
/>
|
||||
<EncryptedField
|
||||
name="SOCIAL_AUTH_GITHUB_ORG_SECRET"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_SECRET}
|
||||
/>
|
||||
<InputField
|
||||
name="SOCIAL_AUTH_GITHUB_ORG_NAME"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_NAME}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
onRevert={toggleModal}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<RevertAllAlert
|
||||
onClose={closeModal}
|
||||
onRevertAll={handleRevertAll}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
)}
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default GitHubOrgEdit;
|
||||
@ -0,0 +1,186 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import GitHubOrgEdit from './GitHubOrgEdit';
|
||||
|
||||
jest.mock('../../../../api/models/Settings');
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL:
|
||||
'https://towerhost/sso/complete/github-org/',
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: null,
|
||||
},
|
||||
});
|
||||
|
||||
describe('<GitHubOrgEdit />', () => {
|
||||
let wrapper;
|
||||
let history;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
history = createMemoryHistory({
|
||||
initialEntries: ['/settings/github/organization/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubOrgEdit />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('GitHubOrgEdit').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should display expected form fields', async () => {
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Organization OAuth2 Key"]').length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Organization OAuth2 Secret"]')
|
||||
.length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Organization Name"]').length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find(
|
||||
'FormGroup[label="GitHub Organization OAuth2 Organization Map"]'
|
||||
).length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Organization OAuth2 Team Map"]')
|
||||
.length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('button[aria-label="Revert all to default"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(1);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('RevertAllAlert button[aria-label="Confirm revert all"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
.find(
|
||||
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_ORG_SECRET"] button[aria-label="Revert"]'
|
||||
)
|
||||
.invoke('onClick')();
|
||||
wrapper.find('input#SOCIAL_AUTH_GITHUB_ORG_NAME').simulate('change', {
|
||||
target: { value: 'new org', name: 'SOCIAL_AUTH_GITHUB_ORG_NAME' },
|
||||
});
|
||||
wrapper
|
||||
.find('CodeMirrorInput#SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP')
|
||||
.invoke('onChange')('{\n"Default":{\n"users":\nfalse\n}\n}');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: 'new org',
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: {
|
||||
Default: {
|
||||
users: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should navigate to github organization detail on successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/github/organization/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should navigate to github organization detail when cancel is clicked', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/github/organization/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should display error message on unsuccessful submission', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
data: { detail: 'An error occurred' },
|
||||
},
|
||||
};
|
||||
SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error));
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should display ContentError on throw', async () => {
|
||||
SettingsAPI.readCategory.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error())
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubOrgEdit />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
export { default } from './GitHubOrgEdit';
|
||||
@ -0,0 +1,147 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
import { CardBody } from '../../../../components/Card';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { FormColumnLayout } from '../../../../components/FormLayout';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import { RevertAllAlert, RevertFormActionGroup } from '../../shared';
|
||||
import {
|
||||
EncryptedField,
|
||||
InputField,
|
||||
ObjectField,
|
||||
} from '../../shared/SharedFields';
|
||||
import { formatJson } from '../../shared/settingUtils';
|
||||
import useModal from '../../../../util/useModal';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
|
||||
function GitHubTeamEdit() {
|
||||
const history = useHistory();
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const { PUT: options } = useSettings();
|
||||
|
||||
const { isLoading, error, request: fetchGithub, result: github } = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('github-team');
|
||||
const mergedData = {};
|
||||
Object.keys(data).forEach(key => {
|
||||
if (!options[key]) {
|
||||
return;
|
||||
}
|
||||
mergedData[key] = options[key];
|
||||
mergedData[key].value = data[key];
|
||||
});
|
||||
return mergedData;
|
||||
}, [options]),
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGithub();
|
||||
}, [fetchGithub]);
|
||||
|
||||
const { error: submitError, request: submitForm } = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/github/team/details');
|
||||
},
|
||||
[history]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: formatJson(
|
||||
form.SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP
|
||||
),
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: formatJson(
|
||||
form.SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push('/settings/github/team/details');
|
||||
};
|
||||
|
||||
const initialValues = fields =>
|
||||
Object.keys(fields).reduce((acc, key) => {
|
||||
if (fields[key].type === 'list' || fields[key].type === 'nested object') {
|
||||
const emptyDefault = fields[key].type === 'list' ? '[]' : '{}';
|
||||
acc[key] = fields[key].value
|
||||
? JSON.stringify(fields[key].value, null, 2)
|
||||
: emptyDefault;
|
||||
} else {
|
||||
acc[key] = fields[key].value ?? '';
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && github && (
|
||||
<Formik initialValues={initialValues(github)} onSubmit={handleSubmit}>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<InputField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_KEY"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_KEY}
|
||||
/>
|
||||
<EncryptedField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_SECRET"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_SECRET}
|
||||
/>
|
||||
<InputField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_ID"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_ID}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP"
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
onRevert={toggleModal}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<RevertAllAlert
|
||||
onClose={closeModal}
|
||||
onRevertAll={handleRevertAll}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
)}
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default GitHubTeamEdit;
|
||||
@ -0,0 +1,177 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import GitHubTeamEdit from './GitHubTeamEdit';
|
||||
|
||||
jest.mock('../../../../api/models/Settings');
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL:
|
||||
'https://towerhost/sso/complete/github-team/',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '$encrypted$',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: 'team_id',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: {},
|
||||
},
|
||||
});
|
||||
|
||||
describe('<GitHubTeamEdit />', () => {
|
||||
let wrapper;
|
||||
let history;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
history = createMemoryHistory({
|
||||
initialEntries: ['/settings/github/team/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubTeamEdit />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('GitHubTeamEdit').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should display expected form fields', async () => {
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Team OAuth2 Key"]').length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Team OAuth2 Secret"]').length
|
||||
).toBe(1);
|
||||
expect(wrapper.find('FormGroup[label="GitHub Team ID"]').length).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Team OAuth2 Organization Map"]')
|
||||
.length
|
||||
).toBe(1);
|
||||
expect(
|
||||
wrapper.find('FormGroup[label="GitHub Team OAuth2 Team Map"]').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('button[aria-label="Revert all to default"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(1);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('RevertAllAlert button[aria-label="Confirm revert all"]')
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
.find(
|
||||
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_TEAM_SECRET"] button[aria-label="Revert"]'
|
||||
)
|
||||
.invoke('onClick')();
|
||||
wrapper.find('input#SOCIAL_AUTH_GITHUB_TEAM_ID').simulate('change', {
|
||||
target: { value: '12345', name: 'SOCIAL_AUTH_GITHUB_TEAM_ID' },
|
||||
});
|
||||
wrapper
|
||||
.find('CodeMirrorInput#SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP')
|
||||
.invoke('onChange')('{\n"Default":{\n"users":\ntrue\n}\n}');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: '12345',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: {
|
||||
Default: {
|
||||
users: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should navigate to github team detail on successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/github/team/details');
|
||||
});
|
||||
|
||||
test('should navigate to github team detail when cancel is clicked', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/github/team/details');
|
||||
});
|
||||
|
||||
test('should display error message on unsuccessful submission', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
data: { detail: 'An error occurred' },
|
||||
},
|
||||
};
|
||||
SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error));
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should display ContentError on throw', async () => {
|
||||
SettingsAPI.readCategory.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error())
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<GitHubTeamEdit />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
export { default } from './GitHubTeamEdit';
|
||||
@ -78,6 +78,7 @@ function GoogleOAuth2Detail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/google_oauth2/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -95,6 +95,7 @@ function JobsDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/jobs/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -159,6 +159,7 @@ function LDAPDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to={`${baseURL}/${category}/edit`}
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -13,6 +13,7 @@ function LicenseDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/license/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -99,6 +99,7 @@ function LoggingDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/logging/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -246,6 +246,7 @@ function LoggingEdit({ i18n }) {
|
||||
<div>
|
||||
<Button
|
||||
aria-label={i18n._(t`Test logging`)}
|
||||
ouiaId="test-logging-button"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={handleTest}
|
||||
|
||||
@ -140,6 +140,7 @@ function MiscSystemDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/miscellaneous_system/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -78,6 +78,7 @@ function RADIUSDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/radius/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -78,6 +78,7 @@ function SAMLDetail({ i18n }) {
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
ouiaId="edit-button"
|
||||
to="/settings/saml/edit"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
|
||||
@ -79,6 +79,7 @@ function TACACSDetail({ i18n }) {
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
to="/settings/tacacs/edit"
|
||||
ouiaId="edit-button"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
</Button>
|
||||
|
||||
@ -94,6 +94,7 @@ function UIDetail({ i18n }) {
|
||||
aria-label={i18n._(t`Edit`)}
|
||||
component={Link}
|
||||
to="/settings/ui/edit"
|
||||
ouiaId="edit-button"
|
||||
>
|
||||
{i18n._(t`Edit`)}
|
||||
</Button>
|
||||
|
||||
@ -33,6 +33,7 @@ function LoggingTestAlert({ i18n, successResponse, errorResponse, onClose }) {
|
||||
{testMessage && (
|
||||
<Alert
|
||||
actionClose={<AlertActionCloseButton onClose={onClose} />}
|
||||
ouiaId="logging-test-alert"
|
||||
title={successResponse ? i18n._(t`Success`) : i18n._(t`Error`)}
|
||||
variant={successResponse ? 'success' : 'danger'}
|
||||
>
|
||||
|
||||
@ -11,12 +11,14 @@ function RevertAllAlert({ i18n, onClose, onRevertAll }) {
|
||||
title={i18n._(t`Revert settings`)}
|
||||
variant="info"
|
||||
onClose={onClose}
|
||||
ouiaId="revert-all-modal"
|
||||
actions={[
|
||||
<Button
|
||||
key="revert"
|
||||
variant="primary"
|
||||
aria-label={i18n._(t`Confirm revert all`)}
|
||||
onClick={onRevertAll}
|
||||
ouiaId="confirm-revert-all-button"
|
||||
>
|
||||
{i18n._(t`Revert all`)}
|
||||
</Button>,
|
||||
@ -25,6 +27,7 @@ function RevertAllAlert({ i18n, onClose, onRevertAll }) {
|
||||
variant="secondary"
|
||||
aria-label={i18n._(t`Cancel revert`)}
|
||||
onClick={onClose}
|
||||
ouiaId="cancel-revert-all-button"
|
||||
>
|
||||
{i18n._(t`Cancel`)}
|
||||
</Button>,
|
||||
|
||||
@ -20,6 +20,7 @@ const RevertFormActionGroup = ({
|
||||
variant="primary"
|
||||
type="button"
|
||||
onClick={onSubmit}
|
||||
ouiaId="save-button"
|
||||
>
|
||||
{i18n._(t`Save`)}
|
||||
</Button>
|
||||
@ -28,6 +29,7 @@ const RevertFormActionGroup = ({
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={onRevert}
|
||||
ouiaId="revert-all-button"
|
||||
>
|
||||
{i18n._(t`Revert all to default`)}
|
||||
</Button>
|
||||
@ -37,6 +39,7 @@ const RevertFormActionGroup = ({
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
ouiaId="cancel-button"
|
||||
>
|
||||
{i18n._(t`Cancel`)}
|
||||
</Button>
|
||||
|
||||
@ -90,6 +90,7 @@ const BooleanField = withI18n()(
|
||||
labelOff={i18n._(t`Off`)}
|
||||
onChange={checked => helpers.setValue(checked)}
|
||||
aria-label={ariaLabel || config.label}
|
||||
ouiaId={ariaLabel || config.label}
|
||||
/>
|
||||
</SettingGroup>
|
||||
) : null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user