Quellcode durchsuchen

UI: SONAR-9355 Create onboarding tutorial (#2137)

tags/6.5-M2
Stas Vilchik vor 7 Jahren
Ursprung
Commit
fbc932a882
83 geänderte Dateien mit 6178 neuen und 290 gelöschten Zeilen
  1. 2
    2
      it/it-tests/src/test/java/pageobjects/settings/SettingsPage.java
  2. 2
    1
      server/sonar-web/src/main/js/api/components.js
  3. 7
    3
      server/sonar-web/src/main/js/api/organizations.js
  4. 18
    7
      server/sonar-web/src/main/js/api/user-tokens.js
  5. 1
    1
      server/sonar-web/src/main/js/app/components/GlobalFooterForSonarQubeDotCom.js
  6. 118
    0
      server/sonar-web/src/main/js/app/components/help/GlobalHelp.js
  7. 50
    0
      server/sonar-web/src/main/js/app/components/help/LinksHelp.js
  8. 45
    0
      server/sonar-web/src/main/js/app/components/help/LinksHelpSonarCloud.js
  9. 135
    0
      server/sonar-web/src/main/js/app/components/help/ShortcutsHelp.js
  10. 34
    0
      server/sonar-web/src/main/js/app/components/help/TutorialsHelp.js
  11. 39
    0
      server/sonar-web/src/main/js/app/components/help/__tests__/GlobalHelp-test.js
  12. 13
    7
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
  13. 0
    188
      server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js
  14. 2
    0
      server/sonar-web/src/main/js/app/styles/boxed-group.css
  15. 2
    0
      server/sonar-web/src/main/js/app/utils/startReactApp.js
  16. 28
    0
      server/sonar-web/src/main/js/app/utils/throwGlobalError.js
  17. 6
    10
      server/sonar-web/src/main/js/apps/account/tokens-view.js
  18. 1
    2
      server/sonar-web/src/main/js/apps/projects-admin/CreateProjectForm.js
  19. 3
    3
      server/sonar-web/src/main/js/apps/settings/components/App.js
  20. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js
  21. 0
    53
      server/sonar-web/src/main/js/apps/settings/styles.css
  22. 181
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js
  23. 188
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js
  24. 157
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js
  25. 145
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js
  26. 109
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js
  27. 39
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js
  28. 240
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
  29. 145
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js
  30. 47
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js
  31. 180
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js
  32. 106
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js
  33. 56
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js
  34. 55
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js
  35. 63
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js
  36. 55
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js
  37. 38
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js
  38. 55
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js
  39. 687
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap
  40. 168
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap
  41. 219
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap
  42. 219
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap
  43. 48
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap
  44. 383
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap
  45. 58
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/BuildWrapper.js
  46. 76
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js
  47. 89
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Command.js
  48. 68
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js
  49. 59
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js
  50. 50
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js
  51. 46
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js
  52. 71
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js
  53. 64
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js
  54. 51
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js
  55. 29
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js
  56. 45
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js
  57. 27
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Command-test.js
  58. 32
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js
  59. 30
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js
  60. 30
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js
  61. 27
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js
  62. 30
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js
  63. 45
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js
  64. 29
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js
  65. 85
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap
  66. 148
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap
  67. 18
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap
  68. 99
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap
  69. 93
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap
  70. 67
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap
  71. 28
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap
  72. 109
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap
  73. 124
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Other-test.js.snap
  74. 82
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap
  75. 59
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css
  76. 31
    0
      server/sonar-web/src/main/js/apps/tutorials/routes.js
  77. 6
    10
      server/sonar-web/src/main/js/apps/users/tokens-view.js
  78. 8
    0
      server/sonar-web/src/main/js/helpers/testUtils.js
  79. 5
    0
      server/sonar-web/src/main/less/components/modals.less
  80. 86
    0
      server/sonar-web/src/main/less/components/side-tabs.less
  81. 1
    0
      server/sonar-web/src/main/less/init/type.less
  82. 1
    0
      server/sonar-web/src/main/less/sonar.less
  83. 82
    2
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 2
- 2
it/it-tests/src/test/java/pageobjects/settings/SettingsPage.java Datei anzeigen

@@ -35,7 +35,7 @@ public class SettingsPage {
}

public SettingsPage assertMenuContains(String categoryName) {
$(".settings-menu").$(By.linkText(categoryName)).shouldBe(visible);
$(".side-tabs-menu").$(By.linkText(categoryName)).shouldBe(visible);
return this;
}

@@ -50,7 +50,7 @@ public class SettingsPage {
}

public SettingsPage openCategory(String categoryName) {
$(".settings-menu").$(By.linkText(categoryName)).click();
$(".side-tabs-menu").$(By.linkText(categoryName)).click();
return this;
}


+ 2
- 1
server/sonar-web/src/main/js/api/components.js Datei anzeigen

@@ -19,6 +19,7 @@
*/
// @flow
import { getJSON, postJSON, post } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';

export function getComponents(data?: Object) {
const url = '/api/projects/search';
@@ -55,7 +56,7 @@ export function createProject(
}
) {
const url = '/api/projects/create';
return postJSON(url, data);
return postJSON(url, data).catch(throwGlobalError);
}

export function searchProjectTags(data?: { ps?: number, q?: string }) {

+ 7
- 3
server/sonar-web/src/main/js/api/organizations.js Datei anzeigen

@@ -20,6 +20,7 @@
// @flow
import { getJSON, post, postJSON } from '../helpers/request';
import type { Organization } from '../store/organizations/duck';
import throwGlobalError from '../app/utils/throwGlobalError';

export const getOrganizations = (organizations?: Array<string>) => {
const data = {};
@@ -44,7 +45,9 @@ type GetOrganizationNavigation = {
};

export const getOrganization = (key: string): Promise<GetOrganizationType> => {
return getOrganizations([key]).then(r => r.organizations.find(o => o.key === key));
return getOrganizations([key])
.then(r => r.organizations.find(o => o.key === key))
.catch(throwGlobalError);
};

export const getOrganizationNavigation = (key: string): Promise<GetOrganizationNavigation> => {
@@ -52,12 +55,13 @@ export const getOrganizationNavigation = (key: string): Promise<GetOrganizationN
};

export const createOrganization = (fields: {}): Promise<Organization> =>
postJSON('/api/organizations/create', fields).then(r => r.organization);
postJSON('/api/organizations/create', fields).then(r => r.organization, throwGlobalError);

export const updateOrganization = (key: string, changes: {}) =>
post('/api/organizations/update', { key, ...changes });

export const deleteOrganization = (key: string) => post('/api/organizations/delete', { key });
export const deleteOrganization = (key: string) =>
post('/api/organizations/delete', { key }).catch(throwGlobalError);

export const searchMembers = (
data: { organization?: string, p?: number, ps?: number, q?: string, selected?: string }

+ 18
- 7
server/sonar-web/src/main/js/api/user-tokens.js Datei anzeigen

@@ -17,14 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
import { getJSON, postJSON, post } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';

/**
* List tokens for given user login
* @param {string} login
* @returns {Promise}
*/
export function getTokens(login) {
export function getTokens(login: string) {
const url = '/api/user_tokens/search';
const data = { login };
return getJSON(url, data).then(r => r.userTokens);
@@ -36,10 +38,16 @@ export function getTokens(login) {
* @param {string} tokenName
* @returns {Promise}
*/
export function generateToken(userLogin, tokenName) {
export function generateToken(
tokenName: string,
userLogin?: string
): Promise<{ name: string, token: string }> {
const url = '/api/user_tokens/generate';
const data = { login: userLogin, name: tokenName };
return postJSON(url, data);
const data: { [string]: string } = { name: tokenName };
if (userLogin) {
data.login = userLogin;
}
return postJSON(url, data).catch(throwGlobalError);
}

/**
@@ -48,8 +56,11 @@ export function generateToken(userLogin, tokenName) {
* @param {string} tokenName
* @returns {Promise}
*/
export function revokeToken(userLogin, tokenName) {
export function revokeToken(tokenName: string, userLogin?: string) {
const url = '/api/user_tokens/revoke';
const data = { login: userLogin, name: tokenName };
return post(url, data);
const data: { [string]: string } = { name: tokenName };
if (userLogin) {
data.login = userLogin;
}
return post(url, data).catch(throwGlobalError);
}

+ 1
- 1
server/sonar-web/src/main/js/app/components/GlobalFooterForSonarQubeDotCom.js Datei anzeigen

@@ -43,7 +43,7 @@ export default function GlobalFooterForSonarQubeDotCom() {
{' - '}
<a href="https://about.sonarcloud.io/contact/">{translate('footer.help')}</a>
{' - '}
{<Link to="/about">{translate('footer.about')}</Link>}
<Link to="/about">{translate('footer.about')}</Link>
</div>
</div>
);

+ 118
- 0
server/sonar-web/src/main/js/app/components/help/GlobalHelp.js Datei anzeigen

@@ -0,0 +1,118 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Modal from 'react-modal';
import classNames from 'classnames';
import LinksHelp from './LinksHelp';
import LinksHelpSonarCloud from './LinksHelpSonarCloud';
import ShortcutsHelp from './ShortcutsHelp';
import TutorialsHelp from './TutorialsHelp';
import { translate } from '../../../helpers/l10n';

type Props = {
onClose: () => void,
sonarCloud?: boolean
};

type State = {
section: string
};

export default class GlobalHelp extends React.PureComponent {
props: Props;
state: State = { section: 'shortcuts' };

handleCloseClick = (event: Event) => {
event.preventDefault();
this.props.onClose();
};

handleSectionClick = (event: Event & { currentTarget: HTMLElement }) => {
event.preventDefault();
const { section } = event.currentTarget.dataset;
this.setState({ section });
};

renderSection = () => {
switch (this.state.section) {
case 'shortcuts':
return <ShortcutsHelp />;
case 'links':
return this.props.sonarCloud
? <LinksHelpSonarCloud onClose={this.props.onClose} />
: <LinksHelp onClose={this.props.onClose} />;
case 'tutorials':
return <TutorialsHelp onClose={this.props.onClose} />;
default:
return null;
}
};

renderMenuItem = (section: string) => (
<li key={section}>
<a
className={classNames({ active: section === this.state.section })}
data-section={section}
href="#"
onClick={this.handleSectionClick}>
{translate('help.section', section)}
</a>
</li>
);

renderMenu = () => (
<ul className="side-tabs-menu">
{['shortcuts', 'tutorials', 'links'].map(this.renderMenuItem)}
</ul>
);

render() {
return (
<Modal
isOpen={true}
contentLabel={translate('help')}
className="modal modal-medium"
overlayClassName="modal-overlay"
onRequestClose={this.props.onClose}>

<div className="modal-head">
<h2>{translate('help')}</h2>
</div>

<div className="side-tabs-layout">
<div className="side-tabs-side">
{this.renderMenu()}
</div>
<div className="side-tabs-main">
{this.renderSection()}
</div>
</div>

<div className="modal-foot">
<a className="js-modal-close" href="#" onClick={this.handleCloseClick}>
{translate('close')}
</a>
</div>

</Modal>
);
}
}

+ 50
- 0
server/sonar-web/src/main/js/app/components/help/LinksHelp.js Datei anzeigen

@@ -0,0 +1,50 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';

type Props = { onClose: () => void };

export default function LinksHelp({ onClose }: Props) {
return (
<div>
<h2 className="spacer-top spacer-bottom">{translate('help.section.links')}</h2>

<a href="http://www.sonarqube.org">{translate('footer.community')}</a>{' - '}
<a href="https://redirect.sonarsource.com/doc/home.html">
{translate('footer.documentation')}
</a>
{' - '}
<a href="https://redirect.sonarsource.com/doc/community.html">
{translate('footer.support')}
</a>
{' - '}
<a href="https://redirect.sonarsource.com/doc/plugin-library.html">
{translate('footer.plugins')}
</a>
{' - '}
<Link to="/web_api" onClick={onClose}>{translate('footer.web_api')}</Link>
{' - '}
<Link to="/about" onClick={onClose}>{translate('footer.about')}</Link>
</div>
);
}

+ 45
- 0
server/sonar-web/src/main/js/app/components/help/LinksHelpSonarCloud.js Datei anzeigen

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';

type Props = { onClose: () => void };

export default function LinksHelpSonarCloud({ onClose }: Props) {
return (
<div>
<h2 className="spacer-top spacer-bottom">{translate('help.section.links')}</h2>

<a href="https://about.sonarcloud.io/news/">{translate('footer.news')}</a>
{' - '}
<a href="https://about.sonarcloud.io/terms.pdf">{translate('footer.terms')}</a>
{' - '}
<a href="https://twitter.com/sonarqube">{translate('footer.twitter')}</a>
{' - '}
<a href="https://about.sonarcloud.io/get-started/">{translate('footer.get_started')}</a>
{' - '}
<a href="https://about.sonarcloud.io/contact/">{translate('footer.help')}</a>
{' - '}
<Link to="/about" onClick={onClose}>{translate('footer.about')}</Link>
</div>
);
}

+ 135
- 0
server/sonar-web/src/main/js/app/components/help/ShortcutsHelp.js Datei anzeigen

@@ -0,0 +1,135 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { translate } from '../../../helpers/l10n';

export default function ShortcutsHelp() {
return (
<div>
<h2 className="spacer-top spacer-bottom">{translate('help.section.shortcuts')}</h2>

<div className="columns">
<div className="column-half">
<div className="spacer-bottom">
<h3 className="shortcuts-section-title">{translate('shortcuts.section.global')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button spacer-right">s</span>
{translate('shortcuts.section.global.search')}
</li>
<li>
<span className="shortcut-button spacer-right">?</span>
{translate('shortcuts.section.global.shortcuts')}
</li>
</ul>
</div>

<h3 className="shortcuts-section-title">{translate('shortcuts.section.rules')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('shortcuts.section.rules.navigate_between_rules')}
</li>
<li>
<span className="shortcut-button spacer-right">→</span>
{translate('shortcuts.section.rules.open_details')}
</li>
<li>
<span className="shortcut-button spacer-right">←</span>
{translate('shortcuts.section.rules.return_to_list')}
</li>
<li>
<span className="shortcut-button spacer-right">a</span>
{translate('shortcuts.section.rules.activate')}
</li>
<li>
<span className="shortcut-button spacer-right">d</span>
{translate('shortcuts.section.rules.deactivate')}
</li>
</ul>
</div>

<div className="column-half">
<h3 className="shortcuts-section-title">{translate('shortcuts.section.issues')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('shortcuts.section.issues.navigate_between_issues')}
</li>
<li>
<span className="shortcut-button spacer-right">→</span>
{translate('shortcuts.section.issues.open_details')}
</li>
<li>
<span className="shortcut-button spacer-right">←</span>
{translate('shortcuts.section.issues.return_to_list')}
</li>
<li>
<span className="shortcut-button little-spacer-right">alt</span>
<span className="little-spacer-right">+</span>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('issues.to_navigate_issue_locations')}
</li>
<li>
<span className="shortcut-button little-spacer-right">alt</span>
<span className="little-spacer-right">+</span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button spacer-right">→</span>
{translate('issues.to_switch_flows')}
</li>
<li>
<span className="shortcut-button spacer-right">f</span>
{translate('shortcuts.section.issue.do_transition')}
</li>
<li>
<span className="shortcut-button spacer-right">a</span>
{translate('shortcuts.section.issue.assign')}
</li>
<li>
<span className="shortcut-button spacer-right">m</span>
{translate('shortcuts.section.issue.assign_to_me')}
</li>
<li>
<span className="shortcut-button spacer-right">i</span>
{translate('shortcuts.section.issue.change_severity')}
</li>
<li>
<span className="shortcut-button spacer-right">c</span>
{translate('shortcuts.section.issue.comment')}
</li>
<li>
<span className="shortcut-button little-spacer-right">ctrl</span>
<span className="shortcut-button spacer-right">enter</span>
{translate('shortcuts.section.issue.submit_comment')}
</li>
<li>
<span className="shortcut-button spacer-right">t</span>
{translate('shortcuts.section.issue.change_tags')}
</li>
</ul>
</div>
</div>
</div>
);
}

+ 34
- 0
server/sonar-web/src/main/js/app/components/help/TutorialsHelp.js Datei anzeigen

@@ -0,0 +1,34 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';

type Props = { onClose: () => void };

export default function TutorialsHelp({ onClose }: Props) {
return (
<div>
<h2 className="spacer-top spacer-bottom">{translate('help.section.tutorials')}</h2>
<Link to="/tutorials/onboarding" onClick={onClose}>Onboarding Tutorial</Link>
</div>
);
}

+ 39
- 0
server/sonar-web/src/main/js/app/components/help/__tests__/GlobalHelp-test.js Datei anzeigen

@@ -0,0 +1,39 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import GlobalHelp from '../GlobalHelp';
import { click } from '../../../../helpers/testUtils';

it('switches between tabs', () => {
const wrapper = shallow(<GlobalHelp onClose={jest.fn()} />);
expect(wrapper.find('ShortcutsHelp')).toHaveLength(1);
clickOnSection(wrapper, 'links');
expect(wrapper.find('LinksHelp')).toHaveLength(1);
clickOnSection(wrapper, 'tutorials');
expect(wrapper.find('TutorialsHelp')).toHaveLength(1);
clickOnSection(wrapper, 'shortcuts');
expect(wrapper.find('ShortcutsHelp')).toHaveLength(1);
});

function clickOnSection(wrapper: Object, section: string) {
click(wrapper.find(`[data-section="${section}"]`), { currentTarget: { dataset: { section } } });
}

+ 13
- 7
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js Datei anzeigen

@@ -23,8 +23,8 @@ import GlobalNavBranding from './GlobalNavBranding';
import GlobalNavMenu from './GlobalNavMenu';
import GlobalNavUserContainer from './GlobalNavUserContainer';
import Search from '../../search/Search';
import ShortcutsHelp from './ShortcutsHelp';
import { getCurrentUser, getAppState } from '../../../../store/rootReducer';
import GlobalHelp from '../../help/GlobalHelp';
import { getCurrentUser, getAppState, getSettingValue } from '../../../../store/rootReducer';

class GlobalNav extends React.PureComponent {
state = { helpOpen: false };
@@ -84,15 +84,21 @@ class GlobalNav extends React.PureComponent {
</ul>
</div>

{this.state.helpOpen && <ShortcutsHelp onClose={this.closeHelp} />}
{this.state.helpOpen &&
<GlobalHelp onClose={this.closeHelp} sonarCloud={this.props.sonarCloud} />}
</nav>
);
}
}

const mapStateToProps = state => ({
currentUser: getCurrentUser(state),
appState: getAppState(state)
});
const mapStateToProps = state => {
const sonarCloudSetting = getSettingValue(state, 'sonar.lf.sonarqube.com.enabled');

return {
currentUser: getCurrentUser(state),
appState: getAppState(state),
sonarCloud: sonarCloudSetting != null && sonarCloudSetting.value === 'true'
};
};

export default connect(mapStateToProps)(GlobalNav);

+ 0
- 188
server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js Datei anzeigen

@@ -1,188 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Modal from 'react-modal';
import { Link } from 'react-router';
import { translate } from '../../../../helpers/l10n';

type Props = {
onClose: () => void
};

export default class ShortcutsHelp extends React.PureComponent {
props: Props;

handleCloseClick = (event: Event) => {
event.preventDefault();
this.props.onClose();
};

render() {
return (
<Modal
isOpen={true}
contentLabel="shortcuts help"
className="modal modal-large"
overlayClassName="modal-overlay"
onRequestClose={this.props.onClose}>

<div className="modal-head">
<h2>{translate('help')}</h2>
</div>

<div className="modal-body modal-container">
<div className="spacer-bottom">
<a href="http://www.sonarqube.org">{translate('footer.community')}</a>{' - '}
<a href="https://redirect.sonarsource.com/doc/home.html">
{translate('footer.documentation')}
</a>
{' - '}
<a href="https://redirect.sonarsource.com/doc/community.html">
{translate('footer.support')}
</a>
{' - '}
<a href="https://redirect.sonarsource.com/doc/plugin-library.html">
{translate('footer.plugins')}
</a>
{' - '}
<Link to="/web_api" onClick={this.props.onClose}>{translate('footer.web_api')}</Link>
{' - '}
<Link to="/about" onClick={this.props.onClose}>{translate('footer.about')}</Link>
</div>

<h2 className="spacer-top spacer-bottom">{translate('shortcuts.modal_title')}</h2>

<div className="columns">
<div className="column-half">
<div className="spacer-bottom">
<h3 className="shortcuts-section-title">{translate('shortcuts.section.global')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button spacer-right">s</span>
{translate('shortcuts.section.global.search')}
</li>
<li>
<span className="shortcut-button spacer-right">?</span>
{translate('shortcuts.section.global.shortcuts')}
</li>
</ul>
</div>

<h3 className="shortcuts-section-title">{translate('shortcuts.section.rules')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('shortcuts.section.rules.navigate_between_rules')}
</li>
<li>
<span className="shortcut-button spacer-right">→</span>
{translate('shortcuts.section.rules.open_details')}
</li>
<li>
<span className="shortcut-button spacer-right">←</span>
{translate('shortcuts.section.rules.return_to_list')}
</li>
<li>
<span className="shortcut-button spacer-right">a</span>
{translate('shortcuts.section.rules.activate')}
</li>
<li>
<span className="shortcut-button spacer-right">d</span>
{translate('shortcuts.section.rules.deactivate')}
</li>
</ul>
</div>

<div className="column-half">
<h3 className="shortcuts-section-title">{translate('shortcuts.section.issues')}</h3>
<ul className="shortcuts-list">
<li>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('shortcuts.section.issues.navigate_between_issues')}
</li>
<li>
<span className="shortcut-button spacer-right">→</span>
{translate('shortcuts.section.issues.open_details')}
</li>
<li>
<span className="shortcut-button spacer-right">←</span>
{translate('shortcuts.section.issues.return_to_list')}
</li>
<li>
<span className="shortcut-button little-spacer-right">alt</span>
<span className="little-spacer-right">+</span>
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button spacer-right">↓</span>
{translate('issues.to_navigate_issue_locations')}
</li>
<li>
<span className="shortcut-button little-spacer-right">alt</span>
<span className="little-spacer-right">+</span>
<span className="shortcut-button little-spacer-right">←</span>
<span className="shortcut-button spacer-right">→</span>
{translate('issues.to_switch_flows')}
</li>
<li>
<span className="shortcut-button spacer-right">f</span>
{translate('shortcuts.section.issue.do_transition')}
</li>
<li>
<span className="shortcut-button spacer-right">a</span>
{translate('shortcuts.section.issue.assign')}
</li>
<li>
<span className="shortcut-button spacer-right">m</span>
{translate('shortcuts.section.issue.assign_to_me')}
</li>
<li>
<span className="shortcut-button spacer-right">i</span>
{translate('shortcuts.section.issue.change_severity')}
</li>
<li>
<span className="shortcut-button spacer-right">c</span>
{translate('shortcuts.section.issue.comment')}
</li>
<li>
<span className="shortcut-button little-spacer-right">ctrl</span>
<span className="shortcut-button spacer-right">enter</span>
{translate('shortcuts.section.issue.submit_comment')}
</li>
<li>
<span className="shortcut-button spacer-right">t</span>
{translate('shortcuts.section.issue.change_tags')}
</li>
</ul>
</div>
</div>
</div>

<div className="modal-foot">
<a className="js-modal-close" href="#" onClick={this.handleCloseClick}>
{translate('close')}
</a>
</div>

</Modal>
);
}
}

+ 2
- 0
server/sonar-web/src/main/js/app/styles/boxed-group.css Datei anzeigen

@@ -34,6 +34,8 @@
}

.boxed-group-actions {
position: relative;
z-index: 12;
float: right;
margin-top: 15px;
margin-right: 20px;

+ 2
- 0
server/sonar-web/src/main/js/app/utils/startReactApp.js Datei anzeigen

@@ -63,6 +63,7 @@ import qualityProfilesRoutes from '../../apps/quality-profiles/routes';
import sessionsRoutes from '../../apps/sessions/routes';
import settingsRoutes from '../../apps/settings/routes';
import systemRoutes from '../../apps/system/routes';
import tutorialRoutes from '../../apps/tutorials/routes';
import updateCenterRoutes from '../../apps/update-center/routes';
import usersRoutes from '../../apps/users/routes';
import webAPIRoutes from '../../apps/web-api/routes';
@@ -159,6 +160,7 @@ const startReactApp = () => {
<Route path="quality_gates" childRoutes={qualityGatesRoutes} />
<Route path="portfolios" component={PortfoliosPage} />
<Route path="profiles" childRoutes={qualityProfilesRoutes} />
<Route path="tutorials" childRoutes={tutorialRoutes} />
<Route path="web_api" childRoutes={webAPIRoutes} />

<Route component={ProjectContainer}>

+ 28
- 0
server/sonar-web/src/main/js/app/utils/throwGlobalError.js Datei anzeigen

@@ -0,0 +1,28 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import getStore from './getStore';
import { onFail } from '../../store/rootActions';

export default function throwGlobalError(error: Object) {
const store = getStore();
onFail(store.dispatch)(error);
return Promise.reject();
}

+ 6
- 10
server/sonar-web/src/main/js/apps/account/tokens-view.js Datei anzeigen

@@ -52,17 +52,13 @@ export default Marionette.ItemView.extend({
this.errors = [];
this.newToken = null;
const tokenName = this.$('.js-generate-token-form input').val();
generateToken(this.model.id, tokenName)
.then(response => {
generateToken(tokenName, this.model.id).then(
response => {
this.newToken = response;
this.requestTokens();
})
.catch(error => {
error.response.json().then(response => {
this.errors = response.errors;
this.render();
});
});
},
() => {}
);
},

onRevokeTokenFormSubmit(e) {
@@ -71,7 +67,7 @@ export default Marionette.ItemView.extend({
const token = this.tokens.find(token => token.name === `${tokenName}`);
if (token) {
if (token.deleting) {
revokeToken(this.model.id, tokenName).then(this.requestTokens.bind(this));
revokeToken(tokenName, this.model.id).then(this.requestTokens.bind(this), () => {});
} else {
token.deleting = true;
this.render();

+ 1
- 2
server/sonar-web/src/main/js/apps/projects-admin/CreateProjectForm.js Datei anzeigen

@@ -103,10 +103,9 @@ export default class CreateProjectForm extends React.PureComponent {
this.props.onProjectCreated();
}
},
error => {
() => {
if (this.mounted) {
this.setState({ loading: false });
this.props.onRequestFail(error);
}
}
);

+ 3
- 3
server/sonar-web/src/main/js/apps/settings/components/App.js Datei anzeigen

@@ -83,15 +83,15 @@ class App extends React.PureComponent {
<Helmet title={translate('settings.page')} />

<PageHeader component={this.props.component} />
<div className="settings-layout">
<div className="settings-side">
<div className="side-tabs-layout settings-layout">
<div className="side-tabs-side">
<AllCategoriesList
component={this.props.component}
selectedCategory={selectedCategory}
defaultCategory={this.props.defaultCategory}
/>
</div>
<div className="settings-main">
<div className="side-tabs-main">
<CategoryDefinitionsList component={this.props.component} category={selectedCategory} />

{selectedCategory === 'exclusions' && <WildcardsHelp />}

+ 1
- 1
server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js Datei anzeigen

@@ -70,7 +70,7 @@ export default class CategoriesList extends React.PureComponent {
const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase());

return (
<ul className="settings-menu">
<ul className="side-tabs-menu">
{sortedCategories.map(category => (
<li key={category.key}>
{this.renderLink(category)}

+ 0
- 53
server/sonar-web/src/main/js/apps/settings/styles.css Datei anzeigen

@@ -1,60 +1,7 @@
.settings-layout {
display: flex;
justify-content: space-between;
align-items: stretch;
margin-bottom: 60px;
}

.settings-main {
position: relative;
z-index: 2;
flex-grow: 1;
padding: 15px 20px;
border: 1px solid #e6e6e6;
box-sizing: border-box;
background-color: #fff;
}

.settings-side {
position: relative;
z-index: 3;
width: 160px;
flex-shrink: 0;
padding: 10px 0;
box-sizing: border-box;
transform: translateX(1px);
}

.settings-menu {}

.settings-menu > li {
margin-bottom: 4px;
}

.settings-menu > li > a {
display: block;
padding: 10px 10px;
line-height: 1.5;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
border: 1px solid #e6e6e6;
border-right: none;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.3s ease, background-color 0.3s ease;
}

.settings-menu > li > a:hover,
.settings-menu > li > a:focus,
.settings-menu > li > a.active {
background-color: #fff;
}

.settings-menu > li > a.active {
color: #444;
cursor: default;
}

.settings-definitions-list > li + li {
margin-top: 30px;
}

+ 181
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/AnalysisStep.js Datei anzeigen

@@ -0,0 +1,181 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Step from './Step';
import LanguageStep from './LanguageStep';
import type { Result } from './LanguageStep';
import JavaMaven from './commands/JavaMaven';
import JavaGradle from './commands/JavaGradle';
import DotNet from './commands/DotNet';
import Msvc from './commands/Msvc';
import ClangGCC from './commands/ClangGCC';
import Other from './commands/Other';
import { translate } from '../../../helpers/l10n';

type Props = {|
open: boolean,
organization?: string,
sonarCloud: boolean,
stepNumber: number,
token: string
|};

type State = {
result?: Result
};

export default class AnalysisStep extends React.PureComponent {
props: Props;
state: State = {};

handleLanguageSelect = (result?: Result) => {
this.setState({ result });
};

handleLanguageReset = () => {
this.setState({ result: undefined });
};

getHost = () => window.location.origin + window.baseUrl;

renderForm = () => {
return (
<div className="boxed-group-inner">
<div className="flex-columns">
<div className="flex-column flex-column-half bordered-right">
<LanguageStep
onDone={this.handleLanguageSelect}
onReset={this.handleLanguageReset}
sonarCloud={this.props.sonarCloud}
/>
</div>
<div className="flex-column flex-column-half">
{this.renderCommand()}
</div>
</div>
</div>
);
};

renderFormattedCommand = (...lines: Array<string>) => (
<pre>{lines.join(' ' + '\\' + '\n' + ' ')}</pre>
);

renderCommand = () => {
const { result } = this.state;

if (!result) {
return null;
}

if (result.language === 'java') {
return result.javaBuild === 'maven'
? this.renderCommandForMaven()
: this.renderCommandForGradle();
} else if (result.language === 'dotnet') {
return this.renderCommandForDotNet();
} else if (result.language === 'c-family') {
return result.cFamilyCompiler === 'msvc'
? this.renderCommandForMSVC()
: this.renderCommandForClangGCC();
} else {
return this.renderCommandForOther();
}
};

renderCommandForMaven = () => (
<JavaMaven
host={this.getHost()}
organization={this.props.organization}
token={this.props.token}
/>
);

renderCommandForGradle = () => (
<JavaGradle
host={this.getHost()}
organization={this.props.organization}
token={this.props.token}
/>
);

renderCommandForDotNet = () => {
return (
<DotNet
host={this.getHost()}
organization={this.props.organization}
// $FlowFixMe
projectKey={this.state.result.projectKey}
token={this.props.token}
/>
);
};

renderCommandForMSVC = () => {
return (
<Msvc
host={this.getHost()}
organization={this.props.organization}
// $FlowFixMe
projectKey={this.state.result.projectKey}
token={this.props.token}
/>
);
};

renderCommandForClangGCC = () => (
<ClangGCC
host={this.getHost()}
organization={this.props.organization}
// $FlowFixMe
os={this.state.result.os}
// $FlowFixMe
projectKey={this.state.result.projectKey}
token={this.props.token}
/>
);

renderCommandForOther = () => (
<Other
host={this.getHost()}
organization={this.props.organization}
// $FlowFixMe
os={this.state.result.os}
// $FlowFixMe
projectKey={this.state.result.projectKey}
token={this.props.token}
/>
);

renderResult = () => null;

render() {
return (
<Step
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
stepNumber={this.props.stepNumber}
stepTitle={translate('onboarding.analysis.header')}
/>
);
}
}

+ 188
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/LanguageStep.js Datei anzeigen

@@ -0,0 +1,188 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import NewProjectForm from './NewProjectForm';
import RadioToggle from '../../../components/controls/RadioToggle';
import { translate } from '../../../helpers/l10n';

type Props = {|
onDone: (result: Result) => void,
onReset: () => void,
organization?: string,
sonarCloud: boolean
|};

type State = {
language?: string,
javaBuild?: string,
cFamilyCompiler?: string,
os?: string,
projectKey?: string
};

export type Result = State;

export default class LanguageStep extends React.PureComponent {
props: Props;

static defaultProps = { sonarCloud: false };

state: State = {};

isConfigured = () => {
const { language, javaBuild, cFamilyCompiler, os, projectKey } = this.state;
const isJavaConfigured = language === 'java' && javaBuild != null;
const isDotNetConfigured = language === 'dotnet' && projectKey != null;
const isCFamilyConfigured =
language === 'c-family' && (cFamilyCompiler === 'msvc' || os != null) && projectKey != null;
const isOtherConfigured = language === 'other' && projectKey != null;

return isJavaConfigured || isDotNetConfigured || isCFamilyConfigured || isOtherConfigured;
};

handleChange = () => {
if (this.isConfigured()) {
this.props.onDone(this.state);
} else {
this.props.onReset();
}
};

handleLanguageChange = (language: string) => {
this.setState({ language }, this.handleChange);
};

handleJavaBuildChange = (javaBuild: string) => {
this.setState({ javaBuild }, this.handleChange);
};

handleCFamilyCompilerChange = (cFamilyCompiler: string) => {
this.setState({ cFamilyCompiler }, this.handleChange);
};

handleOSChange = (os: string) => {
this.setState({ os }, this.handleChange);
};

handleProjectKeyDone = (projectKey: string) => {
this.setState({ projectKey }, this.handleChange);
};

handleProjectKeyDelete = () => {
this.setState({ projectKey: undefined }, this.handleChange);
};

renderJavaBuild = () => (
<div className="big-spacer-top">
<h4 className="spacer-bottom">
{translate('onboarding.language.java.build_technology')}
</h4>
<RadioToggle
name="java-build"
onCheck={this.handleJavaBuildChange}
options={['maven', 'gradle'].map(build => ({
label: translate('onboarding.language.java.build_technology', build),
value: build
}))}
value={this.state.javaBuild}
/>
</div>
);

renderCFamilyCompiler = () => (
<div className="big-spacer-top">
<h4 className="spacer-bottom">
{translate('onboarding.language.c-family.compiler')}
</h4>
<RadioToggle
name="c-family-compiler"
onCheck={this.handleCFamilyCompilerChange}
options={['msvc', 'clang-gcc'].map(compiler => ({
label: translate('onboarding.language.c-family.compiler', compiler),
value: compiler
}))}
value={this.state.cFamilyCompiler}
/>
</div>
);

renderOS = () => (
<div className="big-spacer-top">
<h4 className="spacer-bottom">
{translate('onboarding.language.os')}
</h4>
<RadioToggle
name="os"
onCheck={this.handleOSChange}
options={['linux', 'win', 'mac'].map(os => ({
label: translate('onboarding.language.os', os),
value: os
}))}
value={this.state.os}
/>
</div>
);

renderProjectKey = () => (
<NewProjectForm
onDelete={this.handleProjectKeyDelete}
onDone={this.handleProjectKeyDone}
organization={this.props.organization}
projectKey={this.state.projectKey}
/>
);

render() {
const shouldAskProjectKey =
this.state.language === 'dotnet' ||
(this.state.language === 'c-family' &&
(this.state.cFamilyCompiler === 'msvc' ||
(this.state.cFamilyCompiler === 'clang-gcc' && this.state.os != null))) ||
(this.state.language === 'other' && this.state.os !== undefined);

const languages = this.props.sonarCloud
? ['java', 'dotnet', 'c-family', 'other']
: ['java', 'dotnet', 'other'];

return (
<div>
<div>
<h4 className="spacer-bottom">{translate('onboarding.language')}</h4>
<RadioToggle
name="language"
onCheck={this.handleLanguageChange}
options={languages.map(language => ({
label: translate('onboarding.language', language),
value: language
}))}
value={this.state.language}
/>
</div>
{this.state.language === 'java' && this.renderJavaBuild()}
{this.state.language === 'c-family' && this.renderCFamilyCompiler()}
{((this.state.language === 'c-family' && this.state.cFamilyCompiler === 'clang-gcc') ||
this.state.language === 'other') &&
this.renderOS()}
{shouldAskProjectKey && this.renderProjectKey()}
</div>
);
}
}

+ 157
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/NewOrganizationForm.js Datei anzeigen

@@ -0,0 +1,157 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { debounce } from 'lodash';
import {
createOrganization,
deleteOrganization,
getOrganization
} from '../../../api/organizations';
import { translate } from '../../../helpers/l10n';

type Props = {|
onDelete: () => void,
onDone: (organization: string) => void,
organization?: string
|};

type State = {
done: boolean,
loading: boolean,
organization: string,
unique: boolean
};

export default class NewOrganizationForm extends React.PureComponent {
mounted: boolean;
props: Props;
state: State;

constructor(props: Props) {
super(props);
this.state = {
done: props.organization != null,
loading: false,
organization: props.organization || '',
unique: true
};
this.validateOrganization = debounce(this.validateOrganization, 500);
}

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

stopLoading = () => {
if (this.mounted) {
this.setState({ loading: false });
}
};

validateOrganization = (organization: string) => {
getOrganization(organization).then(response => {
if (this.mounted) {
this.setState({ unique: response == null });
}
});
};

sanitizeOrganization = (organization: string) =>
organization.toLowerCase().replace(/[^a-z0-9-]/, '').replace(/^-/, '');

handleOrganizationChange = (event: { target: HTMLInputElement }) => {
const organization = this.sanitizeOrganization(event.target.value);
this.setState({ organization });
this.validateOrganization(organization);
};

handleOrganizationCreate = (event: Event) => {
event.preventDefault();
const { organization } = this.state;
if (organization) {
this.setState({ loading: true });
createOrganization({ key: organization, name: organization }).then(() => {
if (this.mounted) {
this.setState({ done: true, loading: false });
this.props.onDone(organization);
}
}, this.stopLoading);
}
};

handleOrganizationDelete = (event: Event) => {
event.preventDefault();
const { organization } = this.state;
if (organization) {
this.setState({ loading: true });
deleteOrganization(organization).then(() => {
if (this.mounted) {
this.setState({ done: false, loading: false, organization: '' });
this.props.onDelete();
}
}, this.stopLoading);
}
};

render() {
const { done, loading, organization, unique } = this.state;

const valid = unique && organization.length >= 2;

return done
? <form onSubmit={this.handleOrganizationDelete}>
<span className="spacer-right text-middle">{organization}</span>
{loading
? <i className="spinner" />
: <button className="button-clean">
<i className="icon-delete" />
</button>}
</form>
: <form onSubmit={this.handleOrganizationCreate}>
<input
autoFocus={true}
className="input-super-large spacer-right text-middle"
onChange={this.handleOrganizationChange}
maxLength={32}
minLength={2}
placeholder={translate('onboarding.organization.placeholder')}
required={true}
type="text"
value={organization}
/>
{loading
? <i className="spinner" />
: <button className="text-middle" disabled={!valid}>{translate('create')}</button>}
{!unique &&
<span className="big-spacer-left text-danger text-middle">
<i className="icon-alert-error little-spacer-right text-text-top" />
{translate('this_name_is_already_taken')}
</span>}
<div className="note spacer-top abs-width-300">
{translate('onboarding.organization.key_requirement')}
</div>
</form>;
}
}

+ 145
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/NewProjectForm.js Datei anzeigen

@@ -0,0 +1,145 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { createProject, deleteProject } from '../../../api/components';
import { translate } from '../../../helpers/l10n';

type Props = {|
onDelete: () => void,
onDone: (projectKey: string) => void,
organization?: string,
projectKey?: string
|};

type State = {
done: boolean,
loading: boolean,
projectKey: string
};

export default class NewProjectForm extends React.PureComponent {
mounted: boolean;
props: Props;
state: State;

constructor(props: Props) {
super(props);
this.state = {
done: props.projectKey != null,
loading: false,
projectKey: props.projectKey || ''
};
}

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

stopLoading = () => {
if (this.mounted) {
this.setState({ loading: false });
}
};

sanitizeProjectKey = (projectKey: string) => projectKey.replace(/[^a-zA-Z0-9-_\.:]/, '');

handleProjectKeyChange = (event: { target: HTMLInputElement }) => {
this.setState({ projectKey: this.sanitizeProjectKey(event.target.value) });
};

handleProjectCreate = (event: Event) => {
event.preventDefault();
const { projectKey } = this.state;
const data: { [string]: string } = {
name: projectKey,
project: projectKey
};
if (this.props.organization) {
data.organization = this.props.organization;
}
this.setState({ loading: true });
createProject(data).then(() => {
if (this.mounted) {
this.setState({ done: true, loading: false });
this.props.onDone(projectKey);
}
}, this.stopLoading);
};

handleProjectDelete = (event: Event) => {
event.preventDefault();
const { projectKey } = this.state;
this.setState({ loading: true });
deleteProject(projectKey).then(() => {
if (this.mounted) {
this.setState({ done: false, loading: false, projectKey: '' });
this.props.onDelete();
}
}, this.stopLoading);
};

render() {
const { done, loading, projectKey } = this.state;

const valid = projectKey.length > 0;

const form = done
? <form onSubmit={this.handleProjectDelete}>
<span className="spacer-right text-middle">{projectKey}</span>
{loading
? <i className="spinner" />
: <button className="button-clean">
<i className="icon-delete" />
</button>}
</form>
: <form onSubmit={this.handleProjectCreate}>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
minLength={1}
maxLength={400}
onChange={this.handleProjectKeyChange}
required={true}
type="text"
value={projectKey}
/>
{loading
? <i className="spinner" />
: <button className="text-middle" disabled={!valid}>{translate('Done')}</button>}
<div className="note spacer-top abs-width-300">
{translate('onboarding.project_key_requirement')}
</div>
</form>;

return (
<div className="big-spacer-top">
<h4 className="spacer-bottom">
{translate('onboarding.language.project_key')}
</h4>
{form}
</div>
);
}
}

+ 109
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/Onboarding.js Datei anzeigen

@@ -0,0 +1,109 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import TokenStep from './TokenStep';
import OrganizationStep from './OrganizationStep';
import AnalysisStep from './AnalysisStep';
import { translate } from '../../../helpers/l10n';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
import './styles.css';

type Props = {
currentUser: { login: string, isLoggedIn: boolean },
organizationsEnabled: boolean,
sonarCloud: boolean
};

type State = {
organization?: string,
step: string,
token?: string
};

export default class Onboarding extends React.PureComponent {
props: Props;
state: State;

constructor(props: Props) {
super(props);
this.state = { step: props.organizationsEnabled ? 'organization' : 'token' };
}

componentDidMount() {
if (!this.props.currentUser.isLoggedIn) {
handleRequiredAuthentication();
}
}

handleTokenDone = (token: string) => {
this.setState({ step: 'analysis', token });
};

handleOrganizationDone = (organization: string) => {
this.setState({ organization, step: 'token' });
};

render() {
if (!this.props.currentUser.isLoggedIn) {
return null;
}

const { organizationsEnabled, sonarCloud } = this.props;
const { step, token } = this.state;

let stepNumber = 1;

return (
<div className="page page-limited">
<header className="page-header">
<h1 className="page-title">
{translate(sonarCloud ? 'onboarding.header.sonarcloud' : 'onboarding.header')}
</h1>
<div className="page-description">
{translate('onboarding.header.description')}
</div>
</header>

{organizationsEnabled &&
<OrganizationStep
currentUser={this.props.currentUser}
onContinue={this.handleOrganizationDone}
open={step === 'organization'}
stepNumber={stepNumber++}
/>}

<TokenStep
onContinue={this.handleTokenDone}
open={step === 'token'}
stepNumber={stepNumber++}
/>

<AnalysisStep
organization={this.state.organization}
open={step === 'analysis'}
sonarCloud={sonarCloud}
stepNumber={stepNumber}
token={token}
/>
</div>
);
}
}

+ 39
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingContainer.js Datei anzeigen

@@ -0,0 +1,39 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import { connect } from 'react-redux';
import Onboarding from './Onboarding';
import {
getCurrentUser,
areThereCustomOrganizations,
getSettingValue
} from '../../../store/rootReducer';

const mapStateToProps = state => {
const sonarCloudSetting = getSettingValue(state, 'sonar.lf.sonarqube.com.enabled');

return {
currentUser: getCurrentUser(state),
organizationsEnabled: areThereCustomOrganizations(state),
sonarCloud: sonarCloudSetting != null && sonarCloudSetting.value === 'true'
};
};

export default connect(mapStateToProps)(Onboarding);

+ 240
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js Datei anzeigen

@@ -0,0 +1,240 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Select from 'react-select';
import classNames from 'classnames';
import { sortBy } from 'lodash';
import Step from './Step';
import NewOrganizationForm from './NewOrganizationForm';
import { getMyOrganizations } from '../../../api/organizations';
import { translate } from '../../../helpers/l10n';

type Props = {
currentUser: { login: string, isLoggedIn: boolean },
open: boolean,
onContinue: (organization: string) => void
};

type State = {
loading: boolean,
newOrganization?: string,
existingOrganization?: string,
existingOrganizations: Array<string>,
selection: 'personal' | 'existing' | 'new'
};

export default class OrganizationStep extends React.PureComponent {
mounted: boolean;
props: Props;
state: State = {
loading: true,
existingOrganizations: [],
selection: 'personal'
};

componentDidMount() {
this.mounted = true;
this.fetchOrganizations();
}

componentWillUnmount() {
this.mounted = false;
}

fetchOrganizations = () => {
getMyOrganizations().then(
organizations => {
if (this.mounted) {
this.setState({
loading: false,
existingOrganizations: sortBy(
organizations.filter(organization => organization !== this.props.currentUser.login)
)
});
}
},
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
);
};

getSelectedOrganization = () => {
switch (this.state.selection) {
case 'personal':
return this.props.currentUser.login;
case 'existing':
return this.state.existingOrganization;
case 'new':
return this.state.newOrganization;
default:
return null;
}
};

handlePersonalClick = (event: Event) => {
event.preventDefault();
this.setState({ selection: 'personal' });
};

handleExistingClick = (event: Event) => {
event.preventDefault();
this.setState({ selection: 'existing' });
};

handleNewClick = (event: Event) => {
event.preventDefault();
this.setState({ selection: 'new' });
};

handleOrganizationCreate = (newOrganization: string) => {
this.setState({ newOrganization });
};

handleOrganizationDelete = () => {
this.setState({ newOrganization: undefined });
};

handleExistingOrganizationSelect = ({ value }: { value: string }) => {
this.setState({ existingOrganization: value });
};

handleContinueClick = (event: Event) => {
event.preventDefault();
const organization = this.getSelectedOrganization();
if (organization) {
this.props.onContinue(organization);
}
};

renderPersonalOrganizationOption = () => (
<div>
<a className="link-base-color link-no-underline" href="#" onClick={this.handlePersonalClick}>
<i
className={classNames('icon-radio', 'spacer-right', {
'is-checked': this.state.selection === 'personal'
})}
/>
{translate('onboarding.organization.my_personal_organization')}
<span className="note spacer-left">{this.props.currentUser.login}</span>
</a>
</div>
);

renderExistingOrganizationOption = () => (
<div className="big-spacer-top">
<a
className="js-existing link-base-color link-no-underline"
href="#"
onClick={this.handleExistingClick}>
<i
className={classNames('icon-radio', 'spacer-right', {
'is-checked': this.state.selection === 'existing'
})}
/>
{translate('onboarding.organization.exising_organization')}
</a>
{this.state.selection === 'existing' &&
<div className="big-spacer-top">
<Select
className="input-super-large"
clearable={false}
onChange={this.handleExistingOrganizationSelect}
options={this.state.existingOrganizations.map(organization => ({
label: organization,
value: organization
}))}
value={this.state.existingOrganization}
/>
</div>}
</div>
);

renderNewOrganizationOption = () => (
<div className="big-spacer-top">
<a
className="js-new link-base-color link-no-underline"
href="#"
onClick={this.handleNewClick}>
<i
className={classNames('icon-radio', 'spacer-right', {
'is-checked': this.state.selection === 'new'
})}
/>
{translate('onboarding.organization.create_another_organization')}
</a>
{this.state.selection === 'new' &&
<div className="big-spacer-top">
<NewOrganizationForm
onDelete={this.handleOrganizationDelete}
onDone={this.handleOrganizationCreate}
organization={this.state.newOrganization}
/>
</div>}
</div>
);

renderForm = () => {
return (
<div className="boxed-group-inner">
<div className="big-spacer-bottom width-50">
{translate('onboarding.organization.text')}
</div>

{this.renderPersonalOrganizationOption()}
{this.state.existingOrganizations.length > 0 && this.renderExistingOrganizationOption()}
{this.renderNewOrganizationOption()}

{this.getSelectedOrganization() != null &&
<div className="big-spacer-top">
<button className="js-continue" onClick={this.handleContinueClick}>
{translate('continue')}
</button>
</div>}
</div>
);
};

renderResult = () => {
const result = this.getSelectedOrganization();

return result != null
? <div className="boxed-group-actions">
<i className="icon-check spacer-right" />
<strong>{result}</strong>
</div>
: null;
};

render() {
return (
<Step
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
stepNumber={1}
stepTitle={translate('onboarding.organization.header')}
/>
);
}
}

+ 145
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/ProjectKeyStep.js Datei anzeigen

@@ -0,0 +1,145 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { createProject, deleteProject } from '../../../api/components';
import { translate } from '../../../helpers/l10n';

type Props = {
onDelete: () => void,
onDone: (projectKey: string) => void,
organization?: string,
projectKey?: string
};

type State = {
done: boolean,
loading: boolean,
projectKey: string
};

export default class ProjectKeyStep extends React.PureComponent {
mounted: boolean;
props: Props;
state: State;

constructor(props: Props) {
super(props);
this.state = {
done: props.projectKey != null,
loading: false,
projectKey: props.projectKey || ''
};
}

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

stopLoading = () => {
if (this.mounted) {
this.setState({ loading: false });
}
};

sanitizeProjectKey = (projectKey: string) => projectKey.replace(/[^a-zA-Z0-9-_\.:]/, '');

handleProjectKeyChange = (event: { target: HTMLInputElement }) => {
this.setState({ projectKey: this.sanitizeProjectKey(event.target.value) });
};

handleProjectCreate = (event: Event) => {
event.preventDefault();
const { projectKey } = this.state;
const data: { [string]: string } = {
name: projectKey,
project: projectKey
};
if (this.props.organization) {
data.organization = this.props.organization;
}
this.setState({ loading: true });
createProject(data).then(() => {
if (this.mounted) {
this.setState({ done: true, loading: false });
this.props.onDone(projectKey);
}
}, this.stopLoading);
};

handleProjectDelete = (event: Event) => {
event.preventDefault();
const { projectKey } = this.state;
this.setState({ loading: true });
deleteProject(projectKey).then(() => {
if (this.mounted) {
this.setState({ done: false, loading: false, projectKey: '' });
this.props.onDelete();
}
}, this.stopLoading);
};

render() {
const { done, loading, projectKey } = this.state;

const valid = projectKey.length > 0;

const form = done
? <form onSubmit={this.handleProjectDelete}>
<span className="spacer-right text-middle">{projectKey}</span>
{loading
? <i className="spinner" />
: <button className="button-clean">
<i className="icon-delete" />
</button>}
</form>
: <form onSubmit={this.handleProjectCreate}>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
minLength={1}
maxLength={400}
onChange={this.handleProjectKeyChange}
required={true}
type="text"
value={projectKey}
/>
{loading
? <i className="spinner" />
: <button className="text-middle" disabled={!valid}>{translate('Done')}</button>}
<div className="note spacer-top abs-width-300">
{translate('onboarding.project_key_requirement')}
</div>
</form>;

return (
<div className="big-spacer-top">
<h4 className="spacer-bottom">
{translate('onboarding.language.project_key')}
</h4>
{form}
</div>
);
}
}

+ 47
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js Datei anzeigen

@@ -0,0 +1,47 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import classNames from 'classnames';

type Props = {
open: boolean,
renderForm: () => React.Element<*>,
renderResult: () => ?React.Element<*>,
stepNumber: number,
stepTitle: string
};

export default function Step(props: Props) {
const className = classNames('boxed-group', 'onboarding-step', {
'onboarding-step-open': props.open
});

return (
<div className={className}>
<div className="onboarding-step-number">{props.stepNumber}</div>
{!props.open && props.renderResult()}
<div className="boxed-group-header">
<h2>{props.stepTitle}</h2>
</div>
{props.open ? props.renderForm() : <div className="boxed-group-inner" />}
</div>
);
}

+ 180
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/TokenStep.js Datei anzeigen

@@ -0,0 +1,180 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Step from './Step';
import { generateToken, revokeToken } from '../../../api/user-tokens';
import { translate } from '../../../helpers/l10n';

type Props = {
open: boolean,
onContinue: (token: string) => void,
stepNumber: number
};

type State = {
loading: boolean,
tokenName?: string,
token?: string
};

export default class TokenStep extends React.PureComponent {
mounted: boolean;
props: Props;

static defaultProps = {
stepNumber: 1
};

state: State = {
loading: false
};

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

handleTokenNameChange = (event: { target: HTMLInputElement }) => {
this.setState({ tokenName: event.target.value });
};

handleTokenGenerate = (event: Event) => {
event.preventDefault();
const { tokenName } = this.state;
if (tokenName) {
this.setState({ loading: true });
generateToken(tokenName).then(
({ token }) => {
if (this.mounted) {
this.setState({ loading: false, token });
}
},
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
);
}
};

handleTokenRevoke = (event: Event) => {
event.preventDefault();
const { tokenName } = this.state;
if (tokenName) {
this.setState({ loading: true });
revokeToken(tokenName).then(
() => {
if (this.mounted) {
this.setState({ loading: false, token: undefined, tokenName: undefined });
}
},
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
);
}
};

handleContinueClick = (event: Event) => {
event.preventDefault();
if (this.state.token) {
this.props.onContinue(this.state.token);
}
};

renderForm = () => {
const { loading, token, tokenName } = this.state;

return (
<div className="boxed-group-inner">
<div className="big-spacer-bottom width-50">
{translate('onboarding.token.text')}
</div>

{token != null
? <form onSubmit={this.handleTokenRevoke}>
{tokenName}{': '}
<span className="monospaced spacer-right">{token}</span>
{loading
? <i className="spinner" />
: <button className="button-clean" onClick={this.handleTokenRevoke}>
<i className="icon-delete" />
</button>}
</form>
: <form onSubmit={this.handleTokenGenerate}>
<input
autoFocus={true}
className="input-large spacer-right"
onChange={this.handleTokenNameChange}
placeholder={translate('onboarding.token.placeholder')}
required={true}
type="text"
value={tokenName || ''}
/>
{loading
? <i className="spinner" />
: <button>{translate('onboarding.token.generate')}</button>}
</form>}

{token != null &&
<div className="big-spacer-top">
<button className="js-continue" onClick={this.handleContinueClick}>
{translate('continue')}
</button>
</div>}
</div>
);
};

renderResult = () => {
const { token, tokenName } = this.state;

if (!token) {
return null;
}

return (
<div className="boxed-group-actions">
<i className="icon-check spacer-right" />
{tokenName}{': '}
<strong className="monospaced">{token}</strong>
</div>
);
};

render() {
return (
<Step
open={this.props.open}
renderForm={this.renderForm}
renderResult={this.renderResult}
stepNumber={this.props.stepNumber}
stepTitle={translate('onboarding.token.header')}
/>
);
}
}

+ 106
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/LanguageStep-test.js Datei anzeigen

@@ -0,0 +1,106 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import LanguageStep from '../LanguageStep';

it('selects java', () => {
const onDone = jest.fn();
const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} />);

wrapper.find('RadioToggle').prop('onCheck')('java');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('RadioToggle').at(1).prop('onCheck')('maven');
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDone).lastCalledWith({ language: 'java', javaBuild: 'maven' });

wrapper.find('RadioToggle').at(1).prop('onCheck')('gradle');
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDone).lastCalledWith({ language: 'java', javaBuild: 'gradle' });
});

it('selects c#', () => {
const onDone = jest.fn();
const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} />);

wrapper.find('RadioToggle').prop('onCheck')('dotnet');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('NewProjectForm').prop('onDone')('project-foo');
expect(onDone).lastCalledWith({ language: 'dotnet', projectKey: 'project-foo' });
});

it('selects c-family', () => {
const onDone = jest.fn();
const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} sonarCloud={true} />);

wrapper.find('RadioToggle').prop('onCheck')('c-family');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('RadioToggle').at(1).prop('onCheck')('msvc');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('NewProjectForm').prop('onDone')('project-foo');
expect(onDone).lastCalledWith({
language: 'c-family',
cFamilyCompiler: 'msvc',
projectKey: 'project-foo'
});

wrapper.find('RadioToggle').at(1).prop('onCheck')('clang-gcc');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('RadioToggle').at(2).prop('onCheck')('linux');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('NewProjectForm').prop('onDone')('project-foo');
expect(onDone).lastCalledWith({
language: 'c-family',
cFamilyCompiler: 'clang-gcc',
os: 'linux',
projectKey: 'project-foo'
});
});

it('selects other', () => {
const onDone = jest.fn();
const wrapper = shallow(<LanguageStep onDone={onDone} onReset={jest.fn()} />);

wrapper.find('RadioToggle').prop('onCheck')('other');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('RadioToggle').at(1).prop('onCheck')('mac');
wrapper.update();
expect(wrapper).toMatchSnapshot();

wrapper.find('NewProjectForm').prop('onDone')('project-foo');
expect(onDone).lastCalledWith({ language: 'other', os: 'mac', projectKey: 'project-foo' });
});

+ 56
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js Datei anzeigen

@@ -0,0 +1,56 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { mount } from 'enzyme';
import NewOrganizationForm from '../NewOrganizationForm';
import { change, doAsync, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/organizations', () => ({
createOrganization: () => Promise.resolve(),
deleteOrganization: () => Promise.resolve(),
getOrganization: () => Promise.resolve(null)
}));

it('creates new organization', () => {
const onDone = jest.fn();
const wrapper = mount(<NewOrganizationForm onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
});

it('deletes organization', () => {
const onDelete = jest.fn();
const wrapper = mount(<NewOrganizationForm onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, organization: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
});

+ 55
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js Datei anzeigen

@@ -0,0 +1,55 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { mount } from 'enzyme';
import NewProjectForm from '../NewProjectForm';
import { change, doAsync, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/components', () => ({
createProject: () => Promise.resolve(),
deleteProject: () => Promise.resolve()
}));

it('creates new project', () => {
const onDone = jest.fn();
const wrapper = mount(<NewProjectForm onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
});

it('deletes project', () => {
const onDelete = jest.fn();
const wrapper = mount(<NewProjectForm onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, projectKey: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
});

+ 63
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js Datei anzeigen

@@ -0,0 +1,63 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { mount } from 'enzyme';
import OrganizationStep from '../OrganizationStep';
import { click, doAsync } from '../../../../helpers/testUtils';

jest.mock('../../../../api/organizations', () => ({
getMyOrganizations: () => Promise.resolve(['user', 'another'])
}));

const currentUser = { isLoggedIn: true, login: 'user' };

it('works with personal organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
<OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
);
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('user');
});

it('works with existing organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
<OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
);
return doAsync(() => {
click(wrapper.find('.js-existing'));
wrapper.find('Select').prop('onChange')({ value: 'another' });
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('another');
});
});

it('works with new organization', () => {
const onContinue = jest.fn();
const wrapper = mount(
<OrganizationStep currentUser={currentUser} onContinue={onContinue} open={true} />
);
click(wrapper.find('.js-new'));
wrapper.find('NewOrganizationForm').prop('onDone')('new');
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('new');
});

+ 55
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js Datei anzeigen

@@ -0,0 +1,55 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { mount } from 'enzyme';
import ProjectKeyStep from '../ProjectKeyStep';
import { change, doAsync, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/components', () => ({
createProject: () => Promise.resolve(),
deleteProject: () => Promise.resolve()
}));

it('creates new project', () => {
const onDone = jest.fn();
const wrapper = mount(<ProjectKeyStep onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
});

it('deletes project', () => {
const onDelete = jest.fn();
const wrapper = mount(<ProjectKeyStep onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, projectKey: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
});

+ 38
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/Step-test.js Datei anzeigen

@@ -0,0 +1,38 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import Step from '../Step';

it('renders', () => {
const wrapper = shallow(
<Step
open={true}
renderForm={() => <div>form</div>}
renderResult={() => <div>result</div>}
stepNumber={1}
stepTitle="First Step"
/>
);
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ open: false });
expect(wrapper).toMatchSnapshot();
});

+ 55
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js Datei anzeigen

@@ -0,0 +1,55 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { mount } from 'enzyme';
import TokenStep from '../TokenStep';
import { change, click, doAsync, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/user-tokens', () => ({
generateToken: () => Promise.resolve({ token: 'abcd1234' }),
revokeToken: () => Promise.resolve()
}));

it('generates token', () => {
const wrapper = mount(<TokenStep open={true} onContinue={jest.fn()} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'my token');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => expect(wrapper).toMatchSnapshot());
});

it('revokes token', () => {
const wrapper = mount(<TokenStep open={true} onContinue={jest.fn()} />);
wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => expect(wrapper).toMatchSnapshot());
});

it('continues', () => {
const onContinue = jest.fn();
const wrapper = mount(<TokenStep open={true} onContinue={onContinue} />);
wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('abcd1234');
});

+ 687
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/LanguageStep-test.js.snap Datei anzeigen

@@ -0,0 +1,687 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`selects c# 1`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="dotnet"
/>
</div>
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
/>
</div>
`;

exports[`selects c-family 1`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.c-family",
"value": "c-family",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="c-family"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.c-family.compiler
</h4>
<RadioToggle
disabled={false}
name="c-family-compiler"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.c-family.compiler.msvc",
"value": "msvc",
},
Object {
"label": "onboarding.language.c-family.compiler.clang-gcc",
"value": "clang-gcc",
},
]
}
value={null}
/>
</div>
</div>
`;

exports[`selects c-family 2`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.c-family",
"value": "c-family",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="c-family"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.c-family.compiler
</h4>
<RadioToggle
disabled={false}
name="c-family-compiler"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.c-family.compiler.msvc",
"value": "msvc",
},
Object {
"label": "onboarding.language.c-family.compiler.clang-gcc",
"value": "clang-gcc",
},
]
}
value="msvc"
/>
</div>
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
/>
</div>
`;

exports[`selects c-family 3`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.c-family",
"value": "c-family",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="c-family"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.c-family.compiler
</h4>
<RadioToggle
disabled={false}
name="c-family-compiler"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.c-family.compiler.msvc",
"value": "msvc",
},
Object {
"label": "onboarding.language.c-family.compiler.clang-gcc",
"value": "clang-gcc",
},
]
}
value="clang-gcc"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.os
</h4>
<RadioToggle
disabled={false}
name="os"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.os.linux",
"value": "linux",
},
Object {
"label": "onboarding.language.os.win",
"value": "win",
},
Object {
"label": "onboarding.language.os.mac",
"value": "mac",
},
]
}
value={null}
/>
</div>
</div>
`;

