Adds support for github enterprise auth methods in ui_next

This commit is contained in:
mabashian
2021-01-13 13:38:39 -05:00
committed by Egor Margineanu
parent 9ccee200f3
commit d461090415
16 changed files with 1454 additions and 6 deletions

View File

@@ -898,7 +898,7 @@ register(
field_class=fields.CharField,
allow_blank=True,
default='',
label=_('GitHub OAuth2 Secret'),
label=_('GitHub Enterprise OAuth2 Secret'),
help_text=_('The OAuth2 secret (Client Secret) from your GitHub Enterprise developer application.'),
category=_('GitHub OAuth2'),
category_slug='github-enterprise',
@@ -952,7 +952,7 @@ register(
field_class=fields.CharField,
allow_blank=False,
default='',
label=_('GitHub Enterprise URL'),
label=_('GitHub Enterprise Organization URL'),
help_text=_('The URL for your Github Enteprise.'),
category=_('GitHub Enterprise OAuth2'),
category_slug='github-enterprise-org',
@@ -963,7 +963,7 @@ register(
field_class=fields.CharField,
allow_blank=False,
default='',
label=_('GitHub Enterprise API URL'),
label=_('GitHub Enterprise Organization API URL'),
help_text=_('The API URL for your GitHub Enterprise.'),
category=_('GitHub Enterprise OAuth2'),
category_slug='github-enterprise-org',
@@ -1052,8 +1052,8 @@ register(
field_class=fields.CharField,
allow_blank=False,
default='',
label=_('GitHub Enterprise URL'),
help_text=_('The URL for your Github Enteprise.'),
label=_('GitHub Enterprise Team URL'),
help_text=_('The URL for your Github Enterprise.'),
category=_('GitHub Enterprise OAuth2'),
category_slug='github-enterprise-team',
)
@@ -1063,7 +1063,7 @@ register(
field_class=fields.CharField,
allow_blank=False,
default='',
label=_('GitHub Enterprise API URL'),
label=_('GitHub Enterprise Team API URL'),
help_text=_('The API URL for your GitHub Enterprise.'),
category=_('GitHub Enterprise OAuth2'),
category_slug='github-enterprise-team',

View File

@@ -165,6 +165,41 @@ function AWXLogin({ alt, i18n, isAuthenticated }) {
</LoginMainFooterLinksItem>
);
}
if (authKey === 'github-enterprise') {
return (
<LoginMainFooterLinksItem href={loginUrl} key={authKey}>
<Tooltip
content={i18n._(t`Sign in with GitHub Enterprise`)}
>
<GithubIcon />
</Tooltip>
</LoginMainFooterLinksItem>
);
}
if (authKey === 'github-enterprise-org') {
return (
<LoginMainFooterLinksItem href={loginUrl} key={authKey}>
<Tooltip
content={i18n._(
t`Sign in with GitHub Enterprise Organizations`
)}
>
<GithubIcon />
</Tooltip>
</LoginMainFooterLinksItem>
);
}
if (authKey === 'github-enterprise-team') {
return (
<LoginMainFooterLinksItem href={loginUrl} key={authKey}>
<Tooltip
content={i18n._(t`Sign in with GitHub Enterprise Teams`)}
>
<GithubIcon />
</Tooltip>
</LoginMainFooterLinksItem>
);
}
if (authKey === 'google-oauth2') {
return (
<LoginMainFooterLinksItem href={loginUrl} key={authKey}>

View File

@@ -8,6 +8,9 @@ import GitHubDetail from './GitHubDetail';
import GitHubEdit from './GitHubEdit';
import GitHubOrgEdit from './GitHubOrgEdit';
import GitHubTeamEdit from './GitHubTeamEdit';
import GitHubEnterpriseEdit from './GitHubEnterpriseEdit';
import GitHubEnterpriseOrgEdit from './GitHubEnterpriseOrgEdit';
import GitHubEnterpriseTeamEdit from './GitHubEnterpriseTeamEdit';
function GitHub({ i18n }) {
const baseURL = '/settings/github';
@@ -40,6 +43,15 @@ function GitHub({ i18n }) {
<Route path={`${baseURL}/team/edit`}>
<GitHubTeamEdit />
</Route>
<Route path={`${baseURL}/enterprise/edit`}>
<GitHubEnterpriseEdit />
</Route>
<Route path={`${baseURL}/enterprise_organization/edit`}>
<GitHubEnterpriseOrgEdit />
</Route>
<Route path={`${baseURL}/enterprise_team/edit`}>
<GitHubEnterpriseTeamEdit />
</Route>
<Route key="not-found" path={`${baseURL}/*`}>
<ContentError isNotFound>
<Link to={`${baseURL}/default/details`}>

View File

@@ -48,6 +48,44 @@ describe('<GitHub />', () => {
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: {},
},
});
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: 'ent_key',
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '$encrypted',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: {},
},
});
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: 'ent_org_key',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: 'ent_org_name',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: {},
},
});
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: 'ent_team_key',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: 'ent_team_id',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: {},
},
});
});
afterEach(() => {

View File

@@ -31,21 +31,33 @@ function GitHubDetail({ i18n }) {
{ data: gitHubDefault },
{ data: gitHubOrganization },
{ data: gitHubTeam },
{ data: gitHubEnterprise },
{ data: gitHubEnterpriseOrganization },
{ data: gitHubEnterpriseTeam },
] = await Promise.all([
SettingsAPI.readCategory('github'),
SettingsAPI.readCategory('github-org'),
SettingsAPI.readCategory('github-team'),
SettingsAPI.readCategory('github-enterprise'),
SettingsAPI.readCategory('github-enterprise-org'),
SettingsAPI.readCategory('github-enterprise-team'),
]);
return {
default: gitHubDefault,
organization: gitHubOrganization,
team: gitHubTeam,
enterprise: gitHubEnterprise,
enterprise_organization: gitHubEnterpriseOrganization,
enterprise_team: gitHubEnterpriseTeam,
};
}, []),
{
default: null,
organization: null,
team: null,
enterprise: null,
enterprise_organization: null,
enterprise_team: null,
}
);
@@ -79,6 +91,21 @@ function GitHubDetail({ i18n }) {
link: `${baseURL}/team/details`,
id: 2,
},
{
name: i18n._(t`GitHub Enterprise`),
link: `${baseURL}/enterprise/details`,
id: 3,
},
{
name: i18n._(t`GitHub Enterprise Organization`),
link: `${baseURL}/enterprise_organization/details`,
id: 4,
},
{
name: i18n._(t`GitHub Enterprise Team`),
link: `${baseURL}/enterprise_team/details`,
id: 5,
},
];
if (!Object.keys(gitHubDetails).includes(category)) {

View File

@@ -51,6 +51,44 @@ const mockTeam = {
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: {},
},
};
const mockEnterprise = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: 'https://localhost/enterpriseurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: 'https://localhost/enterpriseapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: 'foobar',
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: null,
},
};
const mockEnterpriseOrg = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: 'https://localhost/orgurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: 'https://localhost/orgapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: 'foobar',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: 'foo',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: null,
},
};
const mockEnterpriseTeam = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: 'https://localhost/teamurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: 'https://localhost/teamapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: 'foobar',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: 'foo',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: null,
},
};
describe('<GitHubDetail />', () => {
describe('Default', () => {
@@ -60,6 +98,9 @@ describe('<GitHubDetail />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/default/details',
path: '/settings/github/:category/details',
@@ -90,6 +131,9 @@ describe('<GitHubDetail />', () => {
'GitHub Default',
'GitHub Organization',
'GitHub Team',
'GitHub Enterprise',
'GitHub Enterprise Organization',
'GitHub Enterprise Team',
];
wrapper.find('RoutedTabs li').forEach((tab, index) => {
expect(tab.text()).toEqual(expectedTabs[index]);
@@ -149,6 +193,9 @@ describe('<GitHubDetail />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/organization/details',
path: '/settings/github/:category/details',
@@ -198,6 +245,9 @@ describe('<GitHubDetail />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/team/details',
path: '/settings/github/:category/details',
@@ -236,6 +286,199 @@ describe('<GitHubDetail />', () => {
});
});
describe('Enterprise', () => {
let wrapper;
beforeAll(async () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/enterprise/details',
path: '/settings/github/:category/details',
params: { category: 'enterprise' },
}));
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubDetail />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
afterAll(() => {
wrapper.unmount();
jest.clearAllMocks();
});
test('should render expected details', () => {
assertDetail(
wrapper,
'GitHub Enterprise OAuth2 Callback URL',
'https://towerhost/sso/complete/github-enterprise/'
);
assertDetail(
wrapper,
'GitHub Enterprise URL',
'https://localhost/enterpriseurl'
);
assertDetail(
wrapper,
'GitHub Enterprise API URL',
'https://localhost/enterpriseapi'
);
assertDetail(wrapper, 'GitHub Enterprise OAuth2 Key', 'foobar');
assertDetail(wrapper, 'GitHub Enterprise OAuth2 Secret', 'Encrypted');
assertVariableDetail(
wrapper,
'GitHub Enterprise OAuth2 Organization Map',
'{}'
);
assertVariableDetail(wrapper, 'GitHub Enterprise OAuth2 Team Map', '{}');
});
});
describe('Enterprise Org', () => {
let wrapper;
beforeAll(async () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/enterprise_organization/details',
path: '/settings/github/:category/details',
params: { category: 'enterprise_organization' },
}));
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubDetail />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
afterAll(() => {
wrapper.unmount();
jest.clearAllMocks();
});
test('should render expected details', () => {
assertDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Callback URL',
'https://towerhost/sso/complete/github-enterprise-org/'
);
assertDetail(
wrapper,
'GitHub Enterprise Organization URL',
'https://localhost/orgurl'
);
assertDetail(
wrapper,
'GitHub Enterprise Organization API URL',
'https://localhost/orgapi'
);
assertDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Key',
'foobar'
);
assertDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Secret',
'Encrypted'
);
assertDetail(wrapper, 'GitHub Enterprise Organization Name', 'foo');
assertVariableDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Organization Map',
'{}'
);
assertVariableDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Team Map',
'{}'
);
});
});
describe('Enterprise Team', () => {
let wrapper;
beforeAll(async () => {
SettingsAPI.readCategory.mockResolvedValueOnce(mockDefault);
SettingsAPI.readCategory.mockResolvedValueOnce(mockOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockTeam);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterprise);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseOrg);
SettingsAPI.readCategory.mockResolvedValueOnce(mockEnterpriseTeam);
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/enterprise_team/details',
path: '/settings/github/:category/details',
params: { category: 'enterprise_team' },
}));
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubDetail />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
afterAll(() => {
wrapper.unmount();
jest.clearAllMocks();
});
test('should render expected details', () => {
assertDetail(
wrapper,
'GitHub Enterprise Team OAuth2 Callback URL',
'https://towerhost/sso/complete/github-enterprise-team/'
);
assertDetail(
wrapper,
'GitHub Enterprise Team URL',
'https://localhost/teamurl'
);
assertDetail(
wrapper,
'GitHub Enterprise Team API URL',
'https://localhost/teamapi'
);
assertDetail(wrapper, 'GitHub Enterprise Team OAuth2 Key', 'foobar');
assertDetail(
wrapper,
'GitHub Enterprise Team OAuth2 Secret',
'Encrypted'
);
assertDetail(wrapper, 'GitHub Enterprise Team ID', 'foo');
assertVariableDetail(
wrapper,
'GitHub Enterprise Team OAuth2 Organization Map',
'{}'
);
assertVariableDetail(
wrapper,
'GitHub Enterprise Team OAuth2 Team Map',
'{}'
);
});
});
describe('Redirect', () => {
test('should render redirect when user navigates to erroneous category', async () => {
let wrapper;

View File

@@ -0,0 +1,151 @@
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 GitHubEnterpriseEdit() {
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-enterprise');
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/enterprise/details');
},
[history]
),
null
);
const handleSubmit = async form => {
await submitForm({
...form,
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP
),
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_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/enterprise/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_ENTERPRISE_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY}
/>
<EncryptedField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_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 GitHubEnterpriseEdit;

View File

@@ -0,0 +1,196 @@
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 GitHubEnterpriseEdit from './GitHubEnterpriseEdit';
jest.mock('../../../../api/models/Settings');
SettingsAPI.updateAll.mockResolvedValue({});
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: null,
},
});
describe('<GitHubEnterpriseEdit />', () => {
let wrapper;
let history;
afterEach(() => {
wrapper.unmount();
jest.clearAllMocks();
});
beforeEach(async () => {
history = createMemoryHistory({
initialEntries: ['/settings/github/enterprise/edit'],
});
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubEnterpriseEdit />
</SettingsProvider>,
{
context: { router: { history } },
}
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
test('initially renders without crashing', () => {
expect(wrapper.find('GitHubEnterpriseEdit').length).toBe(1);
});
test('should display expected form fields', async () => {
expect(
wrapper.find('FormGroup[label="GitHub Enterprise URL"]').length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise API URL"]').length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise OAuth2 Key"]').length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise OAuth2 Secret"]').length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise OAuth2 Organization Map"]'
).length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise 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_ENTERPRISE_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: null,
});
});
test('should successfully send request to api on form submission', async () => {
act(() => {
wrapper
.find(
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET"] button[aria-label="Revert"]'
)
.invoke('onClick')();
wrapper
.find('input#SOCIAL_AUTH_GITHUB_ENTERPRISE_URL')
.simulate('change', {
target: {
value: 'https://localhost',
name: 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL',
},
});
wrapper
.find('CodeMirrorInput#SOCIAL_AUTH_GITHUB_ENTERPRISE_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_ENTERPRISE_URL: 'https://localhost',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: {
Default: {
users: false,
},
},
});
});
test('should navigate to github enterprise detail on successful submission', async () => {
await act(async () => {
wrapper.find('Form').invoke('onSubmit')();
});
expect(history.location.pathname).toEqual(
'/settings/github/enterprise/details'
);
});
test('should navigate to github enterprise detail when cancel is clicked', async () => {
await act(async () => {
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
});
expect(history.location.pathname).toEqual(
'/settings/github/enterprise/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}>
<GitHubEnterpriseEdit />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(wrapper.find('ContentError').length).toBe(1);
});
});

