From db5fc175198e954404be2a9d73d63b7e3c19af49 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Mon, 29 Jun 2020 08:22:22 -0500 Subject: SONAR-13475 - List Github Enterprise repositories API (#2883) fixup! SONAR-13475 - List Github Enterprise repositories API (#2883) --- .../sonar-web/src/main/js/api/alm-integrations.ts | 41 +++-- .../apps/create/project/BitbucketProjectCreate.tsx | 13 +- .../js/apps/create/project/CreateProjectPage.tsx | 25 +-- .../js/apps/create/project/GitHubProjectCreate.tsx | 127 ++++++++++++--- .../create/project/GitHubProjectCreateRenderer.tsx | 170 +++++++++++++-------- .../__tests__/BitbucketProjectCreate-test.tsx | 3 +- .../project/__tests__/CreateProjectPage-test.tsx | 38 ++--- .../project/__tests__/GitHubProjectCreate-test.tsx | 81 +++++++++- .../__tests__/GitHubProjectCreateRenderer-test.tsx | 10 +- .../BitbucketProjectCreate-test.tsx.snap | 1 + .../__snapshots__/CreateProjectPage-test.tsx.snap | 32 +++- .../GitHubProjectCreateRenderer-test.tsx.snap | 56 ++++++- 12 files changed, 434 insertions(+), 163 deletions(-) (limited to 'server/sonar-web') diff --git a/server/sonar-web/src/main/js/api/alm-integrations.ts b/server/sonar-web/src/main/js/api/alm-integrations.ts index 3b5712b5d22..b0007b3c480 100644 --- a/server/sonar-web/src/main/js/api/alm-integrations.ts +++ b/server/sonar-web/src/main/js/api/alm-integrations.ts @@ -87,30 +87,49 @@ export function searchForBitbucketServerRepositories( }); } -export function getGithubClientId(almSetting: string): Promise<{ clientId: string }> { +export function getGithubClientId(almSetting: string): Promise<{ clientId?: string }> { return getJSON('/api/alm_integrations/get_github_client_id', { almSetting }); } +export function importGithubRepository( + almSetting: string, + organization: string, + repositoryKey: string +): Promise<{ project: ProjectBase }> { + return postJSON('/api/alm_integrations/import_github_project', { + almSetting, + organization, + repositoryKey + }).catch(throwGlobalError); +} + export function getGithubOrganizations( almSetting: string, token: string ): Promise<{ organizations: GithubOrganization[] }> { - return getJSON('/api/alm_integrations/list_github_enterprise_organizations', { + return getJSON('/api/alm_integrations/list_github_organizations', { almSetting, token + }).catch((response?: Response) => { + if (response && response.status !== 400) { + throwGlobalError(response); + } }); } -export function getGithubRepositories( - almSetting: string, - organization: string, - p = 1, - query?: string -): Promise<{ repositories: GithubRepository[]; paging: T.Paging }> { - return getJSON('/api/alm_integrations/list_github_enterprise_repositories', { +export function getGithubRepositories(data: { + almSetting: string; + organization: string; + ps: number; + p?: number; + query?: string; +}): Promise<{ repositories: GithubRepository[]; paging: T.Paging }> { + const { almSetting, organization, ps, p = 1, query } = data; + return getJSON('/api/alm_integrations/list_github_repositories', { almSetting, organization, p, - query: query || undefined - }); + ps, + q: query || undefined + }).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx index ef6d825005a..64fbcaa5930 100644 --- a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { connect } from 'react-redux'; import { WithRouterProps } from 'react-router'; import { checkPersonalAccessTokenIsValid, @@ -28,7 +27,6 @@ import { searchForBitbucketServerRepositories, setAlmPersonalAccessToken } from '../../../api/alm-integrations'; -import { getAppState, Store } from '../../../store/rootReducer'; import { BitbucketProject, BitbucketProjectRepositories, @@ -38,8 +36,8 @@ import { AlmSettingsInstance } from '../../../types/alm-settings'; import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer'; interface Props extends Pick { + canAdmin: boolean; bitbucketSettings: AlmSettingsInstance[]; - canAdmin?: boolean; loadingBindings: boolean; onProjectCreate: (projectKeys: string[]) => void; } @@ -58,7 +56,7 @@ interface State { tokenValidationFailed: boolean; } -export class BitbucketProjectCreate extends React.PureComponent { +export default class BitbucketProjectCreate extends React.PureComponent { mounted = false; constructor(props: Props) { @@ -285,10 +283,3 @@ export class BitbucketProjectCreate extends React.PureComponent { ); } } - -const mapStateToProps = (state: Store) => { - const { canAdmin } = getAppState(state); - return { canAdmin }; -}; - -export default connect(mapStateToProps)(BitbucketProjectCreate); diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx index 5737a060933..3b4183beae4 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx @@ -47,7 +47,7 @@ interface State { export class CreateProjectPage extends React.PureComponent { mounted = false; - state: State = { bitbucketSettings: [], githubSettings: [], loading: false }; + state: State = { bitbucketSettings: [], githubSettings: [], loading: true }; componentDidMount() { const { @@ -82,12 +82,6 @@ export class CreateProjectPage extends React.PureComponent { }); }; - handleProjectCreate = (projectKeys: string[]) => { - if (projectKeys.length === 1) { - this.props.router.push(getProjectUrl(projectKeys[0])); - } - }; - handleModeSelect = (mode: CreateProjectModes) => { const { router, location } = this.props; router.push({ @@ -96,10 +90,17 @@ export class CreateProjectPage extends React.PureComponent { }); }; + handleProjectCreate = (projectKeys: string[]) => { + if (projectKeys.length === 1) { + this.props.router.push(getProjectUrl(projectKeys[0])); + } + }; + renderForm(mode?: CreateProjectModes) { const { appState: { canAdmin }, - location + location, + router } = this.props; const { bitbucketSettings, githubSettings, loading } = this.state; @@ -107,6 +108,7 @@ export class CreateProjectPage extends React.PureComponent { case CreateProjectModes.BitbucketServer: { return ( { return ( ); } diff --git a/server/sonar-web/src/main/js/apps/create/project/GitHubProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/GitHubProjectCreate.tsx index 3417541c022..1c03f8c3b50 100644 --- a/server/sonar-web/src/main/js/apps/create/project/GitHubProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/GitHubProjectCreate.tsx @@ -19,25 +19,29 @@ */ import { debounce } from 'lodash'; import * as React from 'react'; +import { WithRouterProps } from 'react-router'; import { getHostUrl } from 'sonar-ui-common/helpers/urls'; import { getGithubClientId, getGithubOrganizations, - getGithubRepositories + getGithubRepositories, + importGithubRepository } from '../../../api/alm-integrations'; import { GithubOrganization, GithubRepository } from '../../../types/alm-integration'; import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; import GitHubProjectCreateRenderer from './GitHubProjectCreateRenderer'; -interface Props { +interface Props extends Pick { canAdmin: boolean; - code?: string; - settings?: AlmSettingsInstance; + loadingBindings: boolean; + onProjectCreate: (projectKeys: string[]) => void; + settings: AlmSettingsInstance[]; } interface State { error: boolean; - loading: boolean; + importing: boolean; + loadingOrganizations: boolean; loadingRepositories: boolean; organizations: GithubOrganization[]; repositoryPaging: T.Paging; @@ -45,6 +49,7 @@ interface State { searchQuery: string; selectedOrganization?: GithubOrganization; selectedRepository?: GithubRepository; + settings?: AlmSettingsInstance; } const REPOSITORY_PAGE_SIZE = 30; @@ -57,12 +62,14 @@ export default class GitHubProjectCreate extends React.Component { this.state = { error: false, - loading: true, + importing: false, + loadingOrganizations: true, loadingRepositories: false, organizations: [], repositories: [], repositoryPaging: { pageSize: REPOSITORY_PAGE_SIZE, total: 0, pageIndex: 1 }, - searchQuery: '' + searchQuery: '', + settings: props.settings[0] }; this.triggerSearch = debounce(this.triggerSearch, 250); @@ -75,8 +82,8 @@ export default class GitHubProjectCreate extends React.Component { } componentDidUpdate(prevProps: Props) { - if (!prevProps.settings && this.props.settings) { - this.initialize(); + if (prevProps.settings.length === 0 && this.props.settings.length > 0) { + this.setState({ settings: this.props.settings[0] }, () => this.initialize()); } } @@ -85,19 +92,24 @@ export default class GitHubProjectCreate extends React.Component { } async initialize() { - const { code, settings } = this.props; + const { location, router } = this.props; + const { settings } = this.state; - if (!settings) { + if (!settings || !settings.url) { this.setState({ error: true }); return; } else { this.setState({ error: false }); } + const code = location.query?.code; + try { if (!code) { await this.redirectToGithub(settings); } else { + delete location.query.code; + router.replace(location); await this.fetchOrganizations(settings, code); } } catch (e) { @@ -108,8 +120,17 @@ export default class GitHubProjectCreate extends React.Component { } async redirectToGithub(settings: AlmSettingsInstance) { + if (!settings.url) { + return; + } + const { clientId } = await getGithubClientId(settings.key); + if (!clientId) { + this.setState({ error: true }); + return; + } + const queryParams = [ { param: 'client_id', value: clientId }, { param: 'redirect_uri', value: `${getHostUrl()}/projects/create?mode=${AlmKeys.GitHub}` } @@ -117,20 +138,32 @@ export default class GitHubProjectCreate extends React.Component { .map(({ param, value }) => `${param}=${value}`) .join('&'); - window.location.replace(`https://github.com/login/oauth/authorize?${queryParams}`); + let instanceRootUrl; + // Strip the api section from the url, since we're not hitting the api here. + if (settings.url.includes('/api/v3')) { + // GitHub Enterprise + instanceRootUrl = settings.url.replace('/api/v3', ''); + } else { + // github.com + instanceRootUrl = settings.url.replace('api.', ''); + } + + // strip the trailing / + instanceRootUrl = instanceRootUrl.replace(/\/$/, ''); + window.location.replace(`${instanceRootUrl}/login/oauth/authorize?${queryParams}`); } async fetchOrganizations(settings: AlmSettingsInstance, token: string) { const { organizations } = await getGithubOrganizations(settings.key, token); if (this.mounted) { - this.setState({ loading: false, organizations }); + this.setState({ loadingOrganizations: false, organizations }); } } async fetchRepositories(params: { organizationKey: string; page?: number; query?: string }) { const { organizationKey, page = 1, query } = params; - const { settings } = this.props; + const { settings } = this.state; if (!settings) { this.setState({ error: true }); @@ -139,26 +172,45 @@ export default class GitHubProjectCreate extends React.Component { this.setState({ loadingRepositories: true }); - const data = await getGithubRepositories(settings.key, organizationKey, page, query); + try { + const data = await getGithubRepositories({ + almSetting: settings.key, + organization: organizationKey, + ps: REPOSITORY_PAGE_SIZE, + p: page, + query + }); - if (this.mounted) { - this.setState(({ repositories }) => ({ - loadingRepositories: false, - repositoryPaging: data.paging, - repositories: page === 1 ? data.repositories : [...repositories, ...data.repositories] - })); + if (this.mounted) { + this.setState(({ repositories }) => ({ + loadingRepositories: false, + repositoryPaging: data.paging, + repositories: page === 1 ? data.repositories : [...repositories, ...data.repositories] + })); + } + } catch (_) { + if (this.mounted) { + this.setState({ + loadingRepositories: false, + repositoryPaging: { pageIndex: 1, pageSize: REPOSITORY_PAGE_SIZE, total: 0 }, + repositories: [] + }); + } } } triggerSearch = (query: string) => { const { selectedOrganization } = this.state; if (selectedOrganization) { + this.setState({ selectedRepository: undefined }); this.fetchRepositories({ organizationKey: selectedOrganization.key, query }); } }; handleSelectOrganization = (key: string) => { this.setState(({ organizations }) => ({ + searchQuery: '', + selectedRepository: undefined, selectedOrganization: organizations.find(o => o.key === key) })); this.fetchRepositories({ organizationKey: key }); @@ -187,11 +239,34 @@ export default class GitHubProjectCreate extends React.Component { } }; + handleImportRepository = async () => { + const { selectedOrganization, selectedRepository, settings } = this.state; + + if (settings && selectedOrganization && selectedRepository) { + this.setState({ importing: true }); + + try { + const { project } = await importGithubRepository( + settings.key, + selectedOrganization.key, + selectedRepository.key + ); + + this.props.onProjectCreate([project.key]); + } finally { + if (this.mounted) { + this.setState({ importing: false }); + } + } + } + }; + render() { - const { canAdmin } = this.props; + const { canAdmin, loadingBindings } = this.props; const { error, - loading, + importing, + loadingOrganizations, loadingRepositories, organizations, repositoryPaging, @@ -200,12 +275,16 @@ export default class GitHubProjectCreate extends React.Component { selectedOrganization, selectedRepository } = this.state; + return ( void; onLoadMore: () => void; onSearch: (q: string) => void; onSelectOrganization: (key: string) => void; @@ -53,13 +57,13 @@ function orgToOption({ key, name }: GithubOrganization) { return { value: key, label: name }; } -export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRendererProps) { +const handleSearch = (organizations: GithubOrganization[]) => (q: string) => + Promise.resolve(organizations.filter(o => !q || o.name.includes(q)).map(orgToOption)); + +function renderRepositoryList(props: GitHubProjectCreateRendererProps) { const { - canAdmin, - error, - loading, + importing, loadingRepositories, - organizations, repositories, repositoryPaging, searchQuery, @@ -67,9 +71,99 @@ export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRe selectedRepository } = props; + const isChecked = (repository: GithubRepository) => + !!repository.sqProjectKey || + (!!selectedRepository && selectedRepository.key === repository.key); + + const isDisabled = (repository: GithubRepository) => + !!repository.sqProjectKey || loadingRepositories || importing; + + return ( + selectedOrganization && + repositories && ( +
+
+ +
+ + {repositories.length === 0 ? ( +
+ + {translate('no_results')} + +
+ ) : ( + repositories.map(r => ( + +
+
{r.name}
+ {r.sqProjectKey && ( + + {translate('onboarding.create_project.repository_imported')} + + + )} +
+
+ )) + )} + +
+ +
+
+ ) + ); +} + +export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRendererProps) { + const { + canAdmin, + error, + importing, + loadingBindings, + loadingOrganizations, + organizations, + selectedOrganization, + selectedRepository + } = props; + + if (loadingBindings) { + return ; + } + return (
+ + +
+ ) + } title={ ) : ( - +
{organizations.length > 0 ? ( - Promise.resolve( - organizations.filter(o => !q || o.name.includes(q)).map(orgToOption) - ) - } + defaultOptions={organizations.map(orgToOption)} + onSearch={handleSearch(organizations)} minimumQueryLength={0} onSelect={({ value }) => props.onSelectOrganization(value)} value={selectedOrganization && orgToOption(selectedOrganization)} /> ) : ( - !loading && ( + !loadingOrganizations && ( {canAdmin ? ( )} - {selectedOrganization && repositories && ( -
-
- -
- - {repositories.length === 0 ? ( -
- - {translate('no_results')} - -
- ) : ( - repositories.map(r => ( - -
-
{r.name}
- {r.sqProjectKey && ( - - {translate('onboarding.create_project.repository_imported')} - - - )} -
-
- )) - )} - -
- -
-
- )} + {renderRepositoryList(props)}
); } diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx index 6d1405bb939..b142ef13b16 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx @@ -33,7 +33,7 @@ import { mockBitbucketRepository } from '../../../../helpers/mocks/alm-integrati import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings'; import { mockLocation } from '../../../../helpers/testMocks'; import { AlmKeys } from '../../../../types/alm-settings'; -import { BitbucketProjectCreate } from '../BitbucketProjectCreate'; +import BitbucketProjectCreate from '../BitbucketProjectCreate'; jest.mock('../../../../api/alm-integrations', () => { const { mockBitbucketProject, mockBitbucketRepository } = jest.requireActual( @@ -163,6 +163,7 @@ it('should correctly handle search', async () => { function shallowRender(props: Partial = {}) { return shallow( { }); it('should render correctly if the manual method is selected', () => { - const push = jest.fn(); - const location = { query: { mode: CreateProjectModes.Manual } }; - const wrapper = shallowRender({ router: mockRouter({ push }) }); - - wrapper.instance().handleModeSelect(CreateProjectModes.Manual); - expect(push).toBeCalledWith(expect.objectContaining(location)); - - expect(wrapper.setProps({ location: mockLocation(location) })).toMatchSnapshot(); + expect( + shallowRender({ + location: mockLocation({ query: { mode: CreateProjectModes.Manual } }) + }) + ).toMatchSnapshot(); }); it('should render correctly if the BBS method is selected', () => { - const push = jest.fn(); - const location = { query: { mode: CreateProjectModes.BitbucketServer } }; - const wrapper = shallowRender({ router: mockRouter({ push }) }); - - wrapper.instance().handleModeSelect(CreateProjectModes.BitbucketServer); - expect(push).toBeCalledWith(expect.objectContaining(location)); - - expect(wrapper.setProps({ location: mockLocation(location) })).toMatchSnapshot(); + expect( + shallowRender({ + location: mockLocation({ query: { mode: CreateProjectModes.BitbucketServer } }) + }) + ).toMatchSnapshot(); }); it('should render correctly if the GitHub method is selected', () => { - const push = jest.fn(); - const location = { query: { mode: CreateProjectModes.GitHub } }; - const wrapper = shallowRender({ router: mockRouter({ push }) }); - - wrapper.instance().handleModeSelect(CreateProjectModes.GitHub); - expect(push).toBeCalledWith(expect.objectContaining(location)); - - expect(wrapper.setProps({ location: mockLocation(location) })).toMatchSnapshot(); + const wrapper = shallowRender({ + location: mockLocation({ query: { mode: CreateProjectModes.GitHub } }) + }); + expect(wrapper).toMatchSnapshot(); }); function shallowRender(props: Partial = {}) { diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreate-test.tsx index 36203dcfc8e..4577ba4a0df 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreate-test.tsx @@ -24,16 +24,19 @@ import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { getGithubClientId, getGithubOrganizations, - getGithubRepositories + getGithubRepositories, + importGithubRepository } from '../../../../api/alm-integrations'; import { mockGitHubRepository } from '../../../../helpers/mocks/alm-integrations'; import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings'; +import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; import GitHubProjectCreate from '../GitHubProjectCreate'; jest.mock('../../../../api/alm-integrations', () => ({ getGithubClientId: jest.fn().mockResolvedValue({ clientId: 'client-id-124' }), getGithubOrganizations: jest.fn().mockResolvedValue({ organizations: [] }), - getGithubRepositories: jest.fn().mockResolvedValue({ repositories: [], paging: {} }) + getGithubRepositories: jest.fn().mockResolvedValue({ repositories: [], paging: {} }), + importGithubRepository: jest.fn().mockResolvedValue({ project: {} }) })); const originalLocation = window.location; @@ -61,7 +64,7 @@ beforeEach(() => { }); it('should handle no settings', async () => { - const wrapper = shallowRender({ settings: undefined }); + const wrapper = shallowRender({ settings: [] }); await waitAndUpdate(wrapper); expect(wrapper.state().error).toBe(true); }); @@ -74,15 +77,41 @@ it('should redirect when no code', async () => { expect(window.location.replace).toBeCalled(); }); +it('should redirect when no code - github.com', async () => { + const wrapper = shallowRender({ + settings: [mockAlmSettingsInstance({ key: 'a', url: 'api.github.com' })] + }); + await waitAndUpdate(wrapper); + + expect(getGithubClientId).toBeCalled(); + expect(window.location.replace).toBeCalledWith( + 'github.com/login/oauth/authorize?client_id=client-id-124&redirect_uri=http://localhost/projects/create?mode=github' + ); +}); + +it('should not redirect when invalid clientId', async () => { + (getGithubClientId as jest.Mock).mockResolvedValue({ clientId: undefined }); + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + + expect(wrapper.state().error).toBe(true); + expect(window.location.replace).not.toBeCalled(); +}); + it('should fetch organizations when code', async () => { const organizations = [ { key: '1', name: 'org1' }, { key: '2', name: 'org2' } ]; (getGithubOrganizations as jest.Mock).mockResolvedValueOnce({ organizations }); - const wrapper = shallowRender({ code: '123456' }); + const replace = jest.fn(); + const wrapper = shallowRender({ + location: mockLocation({ query: { code: '123456' } }), + router: mockRouter({ replace }) + }); await waitAndUpdate(wrapper); + expect(replace).toBeCalled(); expect(getGithubOrganizations).toBeCalled(); expect(wrapper.state().organizations).toBe(organizations); }); @@ -98,7 +127,7 @@ it('should handle org selection', async () => { repositories, paging: { total: 1, pageIndex: 1 } }); - const wrapper = shallowRender({ code: '123456' }); + const wrapper = shallowRender({ location: mockLocation({ query: { code: '123456' } }) }); await waitAndUpdate(wrapper); wrapper.instance().handleSelectOrganization('1'); @@ -154,7 +183,13 @@ it('should handle search', async () => { await waitAndUpdate(wrapper); - expect(getGithubRepositories).toBeCalledWith('a', 'o1', 1, query); + expect(getGithubRepositories).toBeCalledWith({ + almSetting: 'a', + organization: 'o1', + p: 1, + ps: 30, + query: 'query' + }); expect(wrapper.state().repositories).toEqual(repositories); }); @@ -169,11 +204,43 @@ it('should handle repository selection', async () => { expect(wrapper.state().selectedRepository).toBe(repo); }); +it('should handle importing', async () => { + const project = { key: 'new_project' }; + + (importGithubRepository as jest.Mock).mockResolvedValueOnce({ project }); + + const onProjectCreate = jest.fn(); + const wrapper = shallowRender({ onProjectCreate }); + + wrapper.instance().handleImportRepository(); + expect(importGithubRepository).not.toBeCalled(); + + const selectedOrganization = { key: 'org1', name: 'org1' }; + const selectedRepository = mockGitHubRepository(); + wrapper.setState({ + selectedOrganization, + selectedRepository + }); + + wrapper.instance().handleImportRepository(); + await waitAndUpdate(wrapper); + expect(importGithubRepository).toBeCalledWith( + 'a', + selectedOrganization.key, + selectedRepository.key + ); + expect(onProjectCreate).toBeCalledWith([project.key]); +}); + function shallowRender(props: Partial = {}) { return shallow( ); diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreateRenderer-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreateRenderer-test.tsx index b6a0ec8a26a..0bca0141708 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreateRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHubProjectCreateRenderer-test.tsx @@ -31,6 +31,7 @@ import GitHubProjectCreateRenderer, { it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ loadingBindings: true })).toMatchSnapshot('loading'); expect(shallowRender({ error: true })).toMatchSnapshot('error'); expect(shallowRender({ canAdmin: true, error: true })).toMatchSnapshot('error for admin'); @@ -64,11 +65,13 @@ it('should render correctly', () => { }); describe('callback', () => { + const onImportRepository = jest.fn(); const onSelectOrganization = jest.fn(); const onSelectRepository = jest.fn(); const onSearch = jest.fn(); const org = { key: 'o1', name: 'org' }; const wrapper = shallowRender({ + onImportRepository, onSelectOrganization, onSelectRepository, onSearch, @@ -83,7 +86,7 @@ describe('callback', () => { it('should be called when org is selected', () => { const value = 'o1'; - wrapper.find(SearchSelect).props().onSelect!({ value }); + wrapper.find(SearchSelect).simulate('select', { value }); expect(onSelectOrganization).toBeCalledWith(value); }); @@ -111,8 +114,11 @@ function shallowRender(props: Partial = {}) { - diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitHubProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitHubProjectCreateRenderer-test.tsx.snap index c97790e1755..f5deb0c4b9b 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitHubProjectCreateRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitHubProjectCreateRenderer-test.tsx.snap @@ -128,9 +128,33 @@ exports[`should render correctly: error for admin 1`] = ` `; +exports[`should render correctly: loading 1`] = ` + +`; + exports[`should render correctly: no repositories 1`] = `
+ + +
+ } title={ + + + + } title={
repository 1
@@ -318,7 +360,7 @@ exports[`should render correctly: repositories 1`] = `
repository 1
- onboarding.create_project.already_imported + onboarding.create_project.repository_imported
repository 1
-- cgit v1.2.3