Browse Source

SONAR-13147 lazyloading

tags/8.3.0.34182
Jeremy Davis 4 years ago
parent
commit
85c94cb261
69 changed files with 630 additions and 454 deletions
  1. 2
    2
      server/sonar-web/src/main/js/app/components/App.tsx
  2. 2
    2
      server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx
  3. 5
    3
      server/sonar-web/src/main/js/app/components/StartupModal.tsx
  4. 2
    2
      server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopupHelper.tsx
  5. 4
    3
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx
  6. 1
    1
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap
  7. 4
    4
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
  8. 1
    1
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
  9. 3
    3
      server/sonar-web/src/main/js/app/components/search/Search.tsx
  10. 35
    20
      server/sonar-web/src/main/js/app/utils/startReactApp.tsx
  11. 4
    2
      server/sonar-web/src/main/js/apps/about/routes.ts
  12. 7
    7
      server/sonar-web/src/main/js/apps/account/routes.ts
  13. 2
    2
      server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx
  14. 2
    2
      server/sonar-web/src/main/js/apps/background-tasks/routes.ts
  15. 2
    2
      server/sonar-web/src/main/js/apps/code/routes.ts
  16. 2
    2
      server/sonar-web/src/main/js/apps/coding-rules/routes.ts
  17. 2
    2
      server/sonar-web/src/main/js/apps/component-measures/routes.ts
  18. 2
    2
      server/sonar-web/src/main/js/apps/custom-measures/routes.ts
  19. 2
    2
      server/sonar-web/src/main/js/apps/custom-metrics/routes.ts
  20. 2
    2
      server/sonar-web/src/main/js/apps/documentation/routes.ts
  21. 2
    2
      server/sonar-web/src/main/js/apps/groups/routes.ts
  22. 0
    37
      server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx
  23. 0
    56
      server/sonar-web/src/main/js/apps/issues/__tests__/IssuesPageSelector-test.tsx
  24. 0
    17
      server/sonar-web/src/main/js/apps/issues/__tests__/__snapshots__/IssuesPageSelector-test.tsx.snap
  25. 9
    14
      server/sonar-web/src/main/js/apps/issues/components/App.tsx
  26. 10
    10
      server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
  27. 0
    3
      server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
  28. 3
    3
      server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
  29. 0
    5
      server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesSourceViewer-test.tsx.snap
  30. 2
    2
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
  31. 3
    3
      server/sonar-web/src/main/js/apps/maintenance/routes.tsx
  32. 5
    2
      server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
  33. 1
    1
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
  34. 2
    2
      server/sonar-web/src/main/js/apps/marketplace/routes.ts
  35. 1
    5
      server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.tsx
  36. 29
    13
      server/sonar-web/src/main/js/apps/organizations/routes.ts
  37. 3
    3
      server/sonar-web/src/main/js/apps/overview/components/App.tsx
  38. 16
    5
      server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
  39. 2
    2
      server/sonar-web/src/main/js/apps/overview/routes.ts
  40. 2
    2
      server/sonar-web/src/main/js/apps/permission-templates/routes.ts
  41. 3
    3
      server/sonar-web/src/main/js/apps/permissions/routes.ts
  42. 2
    2
      server/sonar-web/src/main/js/apps/portfolio/routes.ts
  43. 2
    2
      server/sonar-web/src/main/js/apps/projectActivity/routes.ts
  44. 2
    2
      server/sonar-web/src/main/js/apps/projectBaseline/routes.ts
  45. 2
    2
      server/sonar-web/src/main/js/apps/projectBranches/routes.ts
  46. 2
    2
      server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts
  47. 2
    2
      server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts
  48. 2
    2
      server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
  49. 2
    14
      server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx
  50. 2
    2
      server/sonar-web/src/main/js/apps/projects/routes.ts
  51. 2
    2
      server/sonar-web/src/main/js/apps/projectsManagement/routes.ts
  52. 2
    2
      server/sonar-web/src/main/js/apps/quality-gates/routes.ts
  53. 2
    2
      server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
  54. 7
    7
      server/sonar-web/src/main/js/apps/quality-profiles/routes.ts
  55. 5
    5
      server/sonar-web/src/main/js/apps/sessions/routes.ts
  56. 3
    3
      server/sonar-web/src/main/js/apps/settings/routes.ts
  57. 2
    2
      server/sonar-web/src/main/js/apps/system/routes.ts
  58. 2
    5
      server/sonar-web/src/main/js/apps/tutorials/routes.ts
  59. 2
    2
      server/sonar-web/src/main/js/apps/users/routes.ts
  60. 3
    3
      server/sonar-web/src/main/js/apps/web-api/routes.ts
  61. 2
    2
      server/sonar-web/src/main/js/apps/webhooks/routes.ts
  62. 2
    2
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx
  63. 99
    103
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
  64. 127
    4
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerBase-test.tsx
  65. 158
    15
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap
  66. 2
    2
      server/sonar-web/src/main/js/components/controls/DateInput.tsx
  67. 2
    2
      server/sonar-web/src/main/js/components/docs/DocTooltip.tsx
  68. 1
    1
      server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap
  69. 10
    4
      server/sonar-web/src/main/js/components/workspace/Workspace.tsx

+ 2
- 2
server/sonar-web/src/main/js/app/components/App.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { fetchMyOrganizations } from '../../apps/account/organizations/actions'; import { fetchMyOrganizations } from '../../apps/account/organizations/actions';
import { isSonarCloud } from '../../helpers/system'; import { isSonarCloud } from '../../helpers/system';
import { isLoggedIn } from '../../helpers/users'; import { isLoggedIn } from '../../helpers/users';
import { fetchLanguages } from '../../store/rootActions'; import { fetchLanguages } from '../../store/rootActions';
import { getAppState, getCurrentUser, getGlobalSettingValue, Store } from '../../store/rootReducer'; import { getAppState, getCurrentUser, getGlobalSettingValue, Store } from '../../store/rootReducer';


const PageTracker = lazyLoad(() => import('./PageTracker'));
const PageTracker = lazyLoadComponent(() => import('./PageTracker'));