View File

@@ -0,0 +1 @@
export { default } from './GitHubEnterpriseEdit';

View File

@@ -0,0 +1,157 @@
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 GitHubEnterpriseOrgEdit() {
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-enterprise-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/enterprise_organization/details');
},
[history]
),
null
);
const handleSubmit = async form => {
await submitForm({
...form,
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP
),
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_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/enterprise_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_ENTERPRISE_ORG_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY}
/>
<EncryptedField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP"
config={
github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP
}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_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 GitHubEnterpriseOrgEdit;

View File

@@ -0,0 +1,212 @@
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 GitHubEnterpriseOrgEdit from './GitHubEnterpriseOrgEdit';
jest.mock('../../../../api/models/Settings');
SettingsAPI.updateAll.mockResolvedValue({});
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: null,
},
});
describe('<GitHubEnterpriseOrgEdit />', () => {
let wrapper;
let history;
afterEach(() => {
wrapper.unmount();
jest.clearAllMocks();
});
beforeEach(async () => {
history = createMemoryHistory({
initialEntries: ['/settings/github/enterprise_organization/edit'],
});
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubEnterpriseOrgEdit />
</SettingsProvider>,
{
context: { router: { history } },
}
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
test('initially renders without crashing', () => {
expect(wrapper.find('GitHubEnterpriseOrgEdit').length).toBe(1);
});
test('should display expected form fields', async () => {
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Organization URL"]')
.length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Organization API URL"]')
.length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise Organization OAuth2 Key"]'
).length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise Organization OAuth2 Secret"]'
).length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Organization Name"]')
.length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise Organization OAuth2 Organization Map"]'
).length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise 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_ENTERPRISE_ORG_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: null,
});
});
test('should successfully send request to api on form submission', async () => {
act(() => {
wrapper
.find(
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET"] button[aria-label="Revert"]'
)
.invoke('onClick')();
wrapper
.find('input#SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL')
.simulate('change', {
target: {
value: 'https://localhost',
name: 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL',
},
});
wrapper
.find(
'CodeMirrorInput#SOCIAL_AUTH_GITHUB_ENTERPRISE_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_ENTERPRISE_ORG_URL: 'https://localhost',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: {
Default: {
users: false,
},
},
});
});
test('should navigate to github enterprise org detail on successful submission', async () => {
await act(async () => {
wrapper.find('Form').invoke('onSubmit')();
});
expect(history.location.pathname).toEqual(
'/settings/github/enterprise_organization/details'
);
});
test('should navigate to github enterprise org detail when cancel is clicked', async () => {
await act(async () => {
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
});
expect(history.location.pathname).toEqual(
'/settings/github/enterprise_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}>
<GitHubEnterpriseOrgEdit />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(wrapper.find('ContentError').length).toBe(1);
});
});