exports[`selects c-family 4`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.c-family",
"value": "c-family",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="c-family"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.c-family.compiler
</h4>
<RadioToggle
disabled={false}
name="c-family-compiler"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.c-family.compiler.msvc",
"value": "msvc",
},
Object {
"label": "onboarding.language.c-family.compiler.clang-gcc",
"value": "clang-gcc",
},
]
}
value="clang-gcc"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.os
</h4>
<RadioToggle
disabled={false}
name="os"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.os.linux",
"value": "linux",
},
Object {
"label": "onboarding.language.os.win",
"value": "win",
},
Object {
"label": "onboarding.language.os.mac",
"value": "mac",
},
]
}
value="linux"
/>
</div>
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
projectKey="project-foo"
/>
</div>
`;

exports[`selects java 1`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="java"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.java.build_technology
</h4>
<RadioToggle
disabled={false}
name="java-build"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java.build_technology.maven",
"value": "maven",
},
Object {
"label": "onboarding.language.java.build_technology.gradle",
"value": "gradle",
},
]
}
value={null}
/>
</div>
</div>
`;

exports[`selects java 2`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="java"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.java.build_technology
</h4>
<RadioToggle
disabled={false}
name="java-build"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java.build_technology.maven",
"value": "maven",
},
Object {
"label": "onboarding.language.java.build_technology.gradle",
"value": "gradle",
},
]
}
value="maven"
/>
</div>
</div>
`;

exports[`selects java 3`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="java"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.java.build_technology
</h4>
<RadioToggle
disabled={false}
name="java-build"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java.build_technology.maven",
"value": "maven",
},
Object {
"label": "onboarding.language.java.build_technology.gradle",
"value": "gradle",
},
]
}
value="gradle"
/>
</div>
</div>
`;

