BitbucketProject,
BitbucketRepository,
GithubOrganization,
- GithubRepository
+ GithubRepository,
+ GitlabProject
} from '../types/alm-integration';
import { ProjectBase } from './components';
export function getGithubRepositories(data: {
almSetting: string;
organization: string;
- ps: number;
- p?: number;
+ pageSize: number;
+ page?: number;
query?: string;
}): Promise<{ repositories: GithubRepository[]; paging: T.Paging }> {
- const { almSetting, organization, ps, p = 1, query } = data;
+ const { almSetting, organization, pageSize, page = 1, query } = data;
return getJSON('/api/alm_integrations/list_github_repositories', {
almSetting,
organization,
- p,
- ps,
+ p: page,
+ ps: pageSize,
q: query || undefined
}).catch(throwGlobalError);
}
+
+export function getGitlabProjects(data: {
+ almSetting: string;
+ page?: number;
+ pageSize?: number;
+ query?: string;
+}): Promise<{ projects: GitlabProject[]; projectsPaging: T.Paging }> {
+ const { almSetting, pageSize, page, query } = data;
+ return getJSON('/api/alm_integrations/search_gitlab_repos', {
+ almSetting,
+ projectName: query || undefined,
+ p: page,
+ ps: pageSize
+ })
+ .then(({ repositories, paging }) => ({ projects: repositories, projectsPaging: paging }))
+ .catch(throwGlobalError);
+}
loadingBindings={loading}
location={location}
onProjectCreate={this.handleProjectCreate}
+ router={router}
settings={gitlabSettings}
/>
);
const data = await getGithubRepositories({
almSetting: settings.key,
organization: organizationKey,
- ps: REPOSITORY_PAGE_SIZE,
- p: page,
+ pageSize: REPOSITORY_PAGE_SIZE,
+ page,
query
});
import { WithRouterProps } from 'react-router';
import {
checkPersonalAccessTokenIsValid,
+ getGitlabProjects,
setAlmPersonalAccessToken
} from '../../../api/alm-integrations';
+import { GitlabProject } from '../../../types/alm-integration';
import { AlmSettingsInstance } from '../../../types/alm-settings';
import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer';
-interface Props extends Pick<WithRouterProps, 'location'> {
+interface Props extends Pick<WithRouterProps, 'location' | 'router'> {
canAdmin: boolean;
loadingBindings: boolean;
onProjectCreate: (projectKeys: string[]) => void;
interface State {
loading: boolean;
+ loadingMore: boolean;
+ projects?: GitlabProject[];
+ projectsPaging: T.Paging;
submittingToken: boolean;
tokenIsValid: boolean;
tokenValidationFailed: boolean;
+ searching: boolean;
+ searchQuery: string;
settings?: AlmSettingsInstance;
}
+const GITLAB_PROJECTS_PAGESIZE = 30;
+
export default class GitlabProjectCreate extends React.PureComponent<Props, State> {
mounted = false;
constructor(props: Props) {
super(props);
+
this.state = {
loading: false,
+ loadingMore: false,
+ projectsPaging: { pageIndex: 1, total: 0, pageSize: GITLAB_PROJECTS_PAGESIZE },
tokenIsValid: false,
+ searching: false,
+ searchQuery: '',
settings: props.settings.length === 1 ? props.settings[0] : undefined,
submittingToken: false,
tokenValidationFailed: false
const tokenIsValid = await this.checkPersonalAccessToken();
+ let result;
+ if (tokenIsValid) {
+ result = await this.fetchProjects();
+ }
+
if (this.mounted) {
- this.setState({
- tokenIsValid,
- loading: false
- });
+ if (result) {
+ const { projects, projectsPaging } = result;
+
+ this.setState({
+ tokenIsValid,
+ loading: false,
+ projects,
+ projectsPaging
+ });
+ } else {
+ this.setState({
+ tokenIsValid,
+ loading: false
+ });
+ }
}
};
return checkPersonalAccessTokenIsValid(settings.key).catch(() => false);
};
- handlePersonalAccessTokenCreate = (token: string) => {
+ fetchProjects = (pageIndex = 1, query?: string) => {
+ const { settings } = this.state;
+
+ if (!settings) {
+ return Promise.resolve(undefined);
+ }
+
+ return getGitlabProjects({
+ almSetting: settings.key,
+ page: pageIndex,
+ pageSize: GITLAB_PROJECTS_PAGESIZE,
+ query
+ }).catch(() => undefined);
+ };
+
+ handleLoadMore = async () => {
+ this.setState({ loadingMore: true });
+
+ const {
+ projectsPaging: { pageIndex },
+ searchQuery
+ } = this.state;
+
+ const result = await this.fetchProjects(pageIndex + 1, searchQuery);
+
+ if (this.mounted) {
+ this.setState(({ projects = [], projectsPaging }) => ({
+ loadingMore: false,
+ projects: result ? [...projects, ...result.projects] : projects,
+ projectsPaging: result ? result.projectsPaging : projectsPaging
+ }));
+ }
+ };
+
+ handleSearch = async (searchQuery: string) => {
+ this.setState({ searching: true, searchQuery });
+
+ const result = await this.fetchProjects(1, searchQuery);
+
+ if (this.mounted) {
+ this.setState(({ projects, projectsPaging }) => ({
+ searching: false,
+ projects: result ? result.projects : projects,
+ projectsPaging: result ? result.projectsPaging : projectsPaging
+ }));
+ }
+ };
+
+ cleanUrl = () => {
+ const { location, router } = this.props;
+ delete location.query.resetPat;
+ router.replace(location);
+ };
+
+ handlePersonalAccessTokenCreate = async (token: string) => {
const { settings } = this.state;
if (!settings || token.length < 1) {
}
this.setState({ submittingToken: true, tokenValidationFailed: false });
- setAlmPersonalAccessToken(settings.key, token)
- .then(this.checkPersonalAccessToken)
- .then(patIsValid => {
- if (this.mounted) {
- this.setState({
- submittingToken: false,
- tokenIsValid: patIsValid,
- tokenValidationFailed: !patIsValid
- });
- if (patIsValid) {
- this.fetchInitialData();
- }
- }
- })
- .catch(() => {
- if (this.mounted) {
- this.setState({ submittingToken: false });
+
+ try {
+ await setAlmPersonalAccessToken(settings.key, token);
+
+ const patIsValid = await this.checkPersonalAccessToken();
+
+ if (this.mounted) {
+ this.setState({
+ submittingToken: false,
+ tokenIsValid: patIsValid,
+ tokenValidationFailed: !patIsValid
+ });
+
+ if (patIsValid) {
+ this.cleanUrl();
+ await this.fetchInitialData();
}
- });
+ }
+ } catch (e) {
+ if (this.mounted) {
+ this.setState({ submittingToken: false });
+ }
+ }
};
render() {
const { canAdmin, loadingBindings, location } = this.props;
- const { loading, tokenIsValid, settings, submittingToken, tokenValidationFailed } = this.state;
+ const {
+ loading,
+ loadingMore,
+ projects,
+ projectsPaging,
+ tokenIsValid,
+ searching,
+ searchQuery,
+ settings,
+ submittingToken,
+ tokenValidationFailed
+ } = this.state;
return (
<GitlabProjectCreateRenderer
settings={settings}
canAdmin={canAdmin}
loading={loading || loadingBindings}
+ loadingMore={loadingMore}
+ onLoadMore={this.handleLoadMore}
onPersonalAccessTokenCreate={this.handlePersonalAccessTokenCreate}
+ onSearch={this.handleSearch}
+ projects={projects}
+ projectsPaging={projectsPaging}
+ searching={searching}
+ searchQuery={searchQuery}
showPersonalAccessTokenForm={!tokenIsValid || Boolean(location.query.resetPat)}
submittingToken={submittingToken}
tokenValidationFailed={tokenValidationFailed}
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
+import { GitlabProject } from '../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import CreateProjectPageHeader from './CreateProjectPageHeader';
+import GitlabProjectSelectionForm from './GitlabProjectSelectionForm';
import PersonalAccessTokenForm from './PersonalAccessTokenForm';
import WrongBindingCountAlert from './WrongBindingCountAlert';
export interface GitlabProjectCreateRendererProps {
canAdmin?: boolean;
loading: boolean;
+ loadingMore: boolean;
+ onLoadMore: () => void;
onPersonalAccessTokenCreate: (pat: string) => void;
+ onSearch: (searchQuery: string) => void;
+ projects?: GitlabProject[];
+ projectsPaging: T.Paging;
+ searching: boolean;
+ searchQuery: string;
settings?: AlmSettingsInstance;
showPersonalAccessTokenForm?: boolean;
submittingToken?: boolean;
const {
canAdmin,
loading,
+ loadingMore,
+ projects,
+ projectsPaging,
+ searching,
+ searchQuery,
settings,
showPersonalAccessTokenForm,
submittingToken,
validationFailed={tokenValidationFailed}
/>
) : (
- <div>Token is valid!</div>
+ <GitlabProjectSelectionForm
+ loadingMore={loadingMore}
+ onLoadMore={props.onLoadMore}
+ onSearch={props.onSearch}
+ projects={projects}
+ projectsPaging={projectsPaging}
+ searching={searching}
+ searchQuery={searchQuery}
+ />
))}
</>
);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
+import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
+import SearchBox from 'sonar-ui-common/components/controls/SearchBox';
+import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
+import CheckIcon from 'sonar-ui-common/components/icons/CheckIcon';
+import DetachIcon from 'sonar-ui-common/components/icons/DetachIcon';
+import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getProjectUrl } from '../../../helpers/urls';
+import { GitlabProject } from '../../../types/alm-integration';
+import { ComponentQualifier } from '../../../types/component';
+import { CreateProjectModes } from './types';
+
+export interface GitlabProjectSelectionFormProps {
+ loadingMore: boolean;
+ onLoadMore: () => void;
+ onSearch: (searchQuery: string) => void;
+ projects?: GitlabProject[];
+ projectsPaging: T.Paging;
+ searching: boolean;
+ searchQuery: string;
+}
+
+export default function GitlabProjectSelectionForm(props: GitlabProjectSelectionFormProps) {
+ const { loadingMore, projects = [], projectsPaging, searching, searchQuery } = props;
+
+ if (projects.length === 0 && searchQuery.length === 0 && !searching) {
+ return (
+ <Alert className="spacer-top" variant="warning">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.create_project.gitlab.no_projects')}
+ id="onboarding.create_project.gitlab.no_projects"
+ values={{
+ link: (
+ <Link
+ to={{
+ pathname: '/projects/create',
+ query: { mode: CreateProjectModes.GitLab, resetPat: 1 }
+ }}>
+ {translate('onboarding.create_project.update_your_token')}
+ </Link>
+ )
+ }}
+ />
+ </Alert>
+ );
+ }
+
+ return (
+ <div className="boxed-group big-padded create-project-import-gitlab">
+ <SearchBox
+ className="spacer"
+ loading={searching}
+ minLength={3}
+ onChange={props.onSearch}
+ placeholder={translate('onboarding.create_project.gitlab.search_prompt')}
+ />
+
+ <hr />
+
+ {projects.length === 0 ? (
+ <div className="padded">{translate('no_results')}</div>
+ ) : (
+ <table className="data zebra zebra-hover">
+ <tbody>
+ {projects.map(project => (
+ <tr key={project.id}>
+ <td>
+ <Tooltip overlay={project.slug}>
+ <strong className="project-name display-inline-block text-ellipsis">
+ {project.name}
+ </strong>
+ </Tooltip>
+ <br />
+ <Tooltip overlay={project.pathSlug}>
+ <span className="text-muted project-path display-inline-block text-ellipsis">
+ {project.pathName}
+ </span>
+ </Tooltip>
+ </td>
+ <td>
+ <a
+ className="display-inline-flex-center big-spacer-right"
+ href={project.url}
+ rel="noopener noreferrer"
+ target="_blank">
+ <DetachIcon className="little-spacer-right" />
+ {translate('onboarding.create_project.gitlab.link')}
+ </a>
+ </td>
+ {project.sqProjectKey ? (
+ <>
+ <td>
+ <span className="display-flex-center display-flex-justify-end already-set-up">
+ <CheckIcon className="little-spacer-right" size={12} />
+ {translate('onboarding.create_project.repository_imported')}:
+ </span>
+ </td>
+ <td>
+ <div className="sq-project-link text-ellipsis">
+ <Link to={getProjectUrl(project.sqProjectKey)}>
+ <QualifierIcon
+ className="spacer-right"
+ qualifier={ComponentQualifier.Project}
+ />
+ {project.sqProjectName}
+ </Link>
+ </div>
+ </td>
+ </>
+ ) : (
+ <td colSpan={2}> </td>
+ )}
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ )}
+ <ListFooter
+ count={projects.length}
+ loadMore={props.onLoadMore}
+ loading={loadingMore}
+ total={projectsPaging.total}
+ />
+ </div>
+ );
+}
expect(getGithubRepositories).toBeCalledWith({
almSetting: 'a',
organization: 'o1',
- p: 1,
- ps: 30,
+ page: 1,
+ pageSize: 30,
query: 'query'
});
expect(wrapper.state().repositories).toEqual(repositories);
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import {
checkPersonalAccessTokenIsValid,
+ getGitlabProjects,
setAlmPersonalAccessToken
} from '../../../../api/alm-integrations';
+import { mockGitlabProject } from '../../../../helpers/mocks/alm-integrations';
import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings';
-import { mockLocation } from '../../../../helpers/testMocks';
+import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
import { AlmKeys } from '../../../../types/alm-settings';
import GitlabProjectCreate from '../GitlabProjectCreate';
jest.mock('../../../../api/alm-integrations', () => ({
checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue(true),
- setAlmPersonalAccessToken: jest.fn().mockResolvedValue(null)
+ setAlmPersonalAccessToken: jest.fn().mockResolvedValue(null),
+ getGitlabProjects: jest.fn().mockRejectedValue('error')
}));
beforeEach(jest.clearAllMocks);
});
describe('setting a new PAT', () => {
- const wrapper = shallowRender();
+ const routerReplace = jest.fn();
+ const wrapper = shallowRender({ router: mockRouter({ replace: routerReplace }) });
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
it('should correctly handle it if invalid', async () => {
(checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false);
expect(checkPersonalAccessTokenIsValid).toBeCalled();
expect(wrapper.state().submittingToken).toBe(false);
expect(wrapper.state().tokenValidationFailed).toBe(false);
+
+ expect(routerReplace).toBeCalled();
});
});
+it('should fetch more projects and preserve search', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true);
+
+ const projects = [
+ mockGitlabProject({ id: '1' }),
+ mockGitlabProject({ id: '2' }),
+ mockGitlabProject({ id: '3' }),
+ mockGitlabProject({ id: '4' }),
+ mockGitlabProject({ id: '5' }),
+ mockGitlabProject({ id: '6' })
+ ];
+ (getGitlabProjects as jest.Mock)
+ .mockResolvedValueOnce({
+ projects: projects.slice(0, 5),
+ projectsPaging: {
+ pageIndex: 1,
+ pageSize: 4,
+ total: 6
+ }
+ })
+ .mockResolvedValueOnce({
+ projects: projects.slice(5),
+ projectsPaging: {
+ pageIndex: 2,
+ pageSize: 4,
+ total: 6
+ }
+ });
+
+ const wrapper = shallowRender();
+
+ await waitAndUpdate(wrapper);
+ wrapper.setState({ searchQuery: 'query' });
+
+ wrapper.instance().handleLoadMore();
+ expect(wrapper.state().loadingMore).toBe(true);
+
+ await waitAndUpdate(wrapper);
+ expect(wrapper.state().loadingMore).toBe(false);
+ expect(wrapper.state().projects).toEqual(projects);
+
+ expect(getGitlabProjects).toBeCalledWith(expect.objectContaining({ query: 'query' }));
+});
+
+it('should search for projects', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true);
+
+ const projects = [
+ mockGitlabProject({ id: '1' }),
+ mockGitlabProject({ id: '2' }),
+ mockGitlabProject({ id: '3' }),
+ mockGitlabProject({ id: '4' }),
+ mockGitlabProject({ id: '5' }),
+ mockGitlabProject({ id: '6' })
+ ];
+ (getGitlabProjects as jest.Mock)
+ .mockResolvedValueOnce({
+ projects,
+ projectsPaging: {
+ pageIndex: 1,
+ pageSize: 6,
+ total: 6
+ }
+ })
+ .mockResolvedValueOnce({
+ projects: projects.slice(3, 5),
+ projectsPaging: {
+ pageIndex: 1,
+ pageSize: 6,
+ total: 2
+ }
+ });
+ const query = 'query';
+
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+
+ wrapper.instance().handleSearch(query);
+ expect(wrapper.state().searching).toBe(true);
+
+ await waitAndUpdate(wrapper);
+ expect(wrapper.state().searching).toBe(false);
+ expect(wrapper.state().searchQuery).toBe(query);
+ expect(wrapper.state().projects).toEqual([projects[3], projects[4]]);
+
+ expect(getGitlabProjects).toBeCalledWith(expect.objectContaining({ query }));
+});
+
function shallowRender(props: Partial<GitlabProjectCreate['props']> = {}) {
return shallow<GitlabProjectCreate>(
<GitlabProjectCreate
loadingBindings={false}
location={mockLocation()}
onProjectCreate={jest.fn()}
+ router={mockRouter()}
settings={[mockAlmSettingsInstance({ alm: AlmKeys.GitLab, key: almSettingKey })]}
{...props}
/>
'invalid settings, admin user'
);
expect(shallowRender()).toMatchSnapshot('pat form');
+ expect(shallowRender({ showPersonalAccessTokenForm: false })).toMatchSnapshot(
+ 'project selection form'
+ );
});
function shallowRender(props: Partial<GitlabProjectCreateRendererProps> = {}) {
<GitlabProjectCreateRenderer
canAdmin={false}
loading={false}
+ loadingMore={false}
+ onLoadMore={jest.fn()}
onPersonalAccessTokenCreate={jest.fn()}
+ onSearch={jest.fn()}
+ projects={undefined}
+ projectsPaging={{ pageIndex: 1, pageSize: 30, total: 0 }}
+ searching={false}
+ searchQuery=""
showPersonalAccessTokenForm={true}
submittingToken={false}
tokenValidationFailed={false}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockGitlabProject } from '../../../../helpers/mocks/alm-integrations';
+import GitlabProjectSelectionForm, {
+ GitlabProjectSelectionFormProps
+} from '../GitlabProjectSelectionForm';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('projects');
+
+ expect(shallowRender({ projects: undefined, projectsPaging: mockPaging() })).toMatchSnapshot(
+ 'undefined projects'
+ );
+ expect(shallowRender({ projects: [], projectsPaging: mockPaging() })).toMatchSnapshot(
+ 'no projects'
+ );
+ expect(
+ shallowRender({ projects: [], projectsPaging: mockPaging(), searchQuery: 'findme' })
+ ).toMatchSnapshot('no projects when searching');
+});
+
+function shallowRender(props: Partial<GitlabProjectSelectionFormProps> = {}) {
+ const projects = [
+ mockGitlabProject(),
+ mockGitlabProject({
+ id: '2',
+ sqProjectKey: 'already-imported',
+ sqProjectName: 'Already Imported'
+ })
+ ];
+
+ return shallow<GitlabProjectSelectionFormProps>(
+ <GitlabProjectSelectionForm
+ loadingMore={false}
+ onLoadMore={jest.fn()}
+ onSearch={jest.fn()}
+ projects={projects}
+ projectsPaging={mockPaging(projects.length)}
+ searching={false}
+ searchQuery=""
+ {...props}
+ />
+ );
+}
+
+function mockPaging(total = 0) {
+ return { total, pageIndex: 1, pageSize: 30 };
+}
}
}
onProjectCreate={[Function]}
+ router={
+ Object {
+ "createHref": [MockFunction],
+ "createPath": [MockFunction],
+ "go": [MockFunction],
+ "goBack": [MockFunction],
+ "goForward": [MockFunction],
+ "isActive": [MockFunction],
+ "push": [MockFunction],
+ "replace": [MockFunction],
+ "setRouteLeaveHook": [MockFunction],
+ }
+ }
settings={Array []}
/>
</div>
<GitlabProjectCreateRenderer
canAdmin={false}
loading={true}
+ loadingMore={false}
+ onLoadMore={[Function]}
onPersonalAccessTokenCreate={[Function]}
+ onSearch={[Function]}
+ projectsPaging={
+ Object {
+ "pageIndex": 1,
+ "pageSize": 30,
+ "total": 0,
+ }
+ }
+ searchQuery=""
+ searching={false}
settings={
Object {
"alm": "gitlab",
/>
</Fragment>
`;
+
+exports[`should render correctly: project selection form 1`] = `
+<Fragment>
+ <CreateProjectPageHeader
+ title={
+ <span
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="24"
+ src="/images/alm/gitlab.svg"
+ />
+ onboarding.create_project.gitlab.title
+ </span>
+ }
+ />
+ <GitlabProjectSelectionForm
+ loadingMore={false}
+ onLoadMore={[MockFunction]}
+ onSearch={[MockFunction]}
+ projectsPaging={
+ Object {
+ "pageIndex": 1,
+ "pageSize": 30,
+ "total": 0,
+ }
+ }
+ searchQuery=""
+ searching={false}
+ />
+</Fragment>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: no projects 1`] = `
+<Alert
+ className="spacer-top"
+ variant="warning"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.gitlab.no_projects"
+ id="onboarding.create_project.gitlab.no_projects"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects/create",
+ "query": Object {
+ "mode": "gitlab",
+ "resetPat": 1,
+ },
+ }
+ }
+ >
+ onboarding.create_project.update_your_token
+ </Link>,
+ }
+ }
+ />
+</Alert>
+`;
+
+exports[`should render correctly: no projects when searching 1`] = `
+<div
+ className="boxed-group big-padded create-project-import-gitlab"
+>
+ <SearchBox
+ className="spacer"
+ loading={false}
+ minLength={3}
+ onChange={[MockFunction]}
+ placeholder="onboarding.create_project.gitlab.search_prompt"
+ />
+ <hr />
+ <div
+ className="padded"
+ >
+ no_results
+ </div>
+ <ListFooter
+ count={0}
+ loadMore={[MockFunction]}
+ loading={false}
+ total={0}
+ />
+</div>
+`;
+
+exports[`should render correctly: projects 1`] = `
+<div
+ className="boxed-group big-padded create-project-import-gitlab"
+>
+ <SearchBox
+ className="spacer"
+ loading={false}
+ minLength={3}
+ onChange={[MockFunction]}
+ placeholder="onboarding.create_project.gitlab.search_prompt"
+ />
+ <hr />
+ <table
+ className="data zebra zebra-hover"
+ >
+ <tbody>
+ <tr
+ key="id1234"
+ >
+ <td>
+ <Tooltip
+ overlay="awesome-project-exclamation"
+ >
+ <strong
+ className="project-name display-inline-block text-ellipsis"
+ >
+ Awesome Project !
+ </strong>
+ </Tooltip>
+ <br />
+ <Tooltip
+ overlay="company/best-projects"
+ >
+ <span
+ className="text-muted project-path display-inline-block text-ellipsis"
+ >
+ Company / Best Projects
+ </span>
+ </Tooltip>
+ </td>
+ <td>
+ <a
+ className="display-inline-flex-center big-spacer-right"
+ href="https://gitlab.company.com/best-projects/awesome-project-exclamation"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ <DetachIcon
+ className="little-spacer-right"
+ />
+ onboarding.create_project.gitlab.link
+ </a>
+ </td>
+ <td
+ colSpan={2}
+ >
+
+ </td>
+ </tr>
+ <tr
+ key="2"
+ >
+ <td>
+ <Tooltip
+ overlay="awesome-project-exclamation"
+ >
+ <strong
+ className="project-name display-inline-block text-ellipsis"
+ >
+ Awesome Project !
+ </strong>
+ </Tooltip>
+ <br />
+ <Tooltip
+ overlay="company/best-projects"
+ >
+ <span
+ className="text-muted project-path display-inline-block text-ellipsis"
+ >
+ Company / Best Projects
+ </span>
+ </Tooltip>
+ </td>
+ <td>
+ <a
+ className="display-inline-flex-center big-spacer-right"
+ href="https://gitlab.company.com/best-projects/awesome-project-exclamation"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ <DetachIcon
+ className="little-spacer-right"
+ />
+ onboarding.create_project.gitlab.link
+ </a>
+ </td>
+ <td>
+ <span
+ className="display-flex-center display-flex-justify-end already-set-up"
+ >
+ <CheckIcon
+ className="little-spacer-right"
+ size={12}
+ />
+ onboarding.create_project.repository_imported
+ :
+ </span>
+ </td>
+ <td>
+ <div
+ className="sq-project-link text-ellipsis"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "already-imported",
+ },
+ }
+ }
+ >
+ <QualifierIcon
+ className="spacer-right"
+ qualifier="TRK"
+ />
+ Already Imported
+ </Link>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <ListFooter
+ count={2}
+ loadMore={[MockFunction]}
+ loading={false}
+ total={2}
+ />
+</div>
+`;
+
+exports[`should render correctly: undefined projects 1`] = `
+<Alert
+ className="spacer-top"
+ variant="warning"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.gitlab.no_projects"
+ id="onboarding.create_project.gitlab.no_projects"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects/create",
+ "query": Object {
+ "mode": "gitlab",
+ "resetPat": 1,
+ },
+ }
+ }
+ >
+ onboarding.create_project.update_your_token
+ </Link>,
+ }
+ }
+ />
+</Alert>
+`;
.create-project-github-repository .notice svg {
color: var(--green);
}
+
+.create-project-import-gitlab table > tbody > tr > td {
+ vertical-align: middle;
+}
+
+.create-project-import-gitlab .project-name,
+.create-project-import-gitlab .project-path {
+ max-width: 400px;
+}
+
+.create-project-import-gitlab .sq-project-link {
+ max-width: 300px;
+}
+
+.create-project-import-gitlab .already-set-up svg {
+ color: var(--green);
+}
import {
BitbucketProject,
BitbucketRepository,
- GithubRepository
+ GithubRepository,
+ GitlabProject
} from '../../types/alm-integration';
export function mockBitbucketProject(overrides: Partial<BitbucketProject> = {}): BitbucketProject {
...overrides
};
}
+
+export function mockGitlabProject(overrides: Partial<GitlabProject> = {}): GitlabProject {
+ return {
+ id: 'id1234',
+ name: 'Awesome Project !',
+ slug: 'awesome-project-exclamation',
+ pathName: 'Company / Best Projects',
+ pathSlug: 'company/best-projects',
+ sqProjectKey: '',
+ url: 'https://gitlab.company.com/best-projects/awesome-project-exclamation',
+ ...overrides
+ };
+}
url: string;
sqProjectKey: string;
}
+
+export interface GitlabProject {
+ id: string;
+ name: string;
+ pathName: string;
+ pathSlug: string;
+ sqProjectKey?: string;
+ sqProjectName?: string;
+ slug: string;
+ url: string;
+}
onboarding.create_project.github.no_orgs=We couldn't load any organizations with your key. Contact an administrator.
onboarding.create_project.github.no_orgs_admin=We couldn't load any organizations. Make sure the GitHub App is installed in at least one organization and check the GitHub instance configuration in the {link}.
onboarding.create_project.gitlab.title=Which GitLab project do you want to setup?
+onboarding.create_project.gitlab.no_projects=No projects could be fetched from Gitlab. Contact your system administrator, or {link}.
+onboarding.create_project.gitlab.link=See on GitLab
+onboarding.create_project.gitlab.search_prompt=Search for projects
onboarding.create_organization.page.header=Create Organization
onboarding.create_organization.page.description=An organization is a space where a team or a whole company can collaborate accross many projects.