View File

@@ -0,0 +1 @@
export { default } from './GitHubEnterpriseOrgEdit';

View File

@@ -0,0 +1,157 @@
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 GitHubEnterpriseTeamEdit() {
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-enterprise-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/enterprise_team/details');
},
[history]
),
null
);
const handleSubmit = async form => {
await submitForm({
...form,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP
),
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: formatJson(
form.SOCIAL_AUTH_GITHUB_ENTERPRISE_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/enterprise_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_ENTERPRISE_TEAM_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY}
/>
<EncryptedField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET}
/>
<InputField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP"
config={
github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP
}
/>
<ObjectField
name="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP"
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_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 GitHubEnterpriseTeamEdit;

View File

@@ -0,0 +1,206 @@
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 GitHubEnterpriseTeamEdit from './GitHubEnterpriseTeamEdit';
jest.mock('../../../../api/models/Settings');
SettingsAPI.updateAll.mockResolvedValue({});
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: null,
},
});
describe('<GitHubEnterpriseTeamEdit />', () => {
let wrapper;
let history;
afterEach(() => {
wrapper.unmount();
jest.clearAllMocks();
});
beforeEach(async () => {
history = createMemoryHistory({
initialEntries: ['/settings/github/enterprise_team/edit'],
});
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubEnterpriseTeamEdit />
</SettingsProvider>,
{
context: { router: { history } },
}
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
test('initially renders without crashing', () => {
expect(wrapper.find('GitHubEnterpriseTeamEdit').length).toBe(1);
});
test('should display expected form fields', async () => {
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Team URL"]').length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Team API URL"]').length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Team OAuth2 Key"]')
.length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Team OAuth2 Secret"]')
.length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise Team ID"]').length
).toBe(1);
expect(
wrapper.find(
'FormGroup[label="GitHub Enterprise Team OAuth2 Organization Map"]'
).length
).toBe(1);
expect(
wrapper.find('FormGroup[label="GitHub Enterprise 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_ENTERPRISE_TEAM_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: null,
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: null,
});
});
test('should successfully send request to api on form submission', async () => {
act(() => {
wrapper
.find(
'FormGroup[fieldId="SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET"] button[aria-label="Revert"]'
)
.invoke('onClick')();
wrapper
.find('input#SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL')
.simulate('change', {
target: {
value: 'https://localhost',
name: 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL',
},
});
wrapper
.find(
'CodeMirrorInput#SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_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_ENTERPRISE_TEAM_URL: 'https://localhost',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: {},
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: {
Default: {
users: false,
},
},
});
});
test('should navigate to github enterprise team detail on successful submission', async () => {
await act(async () => {
wrapper.find('Form').invoke('onSubmit')();
});
expect(history.location.pathname).toEqual(
'/settings/github/enterprise_team/details'
);
});
test('should navigate to github enterprise 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/enterprise_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}>
<GitHubEnterpriseTeamEdit />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(wrapper.find('ContentError').length).toBe(1);
});
});