exports[`selects other 1`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="other"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.os
</h4>
<RadioToggle
disabled={false}
name="os"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.os.linux",
"value": "linux",
},
Object {
"label": "onboarding.language.os.win",
"value": "win",
},
Object {
"label": "onboarding.language.os.mac",
"value": "mac",
},
]
}
value={null}
/>
</div>
</div>
`;

exports[`selects other 2`] = `
<div>
<div>
<h4
className="spacer-bottom"
>
onboarding.language
</h4>
<RadioToggle
disabled={false}
name="language"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.java",
"value": "java",
},
Object {
"label": "onboarding.language.dotnet",
"value": "dotnet",
},
Object {
"label": "onboarding.language.other",
"value": "other",
},
]
}
value="other"
/>
</div>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.os
</h4>
<RadioToggle
disabled={false}
name="os"
onCheck={[Function]}
options={
Array [
Object {
"label": "onboarding.language.os.linux",
"value": "linux",
},
Object {
"label": "onboarding.language.os.win",
"value": "win",
},
Object {
"label": "onboarding.language.os.mac",
"value": "mac",
},
]
}
value="mac"
/>
</div>
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
/>
</div>
`;

+ 168
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewOrganizationForm-test.js.snap Datei anzeigen

@@ -0,0 +1,168 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`creates new organization 1`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-super-large spacer-right text-middle"
maxLength={32}
minLength={2}
onChange={[Function]}
placeholder="onboarding.organization.placeholder"
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
create
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.organization.key_requirement
</div>
</form>
</NewOrganizationForm>
`;

