From bdbaf099bbc788427899504472319cd8c42e6085 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Thu, 20 Aug 2015 11:38:54 +0200 Subject: [PATCH] rewrite component navigation --- server/sonar-web/src/main/js/apps/nav/app.jsx | 9 +- .../component/component-nav-breadcrumbs.jsx | 23 ++ .../nav/component/component-nav-favorite.jsx | 15 ++ .../apps/nav/component/component-nav-menu.jsx | 226 ++++++++++++++++++ .../apps/nav/component/component-nav-meta.jsx | 13 + .../js/apps/nav/component/component-nav.jsx | 50 ++++ .../main/js/apps/nav/dashboard-name-mixin.jsx | 11 + .../nav/{ => global}/global-nav-branding.jsx | 0 .../apps/nav/{ => global}/global-nav-menu.jsx | 13 +- .../nav/{ => global}/global-nav-search.jsx | 0 .../apps/nav/{ => global}/global-nav-user.jsx | 0 .../js/apps/nav/{ => global}/global-nav.jsx | 0 .../js/apps/nav/{ => global}/search-view.js | 2 +- .../nav/{ => global}/shortcuts-help-view.js | 2 +- .../main/js/components/shared/favorite.jsx | 44 ++++ .../js/components/shared/qualifier-icon.jsx | 11 + .../sonar-web/src/main/less/init/icons.less | 33 +++ server/sonar-web/test/intern.js | 10 +- .../component-nav-breadcrumbs.spec.js | 27 +++ 19 files changed, 474 insertions(+), 15 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/nav/component/component-nav-breadcrumbs.jsx create mode 100644 server/sonar-web/src/main/js/apps/nav/component/component-nav-favorite.jsx create mode 100644 server/sonar-web/src/main/js/apps/nav/component/component-nav-menu.jsx create mode 100644 server/sonar-web/src/main/js/apps/nav/component/component-nav-meta.jsx create mode 100644 server/sonar-web/src/main/js/apps/nav/component/component-nav.jsx create mode 100644 server/sonar-web/src/main/js/apps/nav/dashboard-name-mixin.jsx rename server/sonar-web/src/main/js/apps/nav/{ => global}/global-nav-branding.jsx (100%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/global-nav-menu.jsx (94%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/global-nav-search.jsx (100%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/global-nav-user.jsx (100%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/global-nav.jsx (100%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/search-view.js (99%) rename server/sonar-web/src/main/js/apps/nav/{ => global}/shortcuts-help-view.js (91%) create mode 100644 server/sonar-web/src/main/js/components/shared/favorite.jsx create mode 100644 server/sonar-web/src/main/js/components/shared/qualifier-icon.jsx create mode 100644 server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js diff --git a/server/sonar-web/src/main/js/apps/nav/app.jsx b/server/sonar-web/src/main/js/apps/nav/app.jsx index d7bcc337d81..48a708e321b 100644 --- a/server/sonar-web/src/main/js/apps/nav/app.jsx +++ b/server/sonar-web/src/main/js/apps/nav/app.jsx @@ -1,15 +1,22 @@ import React from 'react'; -import GlobalNav from './global-nav'; +import GlobalNav from './global/global-nav'; +import ComponentNav from './component/component-nav'; export default { start(options) { window.requestMessages().done(() => { this.renderGlobalNav(options); + options.space === 'component' && this.renderComponentNav(options); }); }, renderGlobalNav(options) { const el = document.getElementById('global-navigation'); React.render(, el); + }, + + renderComponentNav(options) { + const el = document.getElementById('context-navigation'); + React.render(, el); } }; diff --git a/server/sonar-web/src/main/js/apps/nav/component/component-nav-breadcrumbs.jsx b/server/sonar-web/src/main/js/apps/nav/component/component-nav-breadcrumbs.jsx new file mode 100644 index 00000000000..1bb643c11ac --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/component/component-nav-breadcrumbs.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import QualifierIcon from 'components/shared/qualifier-icon'; + +export default React.createClass({ + render() { + if (!this.props.breadcrumbs) { + return null; + } + const items = this.props.breadcrumbs.map((item, index) => { + const url = `${window.baseUrl}/dashboard/index?id=${encodeURIComponent(item.key)}`; + return ( +
  • + +  {item.name} + +
  • + ); + }); + return ( +
      {items}
    + ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/nav/component/component-nav-favorite.jsx b/server/sonar-web/src/main/js/apps/nav/component/component-nav-favorite.jsx new file mode 100644 index 00000000000..8e2b8624abd --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/component/component-nav-favorite.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Favorite from 'components/shared/favorite'; + +export default React.createClass({ + render() { + if (!this.props.canBeFavorite) { + return null; + } + return ( +
    + +
    + ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/nav/component/component-nav-menu.jsx b/server/sonar-web/src/main/js/apps/nav/component/component-nav-menu.jsx new file mode 100644 index 00000000000..03e1c28f7ee --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/component/component-nav-menu.jsx @@ -0,0 +1,226 @@ +import React from 'react'; +import DashboardNameMixin from '../dashboard-name-mixin'; + +const SETTINGS_URLS = [ + '/project/settings', '/project/profile', '/project/qualitygate', '/manual_measures/index', + '/action_plans/index', '/project/links', '/project_roles/index', '/project/history', '/project/key', + '/project/deletion' +]; + +const MORE_URLS = ['/dashboards', '/dashboard', '/plugins/resource']; + +export default React.createClass({ + mixins: [DashboardNameMixin], + + activeLink(url) { + return window.location.pathname.indexOf(window.baseUrl + url) === 0 ? 'active' : null; + }, + + renderLink(url, title, highlightUrl = url) { + let fullUrl = window.baseUrl + url; + return ( +
  • + {title} +
  • + ); + }, + + renderOverviewLink() { + const url = `/overview/index?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('overview.page'), '/overview'); + }, + + renderComponentsLink() { + const url = `/components/index?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('components.page'), '/components'); + }, + + renderComponentIssuesLink() { + const url = `/component_issues/index?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('issues.page'), '/component_issues'); + }, + + renderAdministration() { + if (!this.props.conf.showSettings) { + return null; + } + let isSettingsActive = SETTINGS_URLS.some(url => { + return window.location.href.indexOf(url) !== -1; + }), + className = 'dropdown' + (isSettingsActive ? ' active' : ''); + return ( +
  • + + {window.t('layout.settings')}  +
      + {this.renderSettingsLink()} + {this.renderProfilesLink()} + {this.renderQualityGatesLink()} + {this.renderCustomMeasuresLink()} + {this.renderActionPlansLink()} + {this.renderLinksLink()} + {this.renderPermissionsLink()} + {this.renderHistoryLink()} + {this.renderUpdateKeyLink()} + {this.renderDeletionLink()} + {this.renderExtensions()} +
    +
  • + ); + }, + + renderSettingsLink() { + const url = `/project/settings?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('project_settings.page'), '/project/settings'); + }, + + renderProfilesLink() { + if (!this.props.conf.showQualityProfiles) { + return null; + } + const url = `/project/profile?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('project_quality_profiles.page'), '/project/profile'); + }, + + renderQualityGatesLink() { + if (!this.props.conf.showQualityGates) { + return null; + } + const url = `/project/qualitygate?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('project_quality_gate.page'), '/project/qualitygate'); + }, + + renderCustomMeasuresLink() { + if (!this.props.conf.showManualMeasures) { + return null; + } + const url = `/custom_measures?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('custom_measures.page'), '/custom_measures'); + }, + + renderActionPlansLink() { + if (!this.props.conf.showActionPlans) { + return null; + } + const url = `/action_plans?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('action_plans.page'), '/action_plans'); + }, + + renderLinksLink() { + if (!this.props.conf.showLinks) { + return null; + } + const url = `/project/links?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('project_links.page'), '/project/links'); + }, + + renderPermissionsLink() { + if (!this.props.conf.showPermissions) { + return null; + } + const url = `/project_roles?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('permissions.page'), '/project_roles'); + }, + + renderHistoryLink() { + if (!this.props.conf.showHistory) { + return null; + } + const url = `/project/history?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('project_history.page'), '/project/history'); + }, + + renderUpdateKeyLink() { + if (!this.props.conf.showUpdateKey) { + return null; + } + const url = `/project/key?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('update_key.page'), '/project/key'); + }, + + renderDeletionLink() { + if (!this.props.conf.showDeletion) { + return null; + } + const url = `/project/deletion?id=${encodeURIComponent(this.props.component.key)}`; + return this.renderLink(url, window.t('deletion.page'), '/project/deletion'); + }, + + renderExtensions() { + let extensions = this.props.conf.extensions || []; + return extensions.map(e => { + return this.renderLink(e.url, e.name, e.url); + }); + }, + + renderMore() { + let isActive = MORE_URLS.some(url => { + return window.location.href.indexOf(url) !== -1; + }), + className = 'dropdown' + (isActive ? ' active' : ''); + return ( +
  • + + {window.t('more')}  + +
      + {this.renderDashboards()} + {this.renderDashboardManagementLink()} + {this.renderTools()} +
    +
  • + ); + }, + + renderDashboards() { + let dashboards = (this.props.component.dashboards || []).map(d => { + let url = `${window.baseUrl}/dashboard?id=${encodeURIComponent(this.props.component.key)}&did=${d.key}`; + let name = this.getLocalizedDashboardName(d.name); + return this.renderLink(url, name); + }); + return [
  • {window.t('layout.dashboards')}
  • ].concat(dashboards); + }, + + renderDashboardManagementLink() { + if (!window.SS.user) { + return null; + } + let url = `${window.baseUrl}/dashboards?resource=${encodeURIComponent(this.props.component.key)}`; + let name = window.t('dashboard.manage_dashboards'); + return [ +
  • , + this.renderLink(url, name, '/dashboards') + ]; + }, + + renderTools() { + let component = this.props.component; + if (!component.isComparable && !_.size(component.extensions)) { + return null; + } + let tools = [ +
  • , +
  • Tools
  • + ]; + if (component.isComparable) { + let compareUrl = `/comparison/index?resource=${component.key}`; + tools.push(this.renderLink(compareUrl, window.t('comparison.page'))); + } + (component.extensions || []).forEach(e => { + tools.push(this.renderLink(e.url, e.name)); + }); + return tools; + }, + + render() { + return ( +
      + {this.renderOverviewLink()} + {this.renderComponentsLink()} + {this.renderComponentIssuesLink()} + {this.renderAdministration()} + {this.renderMore()} +
    + ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/nav/component/component-nav-meta.jsx b/server/sonar-web/src/main/js/apps/nav/component/component-nav-meta.jsx new file mode 100644 index 00000000000..8ebb8e91a42 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/component/component-nav-meta.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export default React.createClass({ + render() { + const version = this.props.version ? `Version ${this.props.version}` : null; + const snapshotDate = this.props.snapshotDate ? moment(this.props.snapshotDate).format('LLL') : null; + return ( +
    + {version} {snapshotDate} +
    + ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/nav/component/component-nav.jsx b/server/sonar-web/src/main/js/apps/nav/component/component-nav.jsx new file mode 100644 index 00000000000..855482981d9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/component/component-nav.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import ComponentNavFavorite from './component-nav-favorite'; +import ComponentNavBreadcrumbs from './component-nav-breadcrumbs'; +import ComponentNavMeta from './component-nav-meta'; +import ComponentNavMenu from './component-nav-menu'; + +let $ = jQuery; + +export default React.createClass({ + getInitialState() { + return { component: {}, conf: {} }; + }, + + componentDidMount() { + this.loadDetails(); + }, + + loadDetails() { + const url = `${window.baseUrl}/api/navigation/component`; + const data = { componentKey: this.props.componentKey }; + $.get(url, data).done(r => { + this.setState({ + component: r, + conf: r.configuration || {} + }); + }); + }, + + render() { + return ( +
    + + + + + + + +
    + ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/nav/dashboard-name-mixin.jsx b/server/sonar-web/src/main/js/apps/nav/dashboard-name-mixin.jsx new file mode 100644 index 00000000000..e8366f137c7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/nav/dashboard-name-mixin.jsx @@ -0,0 +1,11 @@ +export default { + getLocalizedDashboardName(baseName) { + var l10nKey = 'dashboard.' + baseName + '.name'; + var l10nLabel = window.t(l10nKey); + if (l10nLabel !== l10nKey) { + return l10nLabel; + } else { + return baseName; + } + } +}; diff --git a/server/sonar-web/src/main/js/apps/nav/global-nav-branding.jsx b/server/sonar-web/src/main/js/apps/nav/global/global-nav-branding.jsx similarity index 100% rename from server/sonar-web/src/main/js/apps/nav/global-nav-branding.jsx rename to server/sonar-web/src/main/js/apps/nav/global/global-nav-branding.jsx diff --git a/server/sonar-web/src/main/js/apps/nav/global-nav-menu.jsx b/server/sonar-web/src/main/js/apps/nav/global/global-nav-menu.jsx similarity index 94% rename from server/sonar-web/src/main/js/apps/nav/global-nav-menu.jsx rename to server/sonar-web/src/main/js/apps/nav/global/global-nav-menu.jsx index 9aeb24baa77..037b0231210 100644 --- a/server/sonar-web/src/main/js/apps/nav/global-nav-menu.jsx +++ b/server/sonar-web/src/main/js/apps/nav/global/global-nav-menu.jsx @@ -1,6 +1,9 @@ import React from 'react'; +import DashboardNameMixin from '../dashboard-name-mixin'; export default React.createClass({ + mixins: [DashboardNameMixin], + getDefaultProps: function () { return { globalDashboards: [], globalPages: [] }; }, @@ -9,16 +12,6 @@ export default React.createClass({ return window.location.pathname.indexOf(window.baseUrl + url) === 0 ? 'active' : null; }, - getLocalizedDashboardName(baseName) { - var l10nKey = 'dashboard.' + baseName + '.name'; - var l10nLabel = window.t(l10nKey); - if (l10nLabel !== l10nKey) { - return l10nLabel; - } else { - return baseName; - } - }, - renderDashboardLink(dashboard) { const url = `${window.baseUrl}/dashboard/index?did=${encodeURIComponent(dashboard.key)}`; const name = this.getLocalizedDashboardName(dashboard.name); diff --git a/server/sonar-web/src/main/js/apps/nav/global-nav-search.jsx b/server/sonar-web/src/main/js/apps/nav/global/global-nav-search.jsx similarity index 100% rename from server/sonar-web/src/main/js/apps/nav/global-nav-search.jsx rename to server/sonar-web/src/main/js/apps/nav/global/global-nav-search.jsx diff --git a/server/sonar-web/src/main/js/apps/nav/global-nav-user.jsx b/server/sonar-web/src/main/js/apps/nav/global/global-nav-user.jsx similarity index 100% rename from server/sonar-web/src/main/js/apps/nav/global-nav-user.jsx rename to server/sonar-web/src/main/js/apps/nav/global/global-nav-user.jsx diff --git a/server/sonar-web/src/main/js/apps/nav/global-nav.jsx b/server/sonar-web/src/main/js/apps/nav/global/global-nav.jsx similarity index 100% rename from server/sonar-web/src/main/js/apps/nav/global-nav.jsx rename to server/sonar-web/src/main/js/apps/nav/global/global-nav.jsx diff --git a/server/sonar-web/src/main/js/apps/nav/search-view.js b/server/sonar-web/src/main/js/apps/nav/global/search-view.js similarity index 99% rename from server/sonar-web/src/main/js/apps/nav/search-view.js rename to server/sonar-web/src/main/js/apps/nav/global/search-view.js index d66a26508ff..233a1dc5f29 100644 --- a/server/sonar-web/src/main/js/apps/nav/search-view.js +++ b/server/sonar-web/src/main/js/apps/nav/global/search-view.js @@ -1,6 +1,6 @@ define([ 'components/common/selectable-collection-view', - './templates' + '../templates' ], function (SelectableCollectionView) { var $ = jQuery, diff --git a/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js b/server/sonar-web/src/main/js/apps/nav/global/shortcuts-help-view.js similarity index 91% rename from server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js rename to server/sonar-web/src/main/js/apps/nav/global/shortcuts-help-view.js index 39024617f7a..b016a734d8c 100644 --- a/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js +++ b/server/sonar-web/src/main/js/apps/nav/global/shortcuts-help-view.js @@ -1,6 +1,6 @@ define([ 'components/common/modals', - './templates' + '../templates' ], function (ModalView) { return ModalView.extend({ diff --git a/server/sonar-web/src/main/js/components/shared/favorite.jsx b/server/sonar-web/src/main/js/components/shared/favorite.jsx new file mode 100644 index 00000000000..09601d31cd1 --- /dev/null +++ b/server/sonar-web/src/main/js/components/shared/favorite.jsx @@ -0,0 +1,44 @@ +import React from 'react'; + +let $ = jQuery; + +export default React.createClass({ + propTypes: { + component: React.PropTypes.string.isRequired, + favorite: React.PropTypes.bool.isRequired + }, + + getInitialState() { + return { favorite: this.props.favorite }; + }, + + toggleFavorite(e) { + e.preventDefault(); + this.state.favorite ? this.removeFavorite() : this.addFavorite(); + }, + + addFavorite() { + const url = `${window.baseUrl}/api/favourites`; + const data = { key: this.props.component }; + $.ajax({ type: 'POST', url, data }).done(() => this.setState({ favorite: true })); + }, + + removeFavorite() { + const url = `${window.baseUrl}/api/favourites/${encodeURIComponent(this.props.component)}`; + $.ajax({ type: 'DELETE', url }).done(() => this.setState({ favorite: false })); + }, + + renderSVG() { + return ( + + + + ) + }, + + render() { + const className = this.state.favorite ? 'icon-star icon-star-favorite' : 'icon-star'; + return {this.renderSVG()}; + } +}); diff --git a/server/sonar-web/src/main/js/components/shared/qualifier-icon.jsx b/server/sonar-web/src/main/js/components/shared/qualifier-icon.jsx new file mode 100644 index 00000000000..e0f6e5a342d --- /dev/null +++ b/server/sonar-web/src/main/js/components/shared/qualifier-icon.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export default React.createClass({ + render() { + if (!this.props.qualifier) { + return null; + } + var className = 'icon-qualifier-' + this.props.qualifier.toLowerCase(); + return ; + } +}); diff --git a/server/sonar-web/src/main/less/init/icons.less b/server/sonar-web/src/main/less/init/icons.less index f0d20455d5e..19d7e0cbaae 100644 --- a/server/sonar-web/src/main/less/init/icons.less +++ b/server/sonar-web/src/main/less/init/icons.less @@ -316,14 +316,47 @@ a[class^="icon-"], a[class*=" icon-"] { .square(16px); background-size: 16px 16px; background: no-repeat center center; + .trans !important; } .icon-favorite { background-image: url('data:image/svg+xml,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20stroke-linejoin%3D%22round%22%20stroke-miterlimit%3D%221.414%22%3E%3Cpath%20d%3D%22M15.428%205.777c0%20.13-.078.274-.233.428l-3.24%203.16.767%204.465c.006.042.01.102.01.18%200%20.124-.032.23-.095.316-.062.086-.153.13-.272.13-.113%200-.232-.036-.357-.108l-4.01-2.107L3.99%2014.35c-.13.072-.25.107-.357.107-.125%200-.22-.043-.28-.13-.064-.085-.095-.19-.095-.316%200-.037.006-.096.018-.18l.768-4.464-3.25-3.16C.644%206.045.57%205.9.57%205.775c0-.22.167-.356.5-.41l4.482-.652L7.562.652c.112-.244.258-.366.437-.366.177%200%20.323.122.436.366l2.01%204.062%204.48.652c.335.054.5.19.5.41h.002z%22%20fill%3D%22%23F90%22%20fill-rule%3D%22nonzero%22%2F%3E%3C%2Fsvg%3E'); + .rotate(72deg); } .icon-not-favorite { background-image: url('data:image/svg+xml,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20stroke-linejoin%3D%22round%22%20stroke-miterlimit%3D%221.414%22%3E%3Cpath%20d%3D%22M15.428%205.777c0%20.13-.078.274-.233.428l-3.24%203.16.767%204.465c.006.042.01.102.01.18%200%20.124-.032.23-.095.316-.062.086-.153.13-.272.13-.113%200-.232-.036-.357-.108l-4.01-2.107L3.99%2014.35c-.13.072-.25.107-.357.107-.125%200-.22-.043-.28-.13-.064-.085-.095-.19-.095-.316%200-.037.006-.096.018-.18l.768-4.464-3.25-3.16C.644%206.045.57%205.9.57%205.775c0-.22.167-.356.5-.41l4.482-.652L7.562.652c.112-.244.258-.366.437-.366.177%200%20.323.122.436.366l2.01%204.062%204.48.652c.335.054.5.19.5.41h.002z%22%20fill%3D%22%23CDCDCD%22%20fill-rule%3D%22nonzero%22%2F%3E%3C%2Fsvg%3E'); } +.icon-star { + .trans !important; +} + +.icon-star path { + stroke: #777; + stroke-width: sqrt(2); + stroke-opacity: 1; + fill-opacity: 0; + .trans; +} + +.icon-star-favorite { + animation: spin .6s forwards; +} + +.icon-star-favorite path { + fill: rgb(255, 153, 0); + stroke-opacity: 0; + fill-opacity: 1; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(144deg); + } +} + .icon-help:before { content: "\f059"; color: @blue; diff --git a/server/sonar-web/test/intern.js b/server/sonar-web/test/intern.js index 74b2a6bd9ef..ee712c33f18 100644 --- a/server/sonar-web/test/intern.js +++ b/server/sonar-web/test/intern.js @@ -18,7 +18,8 @@ define(['intern'], function (intern) { 'test/unit/application.spec', 'test/unit/issue.spec', 'test/unit/overview/card.spec', - 'test/unit/code-with-issue-locations-helper.spec' + 'test/unit/code-with-issue-locations-helper.spec', + 'test/unit/nav/component/component-nav-breadcrumbs.spec' ], functionalSuites: [ @@ -40,7 +41,12 @@ define(['intern'], function (intern) { loaderOptions: { paths: { - 'react': 'build/js/libs/third-party/react-with-addons' + 'react': '../../build/js/libs/third-party/react-with-addons' + }, + map: { + '*': { + 'components/shared/qualifier-icon': '../../build/js/components/shared/qualifier-icon' + } } } }; diff --git a/server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js b/server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js new file mode 100644 index 00000000000..4b2a5fecaee --- /dev/null +++ b/server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js @@ -0,0 +1,27 @@ +define(function (require) { + var bdd = require('intern!bdd'); + var assert = require('intern/chai!assert'); + + var React = require('react'); + var TestUtils = React.addons.TestUtils; + + var ComponentNavBreadcrumbs = require('build/js/apps/nav/component/component-nav-breadcrumbs'); + + bdd.describe('ComponentNavBreadcrumbs', function () { + bdd.it('should not render unless `props.breadcrumbs`', function () { + var result = React.renderToStaticMarkup(React.createElement(ComponentNavBreadcrumbs, null)); + assert.equal(result, ''); + }); + + bdd.it('should not render breadcrumbs with one element', function () { + var breadcrumbs = [ + { key: 'my-project', name: 'My Project', qualifier: 'TRK' } + ]; + var result = TestUtils.renderIntoDocument( + React.createElement(ComponentNavBreadcrumbs, { breadcrumbs: breadcrumbs }) + ); + assert.equal(TestUtils.scryRenderedDOMComponentsWithTag(result, 'li').length, 1); + assert.equal(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a').length, 1); + }); + }); +}); -- 2.39.5