View File

@@ -0,0 +1 @@
export { default } from './GitHubEnterpriseTeamEdit';

View File

@@ -57,6 +57,17 @@ function Settings({ i18n }) {
'/settings/github/team': i18n._(t`GitHub Team`),
'/settings/github/team/details': i18n._(t`Details`),
'/settings/github/team/edit': i18n._(t`Edit Details`),
'/settings/github/enterprise': i18n._(t`GitHub Enterprise`),
'/settings/github/enterprise/details': i18n._(t`Details`),
'/settings/github/enterprise/edit': i18n._(t`Edit Details`),
'/settings/github/enterprise_organization': i18n._(
t`GitHub Enterprise Organization`
),
'/settings/github/enterprise_organization/details': i18n._(t`Details`),
'/settings/github/enterprise_organization/edit': i18n._(t`Edit Details`),
'/settings/github/enterprise_team': i18n._(t`GitHub Enterprise Team`),
'/settings/github/enterprise_team/details': i18n._(t`Details`),
'/settings/github/enterprise_team/edit': i18n._(t`Edit Details`),
'/settings/google_oauth2': i18n._(t`Google OAuth2`),
'/settings/google_oauth2/details': i18n._(t`Details`),
'/settings/google_oauth2/edit': i18n._(t`Edit Details`),