exports[`creates new organization 2`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-super-large spacer-right text-middle"
maxLength={32}
minLength={2}
onChange={[Function]}
placeholder="onboarding.organization.placeholder"
required={true}
type="text"
value="foo"
/>
<i
className="spinner"
/>
<div
className="note spacer-top abs-width-300"
>
onboarding.organization.key_requirement
</div>
</form>
</NewOrganizationForm>
`;

exports[`creates new organization 3`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</NewOrganizationForm>
`;

exports[`deletes organization 1`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</NewOrganizationForm>
`;

exports[`deletes organization 2`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<i
className="spinner"
/>
</form>
</NewOrganizationForm>
`;

exports[`deletes organization 3`] = `
<NewOrganizationForm
onDelete={[Function]}
onDone={[Function]}
>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-super-large spacer-right text-middle"
maxLength={32}
minLength={2}
onChange={[Function]}
placeholder="onboarding.organization.placeholder"
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
create
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.organization.key_requirement
</div>
</form>
</NewOrganizationForm>
`;

+ 219
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/NewProjectForm-test.js.snap Datei anzeigen

@@ -0,0 +1,219 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`creates new project 1`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
Done
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</NewProjectForm>
`;

exports[`creates new project 2`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value="foo"
/>
<i
className="spinner"
/>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</NewProjectForm>
`;

exports[`creates new project 3`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</div>
</NewProjectForm>
`;

exports[`deletes project 1`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</div>
</NewProjectForm>
`;

exports[`deletes project 2`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<i
className="spinner"
/>
</form>
</div>
</NewProjectForm>
`;

exports[`deletes project 3`] = `
<NewProjectForm
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
Done
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</NewProjectForm>
`;

+ 219
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/ProjectKeyStep-test.js.snap Datei anzeigen

@@ -0,0 +1,219 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`creates new project 1`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
Done
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</ProjectKeyStep>
`;

exports[`creates new project 2`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value="foo"
/>
<i
className="spinner"
/>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</ProjectKeyStep>
`;

exports[`creates new project 3`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</div>
</ProjectKeyStep>
`;

exports[`deletes project 1`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<button
className="button-clean"
>
<i
className="icon-delete"
/>
</button>
</form>
</div>
</ProjectKeyStep>
`;

exports[`deletes project 2`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<span
className="spacer-right text-middle"
>
foo
</span>
<i
className="spinner"
/>
</form>
</div>
</ProjectKeyStep>
`;

exports[`deletes project 3`] = `
<ProjectKeyStep
onDelete={[Function]}
onDone={[Function]}
>
<div
className="big-spacer-top"
>
<h4
className="spacer-bottom"
>
onboarding.language.project_key
</h4>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right text-middle"
maxLength={400}
minLength={1}
onChange={[Function]}
required={true}
type="text"
value=""
/>
<button
className="text-middle"
disabled={true}
>
Done
</button>
<div
className="note spacer-top abs-width-300"
>
onboarding.project_key_requirement
</div>
</form>
</div>
</ProjectKeyStep>
`;

+ 48
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/Step-test.js.snap Datei anzeigen

@@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders 1`] = `
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
First Step
</h2>
</div>
<div>
form
</div>
</div>
`;

exports[`renders 2`] = `
<div
className="boxed-group onboarding-step"
>
<div
className="onboarding-step-number"
>
1
</div>
<div>
result
</div>
<div
className="boxed-group-header"
>
<h2>
First Step
</h2>
</div>
<div
className="boxed-group-inner"
/>
</div>
`;

+ 383
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap Datei anzeigen

@@ -0,0 +1,383 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`generates token 1`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
type="text"
value=""
/>
<button>
onboarding.token.generate
</button>
</form>
</div>
</div>
</Step>
</TokenStep>
`;

exports[`generates token 2`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
type="text"
value="my token"
/>
<i
className="spinner"
/>
</form>
</div>
</div>
</Step>
</TokenStep>
`;

exports[`generates token 3`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
my token
:
<span
className="monospaced spacer-right"
>
abcd1234
</span>
<button
className="button-clean"
onClick={[Function]}
>
<i
className="icon-delete"
/>
</button>
</form>
<div
className="big-spacer-top"
>
<button
className="js-continue"
onClick={[Function]}
>
continue
</button>
</div>
</div>
</div>
</Step>
</TokenStep>
`;

exports[`revokes token 1`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
my token
:
<span
className="monospaced spacer-right"
>
abcd1234
</span>
<button
className="button-clean"
onClick={[Function]}
>
<i
className="icon-delete"
/>
</button>
</form>
<div
className="big-spacer-top"
>
<button
className="js-continue"
onClick={[Function]}
>
continue
</button>
</div>
</div>
</div>
</Step>
</TokenStep>
`;

exports[`revokes token 2`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
my token
:
<span
className="monospaced spacer-right"
>
abcd1234
</span>
<i
className="spinner"
/>
</form>
<div
className="big-spacer-top"
>
<button
className="js-continue"
onClick={[Function]}
>
continue
</button>
</div>
</div>
</div>
</Step>
</TokenStep>
`;

exports[`revokes token 3`] = `
<TokenStep
onContinue={[Function]}
open={true}
stepNumber={1}
>
<Step
open={true}
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.token.header"
>
<div
className="boxed-group onboarding-step onboarding-step-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.token.header
</h2>
</div>
<div
className="boxed-group-inner"
>
<div
className="big-spacer-bottom width-50"
>
onboarding.token.text
</div>
<form
onSubmit={[Function]}
>
<input
autoFocus={true}
className="input-large spacer-right"
onChange={[Function]}
placeholder="onboarding.token.placeholder"
required={true}
type="text"
value=""
/>
<button>
onboarding.token.generate
</button>
</form>
</div>
</div>
</Step>
</TokenStep>
`;

+ 58
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/BuildWrapper.js Datei anzeigen

@@ -0,0 +1,58 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { translate } from '../../../../helpers/l10n';

type Props = {
className?: string,
os: string
};

const filenames = {
linux: 'build-wrapper-win-x86.zip',
win: 'build-wrapper-linux-x86.zip',
mac: 'build-wrapper-macosx-x86.zip'
};

export default function BuildWrapper(props: Props) {
return (
<div className={props.className}>
<h4 className="spacer-bottom">
{translate('onboarding.analysis.build_wrapper.header', props.os)}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.build_wrapper.text', props.os)
}}
/>
<p>
<a
className="button"
download={filenames[props.os]}
href={window.baseUrl + '/static/cpp/' + filenames[props.os]}
target="_blank">
{translate('download_verb')}
</a>
</p>
</div>
);
}

+ 76
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/ClangGCC.js Datei anzeigen

@@ -0,0 +1,76 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import SQScanner from './SQScanner';
import BuildWrapper from './BuildWrapper';
import { translate } from '../../../../helpers/l10n';

type Props = {
host: string,
os: string,
organization?: string,
projectKey: string,
token: string
};

const executables = {
linux: 'build-wrapper-linux-x86-64',
win: 'build-wrapper-win-x86-64.exe',
mac: 'build-wrapper-macosx-x86'
};

export default function ClangGCC(props: Props) {
const command1 = `${executables[props.os]} --out-dir bw-output make clean all`;

const command2 = [
props.os === 'win' ? 'sonar-scanner.bat' : 'sonar-scanner',
`-Dsonar.projectKey=${props.projectKey}`,
props.organization && `-Dsonar.organization=${props.organization}`,
'-Dsonar.sources=.',
'-Dsonar.cfamily.build-wrapper-output=bw-output',
`-Dsonar.host.url=${props.host}`,
`-Dsonar.login=${props.token}`
];

return (
<div>
<SQScanner os={props.os} />
<BuildWrapper className="huge-spacer-top" os={props.os} />

<h4 className="huge-spacer-top spacer-bottom">
{translate('onboarding.analysis.sq_scanner.execute')}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.sq_scanner.execute.text')
}}
/>
<Command command={command1} />
<Command command={command2} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.sq_scanner.docs') }}
/>
</div>
);
}

+ 89
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Command.js Datei anzeigen

@@ -0,0 +1,89 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Clipboard from 'clipboard';
import Tooltip from '../../../../components/controls/Tooltip';
import { translate } from '../../../../helpers/l10n';

type Props = {
command: string | Array<?string>
};

type State = {
tooltipShown: boolean
};

const s = ' \\' + '\n ';

export default class Command extends React.PureComponent {
clipboard: Object;
copyButton: HTMLButtonElement;
mounted: boolean;
props: Props;
state: State = { tooltipShown: false };

componentDidMount() {
this.mounted = true;
this.clipboard = new Clipboard(this.copyButton);
this.clipboard.on('success', this.showTooltip);
}

componentWillUnmount() {
this.mounted = false;
this.clipboard.destroy();
}

showTooltip = () => {
if (this.mounted) {
this.setState({ tooltipShown: true });
setTimeout(this.hideTooltip, 1000);
}
};

hideTooltip = () => {
if (this.mounted) {
this.setState({ tooltipShown: false });
}
};

render() {
const { command } = this.props;
const commandArray = Array.isArray(command) ? command.filter(line => line != null) : [command];
const finalCommand = commandArray.join(s);

const button = (
<button data-clipboard-text={finalCommand} ref={node => (this.copyButton = node)}>
{translate('copy')}
</button>
);

return (
<div className="onboarding-command">
<pre>{finalCommand}</pre>
{this.state.tooltipShown
? <Tooltip defaultVisible={true} placement="top" overlay="Copied!" trigger="manual">
{button}
</Tooltip>
: button}
</div>
);
}
}

+ 68
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/DotNet.js Datei anzeigen

@@ -0,0 +1,68 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import MSBuildScanner from './MSBuildScanner';
import { translate } from '../../../../helpers/l10n';

type Props = {|
host: string,
organization?: string,
projectKey: string,
token: string
|};

export default function DotNet(props: Props) {
const command1 = [
'SonarQube.Scanner.MSBuild.exe begin',
`/k:"${props.projectKey}"`,
props.organization && `/d:"sonar.organization=${props.organization}"`,
`/d:"sonar.host.url=${props.host}`,
`/d:"sonar.login=${props.token}"`
];

const command2 = 'MsBuild.exe /t:Rebuild';

const command3 = ['SonarQube.Scanner.MSBuild.exe end', `/d:"sonar.login=${props.token}"`];

return (
<div>
<MSBuildScanner />

<h4 className="huge-spacer-top spacer-bottom">
{translate('onboarding.analysis.msbuild.execute')}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.msbuild.execute.text')
}}
/>
<Command command={command1} />
<Command command={command2} />
<Command command={command3} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.docs') }}
/>
</div>
);
}

+ 59
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaGradle.js Datei anzeigen

@@ -0,0 +1,59 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import { translate } from '../../../../helpers/l10n';

type Props = {|
host: string,
organization?: string,
token: string
|};

export default function JavaGradle(props: Props) {
const config = 'plugins {\n id "org.sonarqube" version "2.2"\n}';

const command = [
'./gradlew sonarqube',
props.organization && `-Dsonar.organization=${props.organization}`,
`-Dsonar.host.url=${props.host}`,
`-Dsonar.login=${props.token}`
];

return (
<div>
<h4 className="spacer-bottom">{translate('onboarding.analysis.java.gradle.header')}</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.java.gradle.text.1') }}
/>
<Command command={config} />
<p className="spacer-top spacer-bottom markdown">
{translate('onboarding.analysis.java.gradle.text.2')}
</p>
<Command command={command} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.java.gradle.docs') }}
/>
</div>
);
}

+ 50
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/JavaMaven.js Datei anzeigen

@@ -0,0 +1,50 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import { translate } from '../../../../helpers/l10n';

type Props = {|
host: string,
organization?: string,
token: string
|};

export default function JavaMaven(props: Props) {
const command = [
'mvn sonar:sonar',
props.organization && `-Dsonar.organization=${props.organization}`,
`-Dsonar.host.url=${props.host}`,
`-Dsonar.login=${props.token}`
];

return (
<div>
<h4 className="spacer-bottom">{translate('onboarding.analysis.java.maven.header')}</h4>
<p className="spacer-bottom markdown">{translate('onboarding.analysis.java.maven.text')}</p>
<Command command={command} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.java.maven.docs') }}
/>
</div>
);
}

+ 46
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/MSBuildScanner.js Datei anzeigen

@@ -0,0 +1,46 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { translate } from '../../../../helpers/l10n';

type Props = {
className?: string
};

export default function MSBuildScanner(props: Props) {
return (
<div className={props.className}>
<h4 className="spacer-bottom">{translate('onboarding.analysis.msbuild.header')}</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.text') }}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
target="_blank">
{translate('download_verb')}
</a>
</p>
</div>
);
}

+ 71
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Msvc.js Datei anzeigen

@@ -0,0 +1,71 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import MSBuildScanner from './MSBuildScanner';
import BuildWrapper from './BuildWrapper';
import { translate } from '../../../../helpers/l10n';

type Props = {|
host: string,
organization?: string,
projectKey: string,
token: string
|};

export default function Msvc(props: Props) {
const command1 = [
'SonarQube.Scanner.MSBuild.exe begin',
`/k:"${props.projectKey}"`,
props.organization && `/d:"sonar.organization=${props.organization}"`,
'/d:"sonar.cfamily.build-wrapper-output=bw-output"',
`/d:"sonar.host.url=${props.host}`,
`/d:"sonar.login=${props.token}"`
];

const command2 = 'build-wrapper-win-x86-64.exe --out-dir bw-output MsBuild.exe /t:Rebuild';

const command3 = ['SonarQube.Scanner.MSBuild.exe end', `/d:"sonar.login=${props.token}"`];

return (
<div>
<MSBuildScanner />
<BuildWrapper className="huge-spacer-top" os="win" />

<h4 className="huge-spacer-top spacer-bottom">
{translate('onboarding.analysis.msbuild.execute')}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.msbuild.execute.text')
}}
/>
<Command command={command1} />
<Command command={command2} />
<Command command={command3} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.docs') }}
/>
</div>
);
}