interface StateProps { interface StateProps {
appState: T.AppState | undefined; appState: T.AppState | undefined;

+ 2
- 2
server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import * as React from 'react'; import * as React from 'react';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import GlobalFooterContainer from './GlobalFooterContainer'; import GlobalFooterContainer from './GlobalFooterContainer';


const PageTracker = lazyLoad(() => import('./PageTracker'));
const PageTracker = lazyLoadComponent(() => import('./PageTracker'));


interface Props { interface Props {
children?: React.ReactNode; children?: React.ReactNode;

+ 5
- 3
server/sonar-web/src/main/js/app/components/StartupModal.tsx View File

import * as differenceInDays from 'date-fns/difference_in_days'; import * as differenceInDays from 'date-fns/difference_in_days';
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { parseDate, toShortNotSoISOString } from 'sonar-ui-common/helpers/dates'; import { parseDate, toShortNotSoISOString } from 'sonar-ui-common/helpers/dates';
import { hasMessage } from 'sonar-ui-common/helpers/l10n'; import { hasMessage } from 'sonar-ui-common/helpers/l10n';
import { get, save } from 'sonar-ui-common/helpers/storage'; import { get, save } from 'sonar-ui-common/helpers/storage';
import { EditionKey } from '../../types/editions'; import { EditionKey } from '../../types/editions';
import { OnboardingContext } from './OnboardingContext'; import { OnboardingContext } from './OnboardingContext';


const OnboardingModal = lazyLoad(() => import('../../apps/tutorials/onboarding/OnboardingModal'));
const LicensePromptModal = lazyLoad(
const OnboardingModal = lazyLoadComponent(() =>
import('../../apps/tutorials/onboarding/OnboardingModal')
);
const LicensePromptModal = lazyLoadComponent(
() => import('../../apps/marketplace/components/LicensePromptModal'), () => import('../../apps/marketplace/components/LicensePromptModal'),
'LicensePromptModal' 'LicensePromptModal'
); );

+ 2
- 2
server/sonar-web/src/main/js/app/components/embed-docs-modal/EmbedDocsPopupHelper.tsx View File

import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; import { ButtonLink } from 'sonar-ui-common/components/controls/buttons';
import Toggler from 'sonar-ui-common/components/controls/Toggler'; import Toggler from 'sonar-ui-common/components/controls/Toggler';
import HelpIcon from 'sonar-ui-common/components/icons/HelpIcon'; import HelpIcon from 'sonar-ui-common/components/icons/HelpIcon';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { translate } from 'sonar-ui-common/helpers/l10n'; import { translate } from 'sonar-ui-common/helpers/l10n';


const EmbedDocsPopup = lazyLoad(() => import('./EmbedDocsPopup'));
const EmbedDocsPopup = lazyLoadComponent(() => import('./EmbedDocsPopup'));


interface State { interface State {
helpOpen: boolean; helpOpen: boolean;

+ 4
- 3
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n'; import { translate } from 'sonar-ui-common/helpers/l10n';


const AnalysisWarningsModal = lazyLoad(() =>
import('../../../../components/common/AnalysisWarningsModal')
const AnalysisWarningsModal = lazyLoadComponent(
() => import('../../../../components/common/AnalysisWarningsModal'),
'AnalysisWarningsModal'
); );


interface Props { interface Props {

+ 1
- 1
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap View File

} }
/> />
</Alert> </Alert>
<LazyLoader
<AnalysisWarningsModal
onClose={[Function]} onClose={[Function]}
warnings={ warnings={
Array [ Array [

+ 4
- 4
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import NavBar from 'sonar-ui-common/components/ui/NavBar'; import NavBar from 'sonar-ui-common/components/ui/NavBar';
import { parseDate } from 'sonar-ui-common/helpers/dates'; import { parseDate } from 'sonar-ui-common/helpers/dates';
import { import {
import GlobalNavMenu from './GlobalNavMenu'; import GlobalNavMenu from './GlobalNavMenu';
import GlobalNavUserContainer from './GlobalNavUserContainer'; import GlobalNavUserContainer from './GlobalNavUserContainer';


const GlobalNavPlus = lazyLoad(() => import('./GlobalNavPlus'), 'GlobalNavPlus');
const NotificationsSidebar = lazyLoad(
const GlobalNavPlus = lazyLoadComponent(() => import('./GlobalNavPlus'), 'GlobalNavPlus');
const NotificationsSidebar = lazyLoadComponent(
() => import('../../notifications/NotificationsSidebar'), () => import('../../notifications/NotificationsSidebar'),
'NotificationsSidebar' 'NotificationsSidebar'
); );
const NavLatestNotification = lazyLoad(
const NavLatestNotification = lazyLoadComponent(
() => import('../../notifications/NavLatestNotification'), () => import('../../notifications/NavLatestNotification'),
'NavLatestNotification' 'NavLatestNotification'
); );

+ 1
- 1
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx View File

} }
} }


export default withRouter(GlobalNavPlus);
export default withRouter<Props>(GlobalNavPlus);

+ 3
- 3
server/sonar-web/src/main/js/app/components/search/Search.tsx View File

import OutsideClickHandler from 'sonar-ui-common/components/controls/OutsideClickHandler'; import OutsideClickHandler from 'sonar-ui-common/components/controls/OutsideClickHandler';
import SearchBox from 'sonar-ui-common/components/controls/SearchBox'; import SearchBox from 'sonar-ui-common/components/controls/SearchBox';
import ClockIcon from 'sonar-ui-common/components/icons/ClockIcon'; import ClockIcon from 'sonar-ui-common/components/icons/ClockIcon';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { scrollToElement } from 'sonar-ui-common/helpers/scrolling'; import { scrollToElement } from 'sonar-ui-common/helpers/scrolling';
import './Search.css'; import './Search.css';
import { ComponentResult, More, Results, sortQualifiers } from './utils'; import { ComponentResult, More, Results, sortQualifiers } from './utils';


const SearchResults = lazyLoad(() => import('./SearchResults'));
const SearchResult = lazyLoad(() => import('./SearchResult'));
const SearchResults = lazyLoadComponent(() => import('./SearchResults'));
const SearchResult = lazyLoadComponent(() => import('./SearchResult'));


interface OwnProps { interface OwnProps {
appState: Pick<T.AppState, 'organizationsEnabled'>; appState: Pick<T.AppState, 'organizationsEnabled'>;

+ 35
- 20
server/sonar-web/src/main/js/app/utils/startReactApp.tsx View File

import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { IndexRoute, Redirect, Route, RouteConfig, RouteProps, Router } from 'react-router'; import { IndexRoute, Redirect, Route, RouteConfig, RouteProps, Router } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent'; import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { ThemeProvider } from 'sonar-ui-common/components/theme'; import { ThemeProvider } from 'sonar-ui-common/components/theme';
import getHistory from 'sonar-ui-common/helpers/getHistory'; import getHistory from 'sonar-ui-common/helpers/getHistory';
import ExploreProjects from '../../apps/explore/ExploreProjects'; import ExploreProjects from '../../apps/explore/ExploreProjects';
import groupsRoutes from '../../apps/groups/routes'; import groupsRoutes from '../../apps/groups/routes';
import Issues from '../../apps/issues/components/AppContainer'; import Issues from '../../apps/issues/components/AppContainer';
import IssuesPageSelector from '../../apps/issues/IssuesPageSelector';
import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes'; import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes';
import marketplaceRoutes from '../../apps/marketplace/routes'; import marketplaceRoutes from '../../apps/marketplace/routes';
import organizationsRoutes from '../../apps/organizations/routes'; import organizationsRoutes from '../../apps/organizations/routes';


function renderComponentRoutes() { function renderComponentRoutes() {
return ( return (
<Route component={lazyLoad(() => import('../components/ComponentContainer'))}>
<Route component={lazyLoadComponent(() => import('../components/ComponentContainer'))}>
<RouteWithChildRoutes path="code" childRoutes={codeRoutes} /> <RouteWithChildRoutes path="code" childRoutes={codeRoutes} />
<RouteWithChildRoutes path="component_measures" childRoutes={componentMeasuresRoutes} /> <RouteWithChildRoutes path="component_measures" childRoutes={componentMeasuresRoutes} />
<RouteWithChildRoutes path="dashboard" childRoutes={overviewRoutes} /> <RouteWithChildRoutes path="dashboard" childRoutes={overviewRoutes} />
<RouteWithChildRoutes path="project/activity" childRoutes={projectActivityRoutes} /> <RouteWithChildRoutes path="project/activity" childRoutes={projectActivityRoutes} />
<Route <Route
path="project/extension/:pluginKey/:extensionKey" path="project/extension/:pluginKey/:extensionKey"
component={lazyLoad(() => import('../components/extensions/ProjectPageExtension'))}
component={lazyLoadComponent(() => import('../components/extensions/ProjectPageExtension'))}
/> />
<Route <Route
path="project/issues" path="project/issues"
path="project/quality_profiles" path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes} childRoutes={projectQualityProfilesRoutes}
/> />
<Route component={lazyLoad(() => import('../components/ProjectAdminContainer'))}>
<Route component={lazyLoadComponent(() => import('../components/ProjectAdminContainer'))}>
<RouteWithChildRoutes path="custom_measures" childRoutes={customMeasuresRoutes} /> <RouteWithChildRoutes path="custom_measures" childRoutes={customMeasuresRoutes} />
<Route <Route
path="project/admin/extension/:pluginKey/:extensionKey" path="project/admin/extension/:pluginKey/:extensionKey"
component={lazyLoad(() => import('../components/extensions/ProjectAdminPageExtension'))}
component={lazyLoadComponent(() =>
import('../components/extensions/ProjectAdminPageExtension')
)}
/> />
<RouteWithChildRoutes path="project/background_tasks" childRoutes={backgroundTasksRoutes} /> <RouteWithChildRoutes path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
<RouteWithChildRoutes path="project/baseline" childRoutes={projectBaselineRoutes} /> <RouteWithChildRoutes path="project/baseline" childRoutes={projectBaselineRoutes} />
<RouteWithChildRoutes path="project/webhooks" childRoutes={webhooksRoutes} /> <RouteWithChildRoutes path="project/webhooks" childRoutes={webhooksRoutes} />
<Route <Route
path="project/deletion" path="project/deletion"
component={lazyLoad(() => import('../../apps/projectDeletion/App'))}
component={lazyLoadComponent(() => import('../../apps/projectDeletion/App'))}
/> />
<Route <Route
path="project/links" path="project/links"
component={lazyLoad(() => import('../../apps/projectLinks/App'))}
component={lazyLoadComponent(() => import('../../apps/projectLinks/App'))}
/>
<Route
path="project/key"
component={lazyLoadComponent(() => import('../../apps/projectKey/Key'))}
/> />
<Route path="project/key" component={lazyLoad(() => import('../../apps/projectKey/Key'))} />
</Route> </Route>
</Route> </Route>
); );


function renderAdminRoutes() { function renderAdminRoutes() {
return ( return (
<Route component={lazyLoad(() => import('../components/AdminContainer'))} path="admin">
<Route component={lazyLoadComponent(() => import('../components/AdminContainer'))} path="admin">
<Route <Route
path="extension/:pluginKey/:extensionKey" path="extension/:pluginKey/:extensionKey"
component={lazyLoad(() => import('../components/extensions/GlobalAdminPageExtension'))}
component={lazyLoadComponent(() =>
import('../components/extensions/GlobalAdminPageExtension')
)}
/> />
<RouteWithChildRoutes path="background_tasks" childRoutes={backgroundTasksRoutes} /> <RouteWithChildRoutes path="background_tasks" childRoutes={backgroundTasksRoutes} />
<RouteWithChildRoutes path="custom_metrics" childRoutes={customMetricsRoutes} /> <RouteWithChildRoutes path="custom_metrics" childRoutes={customMetricsRoutes} />


<Route <Route
path="markdown/help" path="markdown/help"
component={lazyLoad(() => import('../components/MarkdownHelp'))}
component={lazyLoadComponent(() => import('../components/MarkdownHelp'))}
/> />


<Route component={lazyLoad(() => import('../components/SimpleContainer'))}>
<Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
<Route path="maintenance">{maintenanceRoutes}</Route> <Route path="maintenance">{maintenanceRoutes}</Route>
<Route path="setup">{setupRoutes}</Route> <Route path="setup">{setupRoutes}</Route>
</Route> </Route>


<Route component={MigrationContainer}> <Route component={MigrationContainer}>
<Route component={lazyLoad(() => import('../components/SimpleSessionsContainer'))}>
<Route
component={lazyLoadComponent(() =>
import('../components/SimpleSessionsContainer')
)}>
<RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} /> <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
</Route> </Route>


<Route path="/" component={App}> <Route path="/" component={App}>
<IndexRoute component={lazyLoad(() => import('../components/Landing'))} />
<IndexRoute
component={lazyLoadComponent(() => import('../components/Landing'))}
/>
<RouteWithChildRoutes path="about" childRoutes={aboutRoutes} /> <RouteWithChildRoutes path="about" childRoutes={aboutRoutes} />


<Route component={GlobalContainer}> <Route component={GlobalContainer}>
</Route> </Route>
<Route <Route
path="extension/:pluginKey/:extensionKey" path="extension/:pluginKey/:extensionKey"
component={lazyLoad(() =>
component={lazyLoadComponent(() =>
import('../components/extensions/GlobalPageExtension') import('../components/extensions/GlobalPageExtension')
)} )}
/> />
<Route path="issues" component={IssuesPageSelector} />
<Route path="issues" component={Issues} />
<RouteWithChildRoutes path="onboarding" childRoutes={onboardingRoutes} /> <RouteWithChildRoutes path="onboarding" childRoutes={onboardingRoutes} />
<RouteWithChildRoutes path="organizations" childRoutes={organizationsRoutes} /> <RouteWithChildRoutes path="organizations" childRoutes={organizationsRoutes} />
<RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} /> <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
<RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} /> <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
<Route <Route
path="portfolios" path="portfolios"
component={lazyLoad(() => import('../components/extensions/PortfoliosPage'))}
component={lazyLoadComponent(() =>
import('../components/extensions/PortfoliosPage')
)}
/> />
<RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} /> <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
<RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} /> <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
</Route> </Route>
<Route <Route
path="not_found" path="not_found"
component={lazyLoad(() => import('../components/NotFound'))}
component={lazyLoadComponent(() => import('../components/NotFound'))}
/>
<Route
path="*"
component={lazyLoadComponent(() => import('../components/NotFound'))}
/> />
<Route path="*" component={lazyLoad(() => import('../components/NotFound'))} />
</Route> </Route>
</Route> </Route>
</Router> </Router>

+ 4
- 2
server/sonar-web/src/main/js/apps/about/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [{ indexRoute: { component: lazyLoad(() => import('./components/AboutApp')) } }];
const routes = [
{ indexRoute: { component: lazyLoadComponent(() => import('./components/AboutApp')) } }
];


export default routes; export default routes;

+ 7
- 7
server/sonar-web/src/main/js/apps/account/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
component: lazyLoad(() => import('./components/Account')),
component: lazyLoadComponent(() => import('./components/Account')),
childRoutes: [ childRoutes: [
{ {
indexRoute: { component: lazyLoad(() => import('./profile/Profile')) }
indexRoute: { component: lazyLoadComponent(() => import('./profile/Profile')) }
}, },
{ {
path: 'security', path: 'security',
component: lazyLoad(() => import('./components/Security'))
component: lazyLoadComponent(() => import('./components/Security'))
}, },
{ {
path: 'projects', path: 'projects',
component: lazyLoad(() => import('./projects/ProjectsContainer'))
component: lazyLoadComponent(() => import('./projects/ProjectsContainer'))
}, },
{ {
path: 'notifications', path: 'notifications',
component: lazyLoad(() => import('./notifications/Notifications'))
component: lazyLoadComponent(() => import('./notifications/Notifications'))
}, },
{ {
path: 'organizations', path: 'organizations',
component: lazyLoad(() => import('./organizations/UserOrganizations'))
component: lazyLoadComponent(() => import('./organizations/UserOrganizations'))
} }
] ]
} }

+ 2
- 2
server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx View File

ActionsDropdownItem ActionsDropdownItem
} from 'sonar-ui-common/components/controls/ActionsDropdown'; } from 'sonar-ui-common/components/controls/ActionsDropdown';
import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal'; import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { STATUSES } from '../constants'; import { STATUSES } from '../constants';
import ScannerContext from './ScannerContext'; import ScannerContext from './ScannerContext';
import Stacktrace from './Stacktrace'; import Stacktrace from './Stacktrace';


const AnalysisWarningsModal = lazyLoad(
const AnalysisWarningsModal = lazyLoadComponent(
() => import('../../../components/common/AnalysisWarningsModal'), () => import('../../../components/common/AnalysisWarningsModal'),
'AnalysisWarningsModal' 'AnalysisWarningsModal'
); );

+ 2
- 2
server/sonar-web/src/main/js/apps/background-tasks/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/BackgroundTasksApp')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/BackgroundTasksApp')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/code/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/coding-rules/routes.ts View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { RedirectFunction, RouterState } from 'react-router'; import { RedirectFunction, RouterState } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { parseQuery, serializeQuery } from './query'; import { parseQuery, serializeQuery } from './query';


function parseHash(hash: string): T.RawQuery { function parseHash(hash: string): T.RawQuery {
replace({ pathname: nextState.location.pathname, query: normalizedQuery }); replace({ pathname: nextState.location.pathname, query: normalizedQuery });
} }
}, },
component: lazyLoad(() => import('./components/App'))
component: lazyLoadComponent(() => import('./components/App'))
} }
} }
]; ];