+ 64
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Other.js Datei anzeigen

@@ -0,0 +1,64 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import Command from './Command';
import SQScanner from './SQScanner';
import { translate } from '../../../../helpers/l10n';

type Props = {|
host: string,
organization?: string,
os: string,
projectKey: string,
token: string
|};

export default function Other(props: Props) {
const command = [
props.os === 'win' ? 'sonar-scanner.bat' : 'sonar-scanner',
`-Dsonar.projectKey=${props.projectKey}`,
props.organization && `-Dsonar.organization=${props.organization}`,
'-Dsonar.sources=.',
`-Dsonar.host.url=${props.host}`,
`-Dsonar.login=${props.token}`
];

return (
<div>
<SQScanner os={props.os} />

<h4 className="huge-spacer-top spacer-bottom">
{translate('onboarding.analysis.sq_scanner.execute')}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.sq_scanner.execute.text')
}}
/>
<Command command={command} />
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.sq_scanner.docs') }}
/>
</div>
);
}

+ 51
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/SQScanner.js Datei anzeigen

@@ -0,0 +1,51 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { translate } from '../../../../helpers/l10n';

type Props = {
className?: string,
os: string
};

export default function SQScanner(props: Props) {
return (
<div className={props.className}>
<h4 className="spacer-bottom">
{translate('onboarding.analysis.sq_scanner.header', props.os)}
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={{
__html: translate('onboarding.analysis.sq_scanner.text', props.os)
}}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
target="_blank">
{translate('download_verb')}
</a>
</p>
</div>
);
}

+ 29
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/BuildWrapper-test.js Datei anzeigen

@@ -0,0 +1,29 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import BuildWrapper from '../BuildWrapper';

it('renders correctly', () => {
expect(shallow(<BuildWrapper os="win" />)).toMatchSnapshot();
expect(shallow(<BuildWrapper os="linux" />)).toMatchSnapshot();
expect(shallow(<BuildWrapper os="mac" />)).toMatchSnapshot();
});

+ 45
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/ClangGCC-test.js Datei anzeigen

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import ClangGCC from '../ClangGCC';

it('renders correctly', () => {
expect(
shallow(<ClangGCC host="host" os="win" projectKey="projectKey" token="token" />)
).toMatchSnapshot();

expect(
shallow(<ClangGCC host="host" os="linux" projectKey="projectKey" token="token" />)
).toMatchSnapshot();

expect(
shallow(
<ClangGCC
host="host"
os="linux"
organization="organization"
projectKey="projectKey"
token="token"
/>
)
).toMatchSnapshot();
});

+ 27
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Command-test.js Datei anzeigen

@@ -0,0 +1,27 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import Command from '../Command';

it('renders correctly', () => {
expect(shallow(<Command command={'foo\nbar'} />)).toMatchSnapshot();
});

+ 32
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/DotNet-test.js Datei anzeigen

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import DotNet from '../DotNet';