+ 2
- 2
server/sonar-web/src/main/js/apps/component-measures/routes.ts View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { RedirectFunction, RouterState } from 'react-router'; import { RedirectFunction, RouterState } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
}, },
{ {
path: 'domain/:domainName', path: 'domain/:domainName',

+ 2
- 2
server/sonar-web/src/main/js/apps/custom-measures/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/custom-metrics/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/documentation/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const App = lazyLoad(() => import(/* webpackChunkName: "docs" */ './components/App'));
const App = lazyLoadComponent(() => import(/* webpackChunkName: "docs" */ './components/App'));


const routes = [{ indexRoute: { component: App } }, { path: '**', indexRoute: { component: App } }]; const routes = [{ indexRoute: { component: App } }, { path: '**', indexRoute: { component: App } }];



+ 2
- 2
server/sonar-web/src/main/js/apps/groups/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 0
- 37
server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx View File

/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { withCurrentUser } from '../../components/hoc/withCurrentUser';
import { Location } from '../../components/hoc/withRouter';
import { isSonarCloud } from '../../helpers/system';
import { isLoggedIn } from '../../helpers/users';
import AppContainer from './components/AppContainer';

export interface Props {
currentUser: T.CurrentUser;
location: Location;
}

export function IssuesPage({ currentUser, location }: Props) {
const myIssues = (isLoggedIn(currentUser) && isSonarCloud()) || undefined;
return <AppContainer location={location} myIssues={myIssues} />;
}

export default withCurrentUser(IssuesPage);

+ 0
- 56
server/sonar-web/src/main/js/apps/issues/__tests__/IssuesPageSelector-test.tsx View File

/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { isSonarCloud } from '../../../helpers/system';
import { mockCurrentUser, mockLocation, mockLoggedInUser } from '../../../helpers/testMocks';
import { IssuesPage, Props } from '../IssuesPageSelector';

jest.mock('../../../helpers/system', () => ({ isSonarCloud: jest.fn().mockReturnValue(false) }));

it('should render normal issues page', () => {
expect(shallowRender()).toMatchSnapshot();
expect(
shallowRender({ currentUser: mockLoggedInUser() })
.find('Connect(IssuesAppContainer)')
.prop('myIssues')
).toBeFalsy();
(isSonarCloud as jest.Mock).mockReturnValueOnce(true);
expect(
shallowRender()
.find('Connect(IssuesAppContainer)')
.prop('myIssues')
).toBeFalsy();
});

it('should render my issues page', () => {
(isSonarCloud as jest.Mock).mockReturnValueOnce(true);
expect(
shallowRender({ currentUser: mockLoggedInUser() })
.find('Connect(IssuesAppContainer)')
.prop('myIssues')
).toBeTruthy();
});

function shallowRender(props: Partial<Props> = {}) {
return shallow(
<IssuesPage currentUser={mockCurrentUser()} location={mockLocation()} {...props} />
);
}

+ 0
- 17
server/sonar-web/src/main/js/apps/issues/__tests__/__snapshots__/IssuesPageSelector-test.tsx.snap View File

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render normal issues page 1`] = `
<Connect(IssuesAppContainer)
location={
Object {
"action": "PUSH",
"hash": "",
"key": "key",
"pathname": "/path",
"query": Object {},
"search": "",
"state": Object {},
}
}
/>
`;

+ 9
- 14
server/sonar-web/src/main/js/apps/issues/components/App.tsx View File

import * as React from 'react'; import * as React from 'react';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Button } from 'sonar-ui-common/components/controls/buttons'; import { Button } from 'sonar-ui-common/components/controls/buttons';
import Checkbox from 'sonar-ui-common/components/controls/Checkbox'; import Checkbox from 'sonar-ui-common/components/controls/Checkbox';
import ListFooter from 'sonar-ui-common/components/controls/ListFooter'; import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
import EmptySearch from '../../../components/common/EmptySearch'; import EmptySearch from '../../../components/common/EmptySearch';
import FiltersHeader from '../../../components/common/FiltersHeader'; import FiltersHeader from '../../../components/common/FiltersHeader';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
import { Location, Router } from '../../../components/hoc/withRouter';
import '../../../components/search-navigator.css'; import '../../../components/search-navigator.css';
import { import {
fillBranchLike, fillBranchLike,
isSameBranchLike isSameBranchLike
} from '../../../helpers/branch-like'; } from '../../../helpers/branch-like';
import { isSonarCloud } from '../../../helpers/system'; import { isSonarCloud } from '../../../helpers/system';
import { fetchBranchStatus } from '../../../store/rootActions';
import { BranchLike } from '../../../types/branch-like'; import { BranchLike } from '../../../types/branch-like';
import * as actions from '../actions'; import * as actions from '../actions';
import ConciseIssuesList from '../conciseIssuesList/ConciseIssuesList'; import ConciseIssuesList from '../conciseIssuesList/ConciseIssuesList';
fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise<void>; fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise<void>;
fetchIssues: (query: T.RawQuery, requestOrganizations?: boolean) => Promise<FetchIssuesPromise>; fetchIssues: (query: T.RawQuery, requestOrganizations?: boolean) => Promise<FetchIssuesPromise>;
hideAuthorFacet?: boolean; hideAuthorFacet?: boolean;
location: Pick<Location, 'pathname' | 'query'>;
location: Location;
multiOrganizations?: boolean; multiOrganizations?: boolean;
myIssues?: boolean; myIssues?: boolean;
onBranchesChange: () => void;
onBranchesChange?: () => void;
organization?: { key: string }; organization?: { key: string };
router: Pick<Router, 'push' | 'replace'>; router: Pick<Router, 'push' | 'replace'>;
userOrganizations: T.Organization[]; userOrganizations: T.Organization[];
const DEFAULT_QUERY = { resolved: 'false' }; const DEFAULT_QUERY = { resolved: 'false' };
const MAX_INITAL_FETCH = 1000; const MAX_INITAL_FETCH = 1000;


export class App extends React.PureComponent<Props, State> {
export default class App extends React.PureComponent<Props, State> {
mounted = false; mounted = false;


constructor(props: Props) { constructor(props: Props) {
const { paging } = this.state; const { paging } = this.state;


if (!paging) { if (!paging) {
return;
return Promise.reject();
} }


const p = paging.pageIndex + 1; const p = paging.pageIndex + 1;


this.setState({ checkAll: false, loadingMore: true }); this.setState({ checkAll: false, loadingMore: true });
this.fetchIssuesPage(p).then(
return this.fetchIssuesPage(p).then(
response => { response => {
if (this.mounted) { if (this.mounted) {
this.setState(state => ({ this.setState(state => ({
handleReload = () => { handleReload = () => {
this.fetchFirstIssues(); this.fetchFirstIssues();
this.refreshBranchStatus(); this.refreshBranchStatus();
if (isPullRequest(this.props.branchLike)) {
this.props.onBranchesChange();
const { branchLike, onBranchesChange } = this.props;
if (onBranchesChange && isPullRequest(branchLike)) {
onBranchesChange();
} }
}; };


); );
} }
} }

const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any };

export default withRouter(connect(null, mapDispatchToProps)(App));

+ 10
- 10
server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx View File

import { uniq } from 'lodash'; import { uniq } from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { searchIssues } from '../../../api/issues'; import { searchIssues } from '../../../api/issues';
import { getOrganizations } from '../../../api/organizations'; import { getOrganizations } from '../../../api/organizations';
import throwGlobalError from '../../../app/utils/throwGlobalError'; import throwGlobalError from '../../../app/utils/throwGlobalError';
import { withRouter } from '../../../components/hoc/withRouter';
import { parseIssueFromResponse } from '../../../helpers/issues'; import { parseIssueFromResponse } from '../../../helpers/issues';
import { receiveOrganizations } from '../../../store/organizations'; import { receiveOrganizations } from '../../../store/organizations';
import { fetchBranchStatus } from '../../../store/rootActions';
import { import {
areThereCustomOrganizations, areThereCustomOrganizations,
getCurrentUser, getCurrentUser,
Store Store
} from '../../../store/rootReducer'; } from '../../../store/rootReducer';


const IssuesAppContainer = lazyLoadComponent(() => import('./App'), 'IssuesAppContainer');

interface StateProps { interface StateProps {
currentUser: T.CurrentUser; currentUser: T.CurrentUser;
userOrganizations: T.Organization[]; userOrganizations: T.Organization[];
.catch(throwGlobalError); .catch(throwGlobalError);
}; };


interface DispatchProps {
fetchIssues: (query: T.RawQuery, requestOrganizations?: boolean) => Promise<void>;
}

// have to type cast this, because of async action // have to type cast this, because of async action
const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps;
const mapDispatchToProps = {
fetchBranchStatus: fetchBranchStatus as any,
fetchIssues: fetchIssues as any
};


export default connect(
mapStateToProps,
mapDispatchToProps
)(lazyLoad(() => import('./App'), 'IssuesAppContainer'));
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(IssuesAppContainer));

+ 0
- 3
server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { uniq } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { scrollToElement } from 'sonar-ui-common/helpers/scrolling'; import { scrollToElement } from 'sonar-ui-common/helpers/scrolling';
import SourceViewer from '../../../components/SourceViewer/SourceViewer'; import SourceViewer from '../../../components/SourceViewer/SourceViewer';
: undefined; : undefined;


if (locations.length > 0) { if (locations.length > 0) {
const components = uniq(locations.map(l => l.component));
return ( return (
<div ref={node => (this.node = node)}> <div ref={node => (this.node = node)}>
<CrossComponentSourceViewer <CrossComponentSourceViewer
branchLike={this.props.branchLike} branchLike={this.props.branchLike}
components={components}
highlightedLocationMessage={highlightedLocationMessage} highlightedLocationMessage={highlightedLocationMessage}
issue={openIssue} issue={openIssue}
issues={this.props.issues} issues={this.props.issues}

+ 3
- 3
server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx View File

selectPreviousFlow, selectPreviousFlow,
selectPreviousLocation selectPreviousLocation
} from '../../actions'; } from '../../actions';
import { App } from '../App';
import App from '../App';


jest.mock('sonar-ui-common/helpers/handleRequiredAuthentication', () => ({ jest.mock('sonar-ui-common/helpers/handleRequiredAuthentication', () => ({
default: jest.fn() default: jest.fn()


it('should open standard facets for vulnerabilities and hotspots', () => { it('should open standard facets for vulnerabilities and hotspots', () => {
const wrapper = shallowRender({ const wrapper = shallowRender({
location: { pathname: '/issues', query: { types: 'VULNERABILITY' } }
location: mockLocation({ pathname: '/issues', query: { types: 'VULNERABILITY' } })
}); });
const instance = wrapper.instance(); const instance = wrapper.instance();
const fetchFacet = jest.spyOn(instance, 'fetchFacet'); const fetchFacet = jest.spyOn(instance, 'fetchFacet');
rules: [], rules: [],
users: [] users: []
})} })}
location={{ pathname: '/issues', query: {} }}
location={mockLocation({ pathname: '/issues', query: {} })}
onBranchesChange={() => {}} onBranchesChange={() => {}}
organization={{ key: 'foo' }} organization={{ key: 'foo' }}
router={mockRouter()} router={mockRouter()}

+ 0
- 5
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesSourceViewer-test.tsx.snap View File

"name": "master", "name": "master",
} }
} }
components={
Array [
"main.js",
]
}
issue={ issue={
Object { Object {
"actions": Array [], "actions": Array [],

+ 2
- 2
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const CrossComponentSourceViewer = lazyLoad(
const CrossComponentSourceViewer = lazyLoadComponent(
() => import(/* webpackPrefetch: true */ './CrossComponentSourceViewerWrapper'), () => import(/* webpackPrefetch: true */ './CrossComponentSourceViewerWrapper'),
'CrossComponentSourceViewer' 'CrossComponentSourceViewer'
); );

+ 3
- 3
server/sonar-web/src/main/js/apps/maintenance/routes.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import { IndexRoute } from 'react-router'; import { IndexRoute } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


export const maintenanceRoutes = ( export const maintenanceRoutes = (
<IndexRoute component={lazyLoad(() => import('./components/MaintenanceAppContainer'))} />
<IndexRoute component={lazyLoadComponent(() => import('./components/MaintenanceAppContainer'))} />
); );


export const setupRoutes = ( export const setupRoutes = (
<IndexRoute component={lazyLoad(() => import('./components/SetupAppContainer'))} />
<IndexRoute component={lazyLoadComponent(() => import('./components/SetupAppContainer'))} />
); );

+ 5
- 2
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx View File

import tooltipDE from 'Docs/tooltips/editions/developer.md'; import tooltipDE from 'Docs/tooltips/editions/developer.md';
import tooltipEE from 'Docs/tooltips/editions/enterprise.md'; import tooltipEE from 'Docs/tooltips/editions/enterprise.md';
import * as React from 'react'; import * as React from 'react';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { translate } from 'sonar-ui-common/helpers/l10n'; import { translate } from 'sonar-ui-common/helpers/l10n';
import { getEditionUrl } from '../../../helpers/editions'; import { getEditionUrl } from '../../../helpers/editions';
import { Edition, EditionKey } from '../../../types/editions'; import { Edition, EditionKey } from '../../../types/editions';


const DocMarkdownBlock = lazyLoad(() => import('../../../components/docs/DocMarkdownBlock'));
const DocMarkdownBlock = lazyLoadComponent(
() => import('../../../components/docs/DocMarkdownBlock'),
'DocMarkdownBlock'
);


interface Props { interface Props {
currentEdition?: EditionKey; currentEdition?: EditionKey;

+ 1
- 1
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap View File

<div <div
className="boxed-group boxed-group-inner marketplace-edition" className="boxed-group boxed-group-inner marketplace-edition"
> >
<LazyLoader />
<DocMarkdownBlock />
<div <div
className="marketplace-edition-action spacer-top" className="marketplace-edition-action spacer-top"
> >

+ 2
- 2
server/sonar-web/src/main/js/apps/marketplace/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./AppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./AppContainer')) }
} }
]; ];



+ 1
- 5
server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.tsx View File

export default function OrganizationProjects(props: Props) { export default function OrganizationProjects(props: Props) {
return ( return (
<> <>
<AllProjectsContainer
isFavorite={false}
location={props.location}
organization={props.organization}
/>
<AllProjectsContainer isFavorite={false} organization={props.organization} />
<Suggestions suggestions="organization_projects" /> <Suggestions suggestions="organization_projects" />
</> </>
); );

+ 29
- 13
server/sonar-web/src/main/js/apps/organizations/routes.ts View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { RedirectFunction, RouterState } from 'react-router'; import { RedirectFunction, RouterState } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import codingRulesRoutes from '../coding-rules/routes'; import codingRulesRoutes from '../coding-rules/routes';
import qualityGatesRoutes from '../quality-gates/routes'; import qualityGatesRoutes from '../quality-gates/routes';
import qualityProfilesRoutes from '../quality-profiles/routes'; import qualityProfilesRoutes from '../quality-profiles/routes';
import webhooksRoutes from '../webhooks/routes'; import webhooksRoutes from '../webhooks/routes';


const OrganizationContainer = lazyLoad(() => import('./components/OrganizationContainer'));
const OrganizationContainer = lazyLoadComponent(() => import('./components/OrganizationContainer'));


const routes = [ const routes = [
{ {
path: ':organizationKey', path: ':organizationKey',
component: lazyLoad(() => import('./components/OrganizationPage')),
component: lazyLoadComponent(() => import('./components/OrganizationPage')),
childRoutes: [ childRoutes: [
{ {
indexRoute: { indexRoute: {
path: 'projects', path: 'projects',
component: OrganizationContainer, component: OrganizationContainer,
childRoutes: [ childRoutes: [
{ indexRoute: { component: lazyLoad(() => import('./components/OrganizationProjects')) } }
{
indexRoute: {
component: lazyLoadComponent(() => import('./components/OrganizationProjects'))
}
}
] ]
}, },
{ {
path: 'issues', path: 'issues',
component: OrganizationContainer, component: OrganizationContainer,
childRoutes: [ childRoutes: [
{ indexRoute: { component: lazyLoad(() => import('../issues/components/AppContainer')) } }
{
indexRoute: {
component: lazyLoadComponent(() => import('../issues/components/AppContainer'))
}
}
] ]
}, },
{ {
}, },
{ {
path: 'members', path: 'members',
component: lazyLoad(() => import('../organizationMembers/OrganizationMembersContainer'))
component: lazyLoadComponent(() =>
import('../organizationMembers/OrganizationMembersContainer')
)
}, },
{ {
path: 'quality_profiles', path: 'quality_profiles',
childRoutes: qualityGatesRoutes childRoutes: qualityGatesRoutes
}, },
{ {
component: lazyLoad(() => import('./components/OrganizationAccessContainer')),
component: lazyLoadComponent(() => import('./components/OrganizationAccessContainer')),
childRoutes: [ childRoutes: [
{ path: 'edit', component: lazyLoad(() => import('./components/OrganizationEdit')) },
{ path: 'groups', component: lazyLoad(() => import('../groups/components/App')) },
{
path: 'edit',
component: lazyLoadComponent(() => import('./components/OrganizationEdit'))
},
{
path: 'groups',
component: lazyLoadComponent(() => import('../groups/components/App'))
},
{ {
path: 'permissions', path: 'permissions',
component: lazyLoad(() => import('../permissions/global/components/App'))
component: lazyLoadComponent(() => import('../permissions/global/components/App'))
}, },
{ {
path: 'permission_templates', path: 'permission_templates',
component: lazyLoad(() => import('../permission-templates/components/App'))
component: lazyLoadComponent(() => import('../permission-templates/components/App'))
}, },
{ {
path: 'projects_management', path: 'projects_management',
component: lazyLoad(() => import('../projectsManagement/AppContainer'))
component: lazyLoadComponent(() => import('../projectsManagement/AppContainer'))
}, },
{ path: 'webhooks', childRoutes: webhooksRoutes }, { path: 'webhooks', childRoutes: webhooksRoutes },
{ {
path: 'extension/:pluginKey/:extensionKey', path: 'extension/:pluginKey/:extensionKey',
component: lazyLoad(() =>
component: lazyLoadComponent(() =>
import('../../app/components/extensions/OrganizationPageExtension') import('../../app/components/extensions/OrganizationPageExtension')
) )
} }

+ 3
- 3
server/sonar-web/src/main/js/apps/overview/components/App.tsx View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import * as React from 'react'; import * as React from 'react';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { Router, withRouter } from '../../../components/hoc/withRouter'; import { Router, withRouter } from '../../../components/hoc/withRouter';
import { isPullRequest } from '../../../helpers/branch-like'; import { isPullRequest } from '../../../helpers/branch-like';
import { ComponentQualifier } from '../../../types/component'; import { ComponentQualifier } from '../../../types/component';
import BranchOverview from '../branches/BranchOverview'; import BranchOverview from '../branches/BranchOverview';


const EmptyOverview = lazyLoad(() => import('./EmptyOverview'));
const PullRequestOverview = lazyLoad(() => import('../pullRequests/PullRequestOverview'));
const EmptyOverview = lazyLoadComponent(() => import('./EmptyOverview'));
const PullRequestOverview = lazyLoadComponent(() => import('../pullRequests/PullRequestOverview'));


interface Props { interface Props {
branchLike?: BranchLike; branchLike?: BranchLike;

+ 16
- 5
server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx View File

import AfterMergeEstimate from './AfterMergeEstimate'; import AfterMergeEstimate from './AfterMergeEstimate';
import LargeQualityGateBadge from './LargeQualityGateBadge'; import LargeQualityGateBadge from './LargeQualityGateBadge';


interface Props {
branchLike: PullRequest;
component: T.Component;
interface StateProps {
conditions?: QualityGateStatusCondition[]; conditions?: QualityGateStatusCondition[];
fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise<void>;
ignoredConditions?: boolean; ignoredConditions?: boolean;
status?: T.Status; status?: T.Status;
} }


interface DispatchProps {
fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise<void>;
}

interface OwnProps {
branchLike: PullRequest;
component: T.Component;
}

type Props = StateProps & DispatchProps & OwnProps;

interface State { interface State {
loading: boolean; loading: boolean;
measures: T.MeasureEnhanced[]; measures: T.MeasureEnhanced[];


const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any }; const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any };


export default connect(mapStateToProps, mapDispatchToProps)(PullRequestOverview);
export default connect<StateProps, DispatchProps, OwnProps>(
mapStateToProps,
mapDispatchToProps
)(PullRequestOverview);

+ 2
- 2
server/sonar-web/src/main/js/apps/overview/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/permission-templates/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 3
- 3
server/sonar-web/src/main/js/apps/permissions/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


export const globalPermissionsRoutes = [ export const globalPermissionsRoutes = [
{ {
indexRoute: { component: lazyLoad(() => import('./global/components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./global/components/App')) }
} }
]; ];


export const projectPermissionsRoutes = [ export const projectPermissionsRoutes = [
{ {
indexRoute: { component: lazyLoad(() => import('./project/components/AppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./project/components/AppContainer')) }
} }
]; ];

+ 2
- 2
server/sonar-web/src/main/js/apps/portfolio/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/projectActivity/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { indexRoute: {
component: lazyLoad(() => import('./components/ProjectActivityAppContainer'))
component: lazyLoadComponent(() => import('./components/ProjectActivityAppContainer'))
} }
} }
]; ];

+ 2
- 2
server/sonar-web/src/main/js/apps/projectBaseline/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/AppContainer')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/projectBranches/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./App')) }
indexRoute: { component: lazyLoadComponent(() => import('./App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./App')) }
indexRoute: { component: lazyLoadComponent(() => import('./App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { areThereCustomOrganizations, getCurrentUser, Store } from '../../../store/rootReducer'; import { areThereCustomOrganizations, getCurrentUser, Store } from '../../../store/rootReducer';


const stateToProps = (state: Store) => ({ const stateToProps = (state: Store) => ({
organizationsEnabled: areThereCustomOrganizations(state) organizationsEnabled: areThereCustomOrganizations(state)
}); });


export default connect(stateToProps)(lazyLoad(() => import('./AllProjects')));
export default connect(stateToProps)(lazyLoadComponent(() => import('./AllProjects')));

+ 2
- 14
server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx View File



render() { render() {
if (isSonarCloud() && isLoggedIn(this.props.currentUser)) { if (isSonarCloud() && isLoggedIn(this.props.currentUser)) {
return (
<AllProjectsContainer
isFavorite={true}
location={this.props.location}
organization={undefined}
/>
);
return <AllProjectsContainer isFavorite={true} organization={undefined} />;
} }


const { shouldBeRedirected, shouldForceSorting } = this.state; const { shouldBeRedirected, shouldForceSorting } = this.state;
shouldBeRedirected !== true && shouldBeRedirected !== true &&
shouldForceSorting === undefined shouldForceSorting === undefined
) { ) {
return (
<AllProjectsContainer
isFavorite={false}
location={this.props.location}
organization={undefined}
/>
);
return <AllProjectsContainer isFavorite={false} organization={undefined} />;
} }


return null; return null;

+ 2
- 2
server/sonar-web/src/main/js/apps/projects/routes.ts View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { RedirectFunction, RouterState } from 'react-router'; import { RedirectFunction, RouterState } from 'react-router';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { save } from 'sonar-ui-common/helpers/storage'; import { save } from 'sonar-ui-common/helpers/storage';
import { isDefined } from 'sonar-ui-common/helpers/types'; import { isDefined } from 'sonar-ui-common/helpers/types';
import DefaultPageSelectorContainer from './components/DefaultPageSelectorContainer'; import DefaultPageSelectorContainer from './components/DefaultPageSelectorContainer';
{ path: 'favorite', component: FavoriteProjectsContainer }, { path: 'favorite', component: FavoriteProjectsContainer },
{ {
path: 'create', path: 'create',
component: lazyLoad(() => import('../create/project/CreateProjectPage'))
component: lazyLoadComponent(() => import('../create/project/CreateProjectPage'))
} }
].filter(isDefined); ].filter(isDefined);



+ 2
- 2
server/sonar-web/src/main/js/apps/projectsManagement/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./AppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./AppContainer')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/quality-gates/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const App = lazyLoad(() => import('./components/App'));
const App = lazyLoadComponent(() => import('./components/App'));


const routes = [{ indexRoute: { component: App } }, { path: 'show/:id', component: App }]; const routes = [{ indexRoute: { component: App } }, { path: 'show/:id', component: App }];



+ 2
- 2
server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons'; import { Button } from 'sonar-ui-common/components/controls/buttons';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n'; import { translate } from 'sonar-ui-common/helpers/l10n';
import { Profile } from '../../../api/quality-profiles'; import { Profile } from '../../../api/quality-profiles';
import { getRuleDetails } from '../../../api/rules'; import { getRuleDetails } from '../../../api/rules';


const ActivationFormModal = lazyLoad(
const ActivationFormModal = lazyLoadComponent(
() => import('../../coding-rules/components/ActivationFormModal'), () => import('../../coding-rules/components/ActivationFormModal'),
'ActivationFormModal' 'ActivationFormModal'
); );

+ 7
- 7
server/sonar-web/src/main/js/apps/quality-profiles/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
component: lazyLoad(() => import('./components/AppContainer')),
indexRoute: { component: lazyLoad(() => import('./home/HomeContainer')) },
component: lazyLoadComponent(() => import('./components/AppContainer')),
indexRoute: { component: lazyLoadComponent(() => import('./home/HomeContainer')) },
childRoutes: [ childRoutes: [
{ {
component: lazyLoad(() => import('./components/ProfileContainer')),
component: lazyLoadComponent(() => import('./components/ProfileContainer')),
childRoutes: [ childRoutes: [
{ {
path: 'show', path: 'show',
component: lazyLoad(() => import('./details/ProfileDetails'))
component: lazyLoadComponent(() => import('./details/ProfileDetails'))
}, },
{ {
path: 'changelog', path: 'changelog',
component: lazyLoad(() => import('./changelog/ChangelogContainer'))
component: lazyLoadComponent(() => import('./changelog/ChangelogContainer'))
}, },
{ {
path: 'compare', path: 'compare',
component: lazyLoad(() => import('./compare/ComparisonContainer'))
component: lazyLoadComponent(() => import('./compare/ComparisonContainer'))
} }
] ]
} }

+ 5
- 5
server/sonar-web/src/main/js/apps/sessions/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
path: 'new', path: 'new',
component: lazyLoad(() => import('./components/LoginContainer'))
component: lazyLoadComponent(() => import('./components/LoginContainer'))
}, },
{ {
path: 'logout', path: 'logout',
component: lazyLoad(() => import('./components/Logout'))
component: lazyLoadComponent(() => import('./components/Logout'))
}, },
{ {
path: 'unauthorized', path: 'unauthorized',
component: lazyLoad(() => import('./components/Unauthorized'))
component: lazyLoadComponent(() => import('./components/Unauthorized'))
}, },
{ {
path: 'email_already_exists', path: 'email_already_exists',
component: lazyLoad(() => import('./components/EmailAlreadyExists'))
component: lazyLoadComponent(() => import('./components/EmailAlreadyExists'))
} }
]; ];



+ 3
- 3
server/sonar-web/src/main/js/apps/settings/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/AppContainer')) }
}, },
{ {
path: 'encryption', path: 'encryption',
component: lazyLoad(() => import('./encryption/EncryptionApp'))
component: lazyLoadComponent(() => import('./encryption/EncryptionApp'))
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/system/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 5
server/sonar-web/src/main/js/apps/tutorials/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { isSonarCloud } from '../../helpers/system';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { indexRoute: {
component: lazyLoad(() =>
isSonarCloud() ? import('./onboarding/OnboardingPage') : import('./ProjectOnboardingPage')
)
component: lazyLoadComponent(() => import('./ProjectOnboardingPage'))
} }
} }
]; ];

+ 2
- 2
server/sonar-web/src/main/js/apps/users/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./UsersAppContainer')) }
indexRoute: { component: lazyLoadComponent(() => import('./UsersAppContainer')) }
} }
]; ];



+ 3
- 3
server/sonar-web/src/main/js/apps/web-api/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/WebApiApp')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/WebApiApp')) }
}, },
{ {
path: '**', path: '**',
component: lazyLoad(() => import('./components/WebApiApp'))
component: lazyLoadComponent(() => import('./components/WebApiApp'))
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/apps/webhooks/routes.ts View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const routes = [ const routes = [
{ {
indexRoute: { component: lazyLoad(() => import('./components/App')) }
indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
} }
]; ];



+ 2
- 2
server/sonar-web/src/main/js/components/SourceViewer/SourceViewer.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';


const SourceViewer = lazyLoad(
const SourceViewer = lazyLoadComponent(
() => import(/* webpackPrefetch: true */ './SourceViewerBase'), () => import(/* webpackPrefetch: true */ './SourceViewerBase'),
'SourceViewer' 'SourceViewer'
); );

+ 99
- 103
server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx View File

// but kept to maintaint the location indexes // but kept to maintaint the location indexes
highlightedLocations?: (T.FlowLocation | undefined)[]; highlightedLocations?: (T.FlowLocation | undefined)[];
highlightedLocationMessage?: { index: number; text: string | undefined }; highlightedLocationMessage?: { index: number; text: string | undefined };
loadComponent: (
loadComponent?: (
component: string, component: string,
branchLike: BranchLike | undefined branchLike: BranchLike | undefined
) => Promise<T.SourceViewerFile>; ) => Promise<T.SourceViewerFile>;
loadIssues: (
loadIssues?: (
component: string, component: string,
from: number, from: number,
to: number, to: number,
branchLike: BranchLike | undefined branchLike: BranchLike | undefined
) => Promise<T.Issue[]>; ) => Promise<T.Issue[]>;
loadSources: (
loadSources?: (
component: string, component: string,
from: number, from: number,
to: number, to: number,
displayAllIssues: false, displayAllIssues: false,
displayIssueLocationsCount: true, displayIssueLocationsCount: true,
displayIssueLocationsLink: true, displayIssueLocationsLink: true,
displayLocationMarkers: true,
loadComponent: defaultLoadComponent,
loadIssues: defaultLoadIssues,
loadSources: defaultLoadSources
displayLocationMarkers: true
}; };


constructor(props: Props) { constructor(props: Props) {
this.mounted = false; this.mounted = false;
} }


get loadComponent() {
return this.props.loadComponent || defaultLoadComponent;
}

get loadIssues() {
return this.props.loadIssues || defaultLoadIssues;
}

get propsLoadSources() {
return this.props.loadSources || defaultLoadSources;
}

computeCoverageStatus(lines: T.SourceLine[]) { computeCoverageStatus(lines: T.SourceLine[]) {
return lines.map(line => ({ ...line, coverageStatus: getCoverageStatus(line) })); return lines.map(line => ({ ...line, coverageStatus: getCoverageStatus(line) }));
} }


const to = (this.props.aroundLine || 0) + LINES; const to = (this.props.aroundLine || 0) + LINES;
const loadIssues = (component: T.SourceViewerFile, sources: T.SourceLine[]) => { const loadIssues = (component: T.SourceViewerFile, sources: T.SourceLine[]) => {
this.props.loadIssues(this.props.component, 1, to, this.props.branchLike).then(
this.loadIssues(this.props.component, 1, to, this.props.branchLike).then(
issues => { issues => {
if (this.mounted) { if (this.mounted) {
const finalSources = sources.slice(0, LINES); const finalSources = sources.slice(0, LINES);
); );
}; };


this.props
.loadComponent(this.props.component, this.props.branchLike)
.then(onResolve, onFailLoadComponent);
this.loadComponent(this.props.component, this.props.branchLike).then(
onResolve,
onFailLoadComponent
);
} }


fetchSources() { fetchSources() {
} }
const firstSourceLine = this.state.sources[0]; const firstSourceLine = this.state.sources[0];
const lastSourceLine = this.state.sources[this.state.sources.length - 1]; const lastSourceLine = this.state.sources[this.state.sources.length - 1];
this.props
.loadIssues(
this.props.component,
firstSourceLine && firstSourceLine.line,
lastSourceLine && lastSourceLine.line,
this.props.branchLike
)
.then(
issues => {
if (this.mounted) {
this.setState({
issues,
issuesByLine: issuesByLine(issues),
issueLocationsByLine: locationsByLine(issues)
});
}
},
() => {
// TODO
this.loadIssues(
this.props.component,
firstSourceLine && firstSourceLine.line,
lastSourceLine && lastSourceLine.line,
this.props.branchLike
).then(
issues => {
if (this.mounted) {
this.setState({
issues,
issuesByLine: issuesByLine(issues),
issueLocationsByLine: locationsByLine(issues)
});
} }
);
},
() => {
// TODO
}
);
} }


loadSources = (): Promise<T.SourceLine[]> => { loadSources = (): Promise<T.SourceLine[]> => {
// request one additional line to define `hasSourcesAfter` // request one additional line to define `hasSourcesAfter`
to++; to++;


return this.props
.loadSources(this.props.component, from, to, this.props.branchLike)
.then(sources => resolve(sources), onFailLoadSources);
return this.propsLoadSources(this.props.component, from, to, this.props.branchLike).then(
sources => resolve(sources),
onFailLoadSources
);
}); });
}; };


const firstSourceLine = this.state.sources[0]; const firstSourceLine = this.state.sources[0];
this.setState({ loadingSourcesBefore: true }); this.setState({ loadingSourcesBefore: true });
const from = Math.max(1, firstSourceLine.line - LINES); const from = Math.max(1, firstSourceLine.line - LINES);
this.props
.loadSources(this.props.component, from, firstSourceLine.line - 1, this.props.branchLike)
.then(
sources => {
this.props
.loadIssues(this.props.component, from, firstSourceLine.line - 1, this.props.branchLike)
.then(
issues => {
if (this.mounted) {
this.setState(prevState => {
const nextIssues = uniqBy(
[...issues, ...(prevState.issues || [])],
issue => issue.key
);
return {
issues: nextIssues,
issuesByLine: issuesByLine(nextIssues),
issueLocationsByLine: locationsByLine(nextIssues),
loadingSourcesBefore: false,
sources: [
...this.computeCoverageStatus(sources),
...(prevState.sources || [])
],
symbolsByLine: { ...prevState.symbolsByLine, ...symbolsByLine(sources) }
};
});
}
},
() => {
// TODO
}
);
},
() => {
// TODO
Promise.all([
this.propsLoadSources(
this.props.component,
from,
firstSourceLine.line - 1,
this.props.branchLike
),
this.loadIssues(this.props.component, from, firstSourceLine.line - 1, this.props.branchLike)
]).then(
([sources, issues]) => {
if (this.mounted) {
this.setState(prevState => {
const nextIssues = uniqBy([...issues, ...(prevState.issues || [])], issue => issue.key);
return {
issues: nextIssues,
issuesByLine: issuesByLine(nextIssues),
issueLocationsByLine: locationsByLine(nextIssues),
loadingSourcesBefore: false,
sources: [...this.computeCoverageStatus(sources), ...(prevState.sources || [])],
symbolsByLine: { ...prevState.symbolsByLine, ...symbolsByLine(sources) }
};
});
} }
);
},
() => {
// TODO
}
);
}; };


loadSourcesAfter = () => { loadSourcesAfter = () => {
const fromLine = lastSourceLine.line + 1; const fromLine = lastSourceLine.line + 1;
// request one additional line to define `hasSourcesAfter` // request one additional line to define `hasSourcesAfter`
const toLine = lastSourceLine.line + LINES + 1; const toLine = lastSourceLine.line + LINES + 1;
this.props.loadSources(this.props.component, fromLine, toLine, this.props.branchLike).then(
sources => {
this.props.loadIssues(this.props.component, fromLine, toLine, this.props.branchLike).then(
issues => {
if (this.mounted) {
this.setState(prevState => {
const nextIssues = uniqBy(
[...(prevState.issues || []), ...issues],
issue => issue.key
);
return {
issues: nextIssues,
issuesByLine: issuesByLine(nextIssues),
issueLocationsByLine: locationsByLine(nextIssues),
hasSourcesAfter: sources.length > LINES,
loadingSourcesAfter: false,
sources: [
...(prevState.sources || []),
...this.computeCoverageStatus(sources.slice(0, LINES))
],
symbolsByLine: {
...prevState.symbolsByLine,
...symbolsByLine(sources.slice(0, LINES))
}
};
});
}
},
() => {
// TODO
}
);
Promise.all([
this.propsLoadSources(this.props.component, fromLine, toLine, this.props.branchLike),
this.loadIssues(this.props.component, fromLine, toLine, this.props.branchLike)
]).then(
([sources, issues]) => {
if (this.mounted) {
this.setState(prevState => {
const nextIssues = uniqBy([...(prevState.issues || []), ...issues], issue => issue.key);
return {
issues: nextIssues,
issuesByLine: issuesByLine(nextIssues),
issueLocationsByLine: locationsByLine(nextIssues),
hasSourcesAfter: sources.length > LINES,
loadingSourcesAfter: false,
sources: [
...(prevState.sources || []),
...this.computeCoverageStatus(sources.slice(0, LINES))
],
symbolsByLine: {
...prevState.symbolsByLine,
...symbolsByLine(sources.slice(0, LINES))
}
};
});
}
}, },
() => { () => {
// TODO // TODO
} }
} }


function defaultLoadComponent(component: string, branchLike: BranchLike | undefined) {
function defaultLoadComponent(
component: string,
branchLike: BranchLike | undefined
): Promise<T.SourceViewerFile> {
return Promise.all([ return Promise.all([
getComponentForSourceViewer({ component, ...getBranchLikeQuery(branchLike) }), getComponentForSourceViewer({ component, ...getBranchLikeQuery(branchLike) }),
getComponentData({ component, ...getBranchLikeQuery(branchLike) }) getComponentData({ component, ...getBranchLikeQuery(branchLike) })

+ 127
- 4
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerBase-test.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { shallow } from 'enzyme';
import * as React from 'react'; import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { getComponentData, getComponentForSourceViewer, getSources } from '../../../api/components';
import { mockMainBranch } from '../../../helpers/mocks/branch-like'; import { mockMainBranch } from '../../../helpers/mocks/branch-like';
import { mockIssue, mockSourceLine, mockSourceViewerFile } from '../../../helpers/testMocks';
import defaultLoadIssues from '../helpers/loadIssues';
import SourceViewerBase from '../SourceViewerBase'; import SourceViewerBase from '../SourceViewerBase';


it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
jest.mock('../helpers/loadIssues', () => ({
default: jest.fn().mockRejectedValue({})
}));

jest.mock('../../../api/components', () => ({
getComponentForSourceViewer: jest.fn().mockRejectedValue(''),
getComponentData: jest.fn().mockRejectedValue(''),
getSources: jest.fn().mockRejectedValue('')
}));

beforeEach(() => {
jest.resetAllMocks();
});

it('should render nothing from the start', () => {
expect(shallowRender().type()).toBeNull();
});

it('should render correctly', async () => {
(defaultLoadIssues as jest.Mock).mockResolvedValueOnce([mockIssue()]);
(getComponentForSourceViewer as jest.Mock).mockResolvedValueOnce(mockSourceViewerFile());
(getComponentData as jest.Mock).mockResolvedValueOnce({
component: { leakPeriodDate: '2018-06-20T17:12:19+0200' }
});
(getSources as jest.Mock).mockResolvedValueOnce([]);

const wrapper = shallowRender();
await waitAndUpdate(wrapper);

expect(wrapper).toMatchSnapshot();
});

it('should use load props if provided', () => {
const loadComponent = jest.fn().mockResolvedValue({});
const loadIssues = jest.fn().mockResolvedValue([]);
const loadSources = jest.fn().mockResolvedValue([]);
const wrapper = shallowRender({
loadComponent,
loadIssues,
loadSources
});

expect(wrapper.instance().loadComponent).toBe(loadComponent);
});

it('should reload', async () => {
(defaultLoadIssues as jest.Mock)
.mockResolvedValueOnce([mockIssue()])
.mockResolvedValueOnce([mockIssue()]);
(getComponentForSourceViewer as jest.Mock).mockResolvedValueOnce(mockSourceViewerFile());
(getComponentData as jest.Mock).mockResolvedValueOnce({
component: { leakPeriodDate: '2018-06-20T17:12:19+0200' }
});
(getSources as jest.Mock).mockResolvedValueOnce([mockSourceLine()]);

const wrapper = shallowRender();
await waitAndUpdate(wrapper);

wrapper.instance().reloadIssues();

expect(defaultLoadIssues).toBeCalledTimes(2);

await waitAndUpdate(wrapper);

expect(wrapper.state().issues).toHaveLength(1);
});

it('should load sources before', async () => {
(defaultLoadIssues as jest.Mock)
.mockResolvedValueOnce([mockIssue(false, { key: 'issue1' })])
.mockResolvedValueOnce([mockIssue(false, { key: 'issue2' })]);
(getComponentForSourceViewer as jest.Mock).mockResolvedValueOnce(mockSourceViewerFile());
(getComponentData as jest.Mock).mockResolvedValueOnce({
component: { leakPeriodDate: '2018-06-20T17:12:19+0200' }
});
(getSources as jest.Mock)
.mockResolvedValueOnce([mockSourceLine()])
.mockResolvedValueOnce([mockSourceLine()]);

const wrapper = shallowRender();
await waitAndUpdate(wrapper);

wrapper.instance().loadSourcesBefore();
expect(wrapper.state().loadingSourcesBefore).toBe(true);

expect(defaultLoadIssues).toBeCalledTimes(2);
expect(getSources).toBeCalledTimes(2);

await waitAndUpdate(wrapper);
expect(wrapper.state().loadingSourcesBefore).toBe(false);
expect(wrapper.state().issues).toHaveLength(2);
});

it('should load sources after', async () => {
(defaultLoadIssues as jest.Mock)
.mockResolvedValueOnce([mockIssue(false, { key: 'issue1' })])
.mockResolvedValueOnce([mockIssue(false, { key: 'issue2' })]);
(getComponentForSourceViewer as jest.Mock).mockResolvedValueOnce(mockSourceViewerFile());
(getComponentData as jest.Mock).mockResolvedValueOnce({
component: { leakPeriodDate: '2018-06-20T17:12:19+0200' }
});
(getSources as jest.Mock)
.mockResolvedValueOnce([mockSourceLine()])
.mockResolvedValueOnce([mockSourceLine()]);

const wrapper = shallowRender();
await waitAndUpdate(wrapper);

wrapper.instance().loadSourcesAfter();
expect(wrapper.state().loadingSourcesAfter).toBe(true);

expect(defaultLoadIssues).toBeCalledTimes(2);
expect(getSources).toBeCalledTimes(2);

await waitAndUpdate(wrapper);

expect(wrapper.state().loadingSourcesAfter).toBe(false);
expect(wrapper.state().issues).toHaveLength(2);
}); });


function shallowRender() {
return <SourceViewerBase branchLike={mockMainBranch()} component="my-component" />;
function shallowRender(overrides: Partial<SourceViewerBase['props']> = {}) {
return shallow<SourceViewerBase>(
<SourceViewerBase branchLike={mockMainBranch()} component="my-component" {...overrides} />
);
} }

+ 158
- 15
server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap View File

// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP


exports[`should render correctly 1`] = ` exports[`should render correctly 1`] = `
<SourceViewerBase
branchLike={
<ContextProvider
value={
Object { Object {
"analysisDate": "2018-01-01",
"excludedFromPurge": true,
"isMain": true,
"name": "master",
"branchLike": Object {
"analysisDate": "2018-01-01",
"excludedFromPurge": true,
"isMain": true,
"name": "master",
},
"file": Object {
"key": "foo",
"leakPeriodDate": "2018-06-20T17:12:19+0200",
"measures": Object {
"coverage": "85.2",
"duplicationDensity": "1.0",
"issues": "12",
"lines": "56",
},
"path": "foo/bar.ts",
"project": "my-project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
},
} }
} }
component="my-component"
displayAllIssues={false}
displayIssueLocationsCount={true}
displayIssueLocationsLink={true}
displayLocationMarkers={true}
loadComponent={[Function]}
loadIssues={[Function]}
loadSources={[Function]}
/>
>
<div
className="source-viewer"
>
<ContextConsumer>
<Component />
</ContextConsumer>
<SourceViewerCode
branchLike={
Object {
"analysisDate": "2018-01-01",
"excludedFromPurge": true,
"isMain": true,
"name": "master",
}
}
componentKey="my-component"
displayAllIssues={false}
displayIssueLocationsCount={true}
displayIssueLocationsLink={true}
displayLocationMarkers={true}
duplicationsByLine={Object {}}
hasSourcesAfter={false}
hasSourcesBefore={false}
highlightedSymbols={Array []}
issueLocationsByLine={
Object {
"25": Array [
Object {
"from": 0,
"line": 25,
"to": 999999,
},
],
"26": Array [
Object {
"from": 0,
"line": 26,
"to": 15,
},
],
}
}
issues={
Array [
Object {
"actions": Array [],
"component": "main.js",
"componentLongName": "main.js",
"componentQualifier": "FIL",
"componentUuid": "foo1234",
"creationDate": "2017-03-01T09:36:01+0100",
"flows": Array [],
"fromHotspot": false,
"key": "AVsae-CQS-9G3txfbFN2",
"line": 25,
"message": "Reduce the number of conditional operators (4) used in the expression",
"organization": "myorg",
"project": "myproject",
"projectKey": "foo",
"projectName": "Foo",
"projectOrganization": "org",
"rule": "javascript:S1067",
"ruleName": "foo",
"secondaryLocations": Array [],
"severity": "MAJOR",
"status": "OPEN",
"textRange": Object {
"endLine": 26,
"endOffset": 15,
"startLine": 25,
"startOffset": 0,
},
"transitions": Array [],
"type": "BUG",
},
]
}
issuesByLine={
Object {
"26": Array [
Object {
"actions": Array [],
"component": "main.js",
"componentLongName": "main.js",
"componentQualifier": "FIL",
"componentUuid": "foo1234",
"creationDate": "2017-03-01T09:36:01+0100",
"flows": Array [],
"fromHotspot": false,
"key": "AVsae-CQS-9G3txfbFN2",
"line": 25,
"message": "Reduce the number of conditional operators (4) used in the expression",
"organization": "myorg",
"project": "myproject",
"projectKey": "foo",
"projectName": "Foo",
"projectOrganization": "org",
"rule": "javascript:S1067",
"ruleName": "foo",
"secondaryLocations": Array [],
"severity": "MAJOR",
"status": "OPEN",
"textRange": Object {
"endLine": 26,
"endOffset": 15,
"startLine": 25,
"startOffset": 0,
},
"transitions": Array [],
"type": "BUG",
},
],
}
}
loadDuplications={[Function]}
loadSourcesAfter={[Function]}
loadSourcesBefore={[Function]}
loadingSourcesAfter={false}
loadingSourcesBefore={false}
onIssueChange={[Function]}
onIssuePopupToggle={[Function]}
onIssueSelect={[Function]}
onIssueUnselect={[Function]}
onIssuesClose={[Function]}
onIssuesOpen={[Function]}
onLinePopupToggle={[Function]}
onSymbolClick={[Function]}
openIssuesByLine={Object {}}
renderDuplicationPopup={[Function]}
sources={Array []}
symbolsByLine={Object {}}
/>
</div>
</ContextProvider>
`; `;

+ 2
- 2
server/sonar-web/src/main/js/components/controls/DateInput.tsx View File

import CalendarIcon from 'sonar-ui-common/components/icons/CalendarIcon'; import CalendarIcon from 'sonar-ui-common/components/icons/CalendarIcon';
import ChevronLeftIcon from 'sonar-ui-common/components/icons/ChevronLeftIcon'; import ChevronLeftIcon from 'sonar-ui-common/components/icons/ChevronLeftIcon';
import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon'; import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { import {
getShortMonthName, getShortMonthName,
getShortWeekDayName, getShortWeekDayName,
import './DayPicker.css'; import './DayPicker.css';
import './styles.css'; import './styles.css';


const DayPicker = lazyLoad(() => import('react-day-picker'));
const DayPicker = lazyLoadComponent(() => import('react-day-picker'), 'DayPicker');


interface Props { interface Props {
className?: string; className?: string;

+ 2
- 2
server/sonar-web/src/main/js/components/docs/DocTooltip.tsx View File

*/ */
import * as React from 'react'; import * as React from 'react';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { filterContent } from '../../helpers/markdown'; import { filterContent } from '../../helpers/markdown';


const DocMarkdownBlock = lazyLoad(() => import('./DocMarkdownBlock'));
const DocMarkdownBlock = lazyLoadComponent(() => import('./DocMarkdownBlock'), 'DocMarkdownBlock');


interface Props { interface Props {
className?: string; className?: string;

+ 1
- 1
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap View File

<div <div
className="abs-width-300" className="abs-width-300"
> >
<LazyLoader
<DocMarkdownBlock
className="cut-margins" className="cut-margins"
content="this is *bold* text" content="this is *bold* text"
isTooltip={true} isTooltip={true}

+ 10
- 4
server/sonar-web/src/main/js/components/workspace/Workspace.tsx View File

*/ */
import { omit, uniqBy } from 'lodash'; import { omit, uniqBy } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
import { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
import { get, save } from 'sonar-ui-common/helpers/storage'; import { get, save } from 'sonar-ui-common/helpers/storage';
import { ComponentDescriptor, RuleDescriptor, WorkspaceContext } from './context'; import { ComponentDescriptor, RuleDescriptor, WorkspaceContext } from './context';
import './styles.css'; import './styles.css';
import WorkspacePortal from './WorkspacePortal'; import WorkspacePortal from './WorkspacePortal';


const WORKSPACE = 'sonarqube-workspace'; const WORKSPACE = 'sonarqube-workspace';
const WorkspaceNav = lazyLoad(() => import('./WorkspaceNav'));
const WorkspaceRuleViewer = lazyLoad(() => import('./WorkspaceRuleViewer'));
const WorkspaceComponentViewer = lazyLoad(() => import('./WorkspaceComponentViewer'));
const WorkspaceNav = lazyLoadComponent(() => import('./WorkspaceNav'), 'WorkspaceNav');
const WorkspaceRuleViewer = lazyLoadComponent(
() => import('./WorkspaceRuleViewer'),
'WorkspaceRuleViewer'
);
const WorkspaceComponentViewer = lazyLoadComponent(
() => import('./WorkspaceComponentViewer'),
'WorkspaceComponentViewer'
);


interface State { interface State {
components: ComponentDescriptor[]; components: ComponentDescriptor[];

Loading…
Cancel
Save