it('renders correctly', () => {
expect(shallow(<DotNet host="host" projectKey="projectKey" token="token" />)).toMatchSnapshot();
expect(
shallow(
<DotNet host="host" organization="organization" projectKey="projectKey" token="token" />
)
).toMatchSnapshot();
});

+ 30
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaGradle-test.js Datei anzeigen

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import JavaGradle from '../JavaGradle';

it('renders correctly', () => {
expect(shallow(<JavaGradle host="host" token="token" />)).toMatchSnapshot();
expect(
shallow(<JavaGradle host="host" organization="organization" token="token" />)
).toMatchSnapshot();
});

+ 30
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/JavaMaven-test.js Datei anzeigen

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import JavaMaven from '../JavaMaven';

it('renders correctly', () => {
expect(shallow(<JavaMaven host="host" token="token" />)).toMatchSnapshot();
expect(
shallow(<JavaMaven host="host" organization="organization" token="token" />)
).toMatchSnapshot();
});

+ 27
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/MSBuildScanner-test.js Datei anzeigen

@@ -0,0 +1,27 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import MSBuildScanner from '../MSBuildScanner';

it('renders correctly', () => {
expect(shallow(<MSBuildScanner />)).toMatchSnapshot();
});

+ 30
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Msvc-test.js Datei anzeigen

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import Msvc from '../Msvc';

it('renders correctly', () => {
expect(shallow(<Msvc host="host" projectKey="projectKey" token="token" />)).toMatchSnapshot();
expect(
shallow(<Msvc host="host" organization="organization" projectKey="projectKey" token="token" />)
).toMatchSnapshot();
});

+ 45
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Other-test.js Datei anzeigen

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import Other from '../Other';

it('renders correctly', () => {
expect(
shallow(<Other host="host" os="win" projectKey="projectKey" token="token" />)
).toMatchSnapshot();

expect(
shallow(<Other host="host" os="linux" projectKey="projectKey" token="token" />)
).toMatchSnapshot();

expect(
shallow(
<Other
host="host"
os="linux"
organization="organization"
projectKey="projectKey"
token="token"
/>
)
).toMatchSnapshot();
});

+ 29
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/SQScanner-test.js Datei anzeigen

@@ -0,0 +1,29 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import SQScanner from '../SQScanner';

it('renders correctly', () => {
expect(shallow(<SQScanner os="win" />)).toMatchSnapshot();
expect(shallow(<SQScanner os="linux" />)).toMatchSnapshot();
expect(shallow(<SQScanner os="mac" />)).toMatchSnapshot();
});

+ 85
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/BuildWrapper-test.js.snap Datei anzeigen

@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.build_wrapper.header.win
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.build_wrapper.text.win",
}
}
/>
<p>
<a
className="button"
download="build-wrapper-linux-x86.zip"
href="/static/cpp/build-wrapper-linux-x86.zip"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.build_wrapper.header.linux
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.build_wrapper.text.linux",
}
}
/>
<p>
<a
className="button"
download="build-wrapper-win-x86.zip"
href="/static/cpp/build-wrapper-win-x86.zip"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

exports[`renders correctly 3`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.build_wrapper.header.mac
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.build_wrapper.text.mac",
}
}
/>
<p>
<a
className="button"
download="build-wrapper-macosx-x86.zip"
href="/static/cpp/build-wrapper-macosx-x86.zip"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

+ 148
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/ClangGCC-test.js.snap Datei anzeigen

@@ -0,0 +1,148 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<SQScanner
os="win"
/>
<BuildWrapper
className="huge-spacer-top"
os="win"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command="build-wrapper-win-x86-64.exe --out-dir bw-output make clean all"
/>
<Command
command={
Array [
"sonar-scanner.bat",
"-Dsonar.projectKey=projectKey",
undefined,
"-Dsonar.sources=.",
"-Dsonar.cfamily.build-wrapper-output=bw-output",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<SQScanner
os="linux"
/>
<BuildWrapper
className="huge-spacer-top"
os="linux"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command="build-wrapper-linux-x86-64 --out-dir bw-output make clean all"
/>
<Command
command={
Array [
"sonar-scanner",
"-Dsonar.projectKey=projectKey",
undefined,
"-Dsonar.sources=.",
"-Dsonar.cfamily.build-wrapper-output=bw-output",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

exports[`renders correctly 3`] = `
<div>
<SQScanner
os="linux"
/>
<BuildWrapper
className="huge-spacer-top"
os="linux"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command="build-wrapper-linux-x86-64 --out-dir bw-output make clean all"
/>
<Command
command={
Array [
"sonar-scanner",
"-Dsonar.projectKey=projectKey",
"-Dsonar.organization=organization",
"-Dsonar.sources=.",
"-Dsonar.cfamily.build-wrapper-output=bw-output",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

+ 18
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap Datei anzeigen

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
className="onboarding-command"
>
<pre>
foo
bar
</pre>
<button
data-clipboard-text="foo
bar"
>
copy
</button>
</div>
`;

+ 99
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/DotNet-test.js.snap Datei anzeigen

@@ -0,0 +1,99 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<MSBuildScanner />
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.msbuild.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.execute.text",
}
}
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe begin",
"/k:\\"projectKey\\"",
undefined,
"/d:\\"sonar.host.url=host",
"/d:\\"sonar.login=token\\"",
]
}
/>
<Command
command="MsBuild.exe /t:Rebuild"
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe end",
"/d:\\"sonar.login=token\\"",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<MSBuildScanner />
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.msbuild.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.execute.text",
}
}
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe begin",
"/k:\\"projectKey\\"",
"/d:\\"sonar.organization=organization\\"",
"/d:\\"sonar.host.url=host",
"/d:\\"sonar.login=token\\"",
]
}
/>
<Command
command="MsBuild.exe /t:Rebuild"
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe end",
"/d:\\"sonar.login=token\\"",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.docs",
}
}
/>
</div>
`;

+ 93
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaGradle-test.js.snap Datei anzeigen

@@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.java.gradle.header
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.gradle.text.1",
}
}
/>
<Command
command="plugins {
id \\"org.sonarqube\\" version \\"2.2\\"
}"
/>
<p
className="spacer-top spacer-bottom markdown"
>
onboarding.analysis.java.gradle.text.2
</p>
<Command
command={
Array [
"./gradlew sonarqube",
undefined,
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.gradle.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.java.gradle.header
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.gradle.text.1",
}
}
/>
<Command
command="plugins {
id \\"org.sonarqube\\" version \\"2.2\\"
}"
/>
<p
className="spacer-top spacer-bottom markdown"
>
onboarding.analysis.java.gradle.text.2
</p>
<Command
command={
Array [
"./gradlew sonarqube",
"-Dsonar.organization=organization",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.gradle.docs",
}
}
/>
</div>
`;

+ 67
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/JavaMaven-test.js.snap Datei anzeigen

@@ -0,0 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.java.maven.header
</h4>
<p
className="spacer-bottom markdown"
>
onboarding.analysis.java.maven.text
</p>
<Command
command={
Array [
"mvn sonar:sonar",
undefined,
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.maven.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.java.maven.header
</h4>
<p
className="spacer-bottom markdown"
>
onboarding.analysis.java.maven.text
</p>
<Command
command={
Array [
"mvn sonar:sonar",
"-Dsonar.organization=organization",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.java.maven.docs",
}
}
/>
</div>
`;

+ 28
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/MSBuildScanner-test.js.snap Datei anzeigen

@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.msbuild.header
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.text",
}
}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

+ 109
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Msvc-test.js.snap Datei anzeigen

@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<MSBuildScanner />
<BuildWrapper
className="huge-spacer-top"
os="win"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.msbuild.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.execute.text",
}
}
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe begin",
"/k:\\"projectKey\\"",
undefined,
"/d:\\"sonar.cfamily.build-wrapper-output=bw-output\\"",
"/d:\\"sonar.host.url=host",
"/d:\\"sonar.login=token\\"",
]
}
/>
<Command
command="build-wrapper-win-x86-64.exe --out-dir bw-output MsBuild.exe /t:Rebuild"
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe end",
"/d:\\"sonar.login=token\\"",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<MSBuildScanner />
<BuildWrapper
className="huge-spacer-top"
os="win"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.msbuild.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.execute.text",
}
}
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe begin",
"/k:\\"projectKey\\"",
"/d:\\"sonar.organization=organization\\"",
"/d:\\"sonar.cfamily.build-wrapper-output=bw-output\\"",
"/d:\\"sonar.host.url=host",
"/d:\\"sonar.login=token\\"",
]
}
/>
<Command
command="build-wrapper-win-x86-64.exe --out-dir bw-output MsBuild.exe /t:Rebuild"
/>
<Command
command={
Array [
"SonarQube.Scanner.MSBuild.exe end",
"/d:\\"sonar.login=token\\"",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.msbuild.docs",
}
}
/>
</div>
`;

+ 124
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Other-test.js.snap Datei anzeigen

@@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<SQScanner
os="win"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command={
Array [
"sonar-scanner.bat",
"-Dsonar.projectKey=projectKey",
undefined,
"-Dsonar.sources=.",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<SQScanner
os="linux"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command={
Array [
"sonar-scanner",
"-Dsonar.projectKey=projectKey",
undefined,
"-Dsonar.sources=.",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

exports[`renders correctly 3`] = `
<div>
<SQScanner
os="linux"
/>
<h4
className="huge-spacer-top spacer-bottom"
>
onboarding.analysis.sq_scanner.execute
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.execute.text",
}
}
/>
<Command
command={
Array [
"sonar-scanner",
"-Dsonar.projectKey=projectKey",
"-Dsonar.organization=organization",
"-Dsonar.sources=.",
"-Dsonar.host.url=host",
"-Dsonar.login=token",
]
}
/>
<p
className="big-spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.docs",
}
}
/>
</div>
`;

+ 82
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/SQScanner-test.js.snap Datei anzeigen

@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.sq_scanner.header.win
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.text.win",
}
}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

exports[`renders correctly 2`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.sq_scanner.header.linux
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.text.linux",
}
}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

exports[`renders correctly 3`] = `
<div>
<h4
className="spacer-bottom"
>
onboarding.analysis.sq_scanner.header.mac
</h4>
<p
className="spacer-bottom markdown"
dangerouslySetInnerHTML={
Object {
"__html": "onboarding.analysis.sq_scanner.text.mac",
}
}
/>
<p>
<a
className="button"
href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
target="_blank"
>
download_verb
</a>
</p>
</div>
`;

+ 59
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css Datei anzeigen

@@ -0,0 +1,59 @@
.onboarding-step {
position: relative;
padding-left: 34px;
}

.onboarding-step .boxed-group-actions {
height: 24px;
line-height: 24px;
}

.onboarding-step-number {
position: absolute;
top: 15px;
left: 15px;
width: 24px;
height: 24px;
line-height: 24px;
border-radius: 24px;
background-color: #cdcdcd;
color: #fff;
font-size: 14px;
text-align: center;
}

.onboarding-step-open .onboarding-step-number {
background-color: #236a97;
}

.onboarding-command {
position: relative;
margin: 8px 0;
}

.onboarding-command pre {
padding: 15px;
border-radius: 2px;
background: #404040;
color: #f0f0f0;
overflow: auto;
}

.onboarding-command button {
position: absolute;
top: 15px;
right: 15px;
height: 20px;
line-height: 18px;
border: 1px solid #fff;
color: #fff;
font-size: 11px;
font-weight: normal;
}

.onboarding-command button:hover,
.onboarding-command button:focus,
.onboarding-command button:active {
background-color: #fff;
color: #404040;
}

+ 31
- 0
server/sonar-web/src/main/js/apps/tutorials/routes.js Datei anzeigen

@@ -0,0 +1,31 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.
*/
const routes = [
{
path: 'onboarding',
getComponent(_, callback) {
require.ensure([], require => {
callback(null, require('./onboarding/OnboardingContainer').default);
});
}
}
];

export default routes;

+ 6
- 10
server/sonar-web/src/main/js/apps/users/tokens-view.js Datei anzeigen

@@ -54,17 +54,13 @@ export default Modal.extend({
this.errors = [];
this.newToken = null;
const tokenName = this.$('.js-generate-token-form input').val();
generateToken(this.model.id, tokenName)
.then(response => {
generateToken(tokenName, this.model.id).then(
response => {
this.newToken = response;
this.requestTokens();
})
.catch(error => {
error.response.json().then(response => {
this.errors = response.errors;
this.render();
});
});
},
() => {}
);
},

onRevokeTokenFormSubmit(e) {
@@ -73,7 +69,7 @@ export default Modal.extend({
const token = this.tokens.find(t => t.name === `${tokenName}`);
if (token) {
if (token.deleting) {
revokeToken(this.model.id, tokenName).then(this.requestTokens.bind(this));
revokeToken(tokenName, this.model.id).then(this.requestTokens.bind(this), () => {});
} else {
token.deleting = true;
this.render();

+ 8
- 0
server/sonar-web/src/main/js/helpers/testUtils.js Datei anzeigen

@@ -54,3 +54,11 @@ export const elementKeydown = (element, keyCode) => {
preventDefault() {}
});
};

export const doAsync = fn =>
new Promise(resolve => {
setTimeout(() => {
fn();
resolve();
}, 0);
});

+ 5
- 0
server/sonar-web/src/main/less/components/modals.less Datei anzeigen

@@ -43,6 +43,11 @@
opacity: 1;
}

.modal-medium {
width: 800px;
margin-left: -400px;
}

.modal-large {
width: 90vw;
margin-left: -45vw;

+ 86
- 0
server/sonar-web/src/main/less/components/side-tabs.less Datei anzeigen

@@ -0,0 +1,86 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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 (reference) "../mixins";
@import (reference) "../variables";

.side-tabs-layout {
display: flex;
justify-content: space-between;
align-items: stretch;

.modal & {
padding-left: 10px;
background-color: @barBackgroundColor;
}
}

.side-tabs-main {
position: relative;
z-index: 2;
flex-grow: 1;
padding: 15px 20px;
border: 1px solid @barBorderColor;
box-sizing: border-box;
background-color: #fff;

.modal & {
border-top: none;
border-bottom: none;
border-right: none;
}
}

.side-tabs-side {
position: relative;
z-index: 3;
width: 160px;
flex-shrink: 0;
padding: 10px 0;
box-sizing: border-box;
transform: translateX(1px);
}

.side-tabs-menu > li {
margin-bottom: 4px;
}

.side-tabs-menu > li > a {
display: block;
padding: 10px 10px;
line-height: 1.5;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
border: 1px solid @barBorderColor;
border-right: none;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.3s ease, background-color 0.3s ease;
}

.side-tabs-menu > li > a:hover,
.side-tabs-menu > li > a:focus,
.side-tabs-menu > li > a.active {
background-color: #fff;
}

.side-tabs-menu > li > a.active {
color: #444;
cursor: default;
}

+ 1
- 0
server/sonar-web/src/main/less/init/type.less Datei anzeigen

@@ -114,6 +114,7 @@ small,
.text-top { vertical-align: top; }
.text-middle { vertical-align: middle; }
.text-bottom { vertical-align: bottom; }
.text-text-top { vertical-align: text-top !important; }


// Overflow

+ 1
- 0
server/sonar-web/src/main/less/sonar.less Datei anzeigen

@@ -58,6 +58,7 @@
@import "components/search";
@import "components/pills";
@import "components/react-select";
@import "components/side-tabs";

@import "pages/coding-rules";
@import "pages/maintenance";

+ 82
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties Datei anzeigen

@@ -47,6 +47,7 @@ component=Component
configurable=Configurable
configure=Configure
confirm=Confirm
continue=Continue
copy=Copy
create=Create
created=Created
@@ -301,6 +302,7 @@ since_previous_version.short=\u0394 version
since_previous_version_detailed=since previous version ({0} - {1})
since_previous_version_with_only_date=since previous version ({0})
since_previous_version_detailed.short=\u0394 version ({0})
this_name_is_already_taken=This name is already taken.
time_changes=Time changes
work_duration.x_days={0}d
work_duration.x_hours={0}h
@@ -1054,10 +1056,12 @@ search.placeholder=Search for projects, sub-projects and files...

#------------------------------------------------------------------------------
#
# SHORTCUTS
# GLOBAL HELP
#
#------------------------------------------------------------------------------
shortcuts.modal_title=Shortcuts
help.section.links=Links
help.section.shortcuts=Shortcuts
help.section.tutorials=Tutorials

shortcuts.section.global=Global
shortcuts.section.global.search=quickly open search bar
@@ -2937,3 +2941,79 @@ footer.terms=Terms
footer.twitter=Twitter
footer.version_x=Version {0}
footer.web_api=Web API


#------------------------------------------------------------------------------
#
# ONBOARDING
#
#------------------------------------------------------------------------------
onboarding.header=Welcome to SonarQube!
onboarding.header.sonarcloud=Welcome to SonarCloud!
onboarding.header.description=Let's learn how to analyze your first public project.

onboarding.token.header=Generate a token
onboarding.token.text=We'll use it as a replacement of the user login. This will increase the security of your installation by not letting your analysis user's password going through your network.
onboarding.token.generate=Generate
onboarding.token.placeholder=Enter a name for your token

onboarding.organization.header=Choose an organization for your project
onboarding.organization.text=Organizations are where your projects belong. You can add your team members to your organization later to allow them to contribute to your projects.
onboarding.organization.placeholder=Enter a name for your organization
onboarding.organization.my_personal_organization=My personal organization
onboarding.organization.exising_organization=Existing organization
onboarding.organization.create_another_organization=Create another organization
onboarding.organization.key_requirement=2 to 32 characters. All chars must be lower-case letters (a to z), digits or dash (but dash can neither be trailing nor heading)

onboarding.project_key_requirement=Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit. 400 characters max.

onboarding.analysis.header=Run analysis on your project

onboarding.language=What is your project's main language?
onboarding.language.java=Java
onboarding.language.java.build_technology=You are developing primarily in Java: what is your build technology?
onboarding.language.java.build_technology.maven=Maven
onboarding.language.java.build_technology.gradle=Gradle
onboarding.language.dotnet=C# or VB.NET
onboarding.language.c-family=C, C++, Objective-C
onboarding.language.c-family.compiler=Which compiler are you using?
onboarding.language.c-family.compiler.msvc=Microsoft Visual C++
onboarding.language.c-family.compiler.clang-gcc=CLang or GGC
onboarding.language.other=Other (JS, Python, PHP, ...)
onboarding.language.os=What is your OS?
onboarding.language.os.linux=Linux
onboarding.language.os.win=Windows
onboarding.language.os.mac=macOS
onboarding.language.project_key=Define a unique project key

onboarding.analysis.java.maven.header=Execute the SonarQube Scanner for Maven from your computer
onboarding.analysis.java.maven.text=Running a SonarQube analysis with Maven is straighforward. You just need to run the following command in your project's folder.
onboarding.analysis.java.maven.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html" target="_blank">official documentation of the SonarQube Scanner for Maven</a> for more details.

onboarding.analysis.java.gradle.header=Execute the SonarQube Scanner for Gradle from your computer
onboarding.analysis.java.gradle.text.1=Running a SonarQube analysis with Gradle is straighforward. You just need to declare the <code>org.sonarqube</code> plugin in your <code>build.gradle</code> file:
onboarding.analysis.java.gradle.text.2=and run the following command:
onboarding.analysis.java.gradle.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/gradle.html" target="_blank">official documentation of the SonarQube Scanner for Gradle</a> for more details.

onboarding.analysis.msbuild.header=Download and unzip the SonarQube Scanner for MSBuild
onboarding.analysis.msbuild.text=And add the executable's directory to the <code>%PATH%</code> environment variable
onboarding.analysis.msbuild.execute=Execute the SonarQube Scanner for MSBuild from your computer
onboarding.analysis.msbuild.execute.text=Running a SonarQube analysis is straighforward. You just need to execute the following commands at the root of your solution.
onboarding.analysis.msbuild.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html" target="_blank">official documentation of the SonarQube Scanner for MSBuild</a> for more details.

onboarding.analysis.build_wrapper.header.linux=Download and unzip the Build Wrapper for Linux
onboarding.analysis.build_wrapper.header.win=Download and unzip the Build Wrapper for Windows
onboarding.analysis.build_wrapper.header.mac=Download and unzip the Build Wrapper for macOS
onboarding.analysis.build_wrapper.text.linux=And add the executable's directory to the <code>PATH</code> environment variable
onboarding.analysis.build_wrapper.text.win=And add the executable's directory to the <code>%PATH%</code> environment variable
onboarding.analysis.build_wrapper.text.mac=And add the executable's directory to the <code>PATH</code> environment variable

onboarding.analysis.sq_scanner.header.linux=Download and unzip the SonarQube Scanner for Linux
onboarding.analysis.sq_scanner.header.win=Download and unzip the SonarQube Scanner for Windows
onboarding.analysis.sq_scanner.header.mac=Download and unzip the SonarQube Scanner for macOS
onboarding.analysis.sq_scanner.text.linux=And add the <code>bin</code> directory to the <code>PATH</code> environment variable
onboarding.analysis.sq_scanner.text.win=And add the <code>bin</code> directory to the <code>%PATH%</code> environment variable
onboarding.analysis.sq_scanner.text.mac=And add the <code>bin</code> directory to the <code>PATH</code> environment variable
onboarding.analysis.sq_scanner.execute=Execute the SonarQube Scanner from your computer
onboarding.analysis.sq_scanner.execute.text=Running a SonarQube analysis is straighforward. You just need to execute the following commands in your project's folder.
onboarding.analysis.sq_scanner.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" target="_blank">official documentation of the SonarQube Scanner</a> for more details.

Laden…
Abbrechen
Speichern