Commit 20ec7721 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '326480-devops-adoption-add-devsecops-tabs-to-table' into 'master'

Resolve "[DevOps Adoption] Introduce Dev, Sec, Ops tabs"

See merge request gitlab-org/gitlab!61131
parents 485c457a 956fd910
<script>
import { GlAlert } from '@gitlab/ui';
import { GlAlert, GlTabs, GlTab } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import dateformat from 'dateformat';
import DevopsScore from '~/analytics/devops_report/components/devops_score.vue';
import API from '~/api';
import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility';
import {
DEVOPS_ADOPTION_STRINGS,
DEVOPS_ADOPTION_ERROR_KEYS,
......@@ -11,6 +14,8 @@ import {
DEFAULT_POLLING_INTERVAL,
DEVOPS_ADOPTION_GROUP_LEVEL_LABEL,
DEVOPS_ADOPTION_TABLE_CONFIGURATION,
TRACK_ADOPTION_TAB_CLICK_EVENT,
TRACK_DEVOPS_SCORE_TAB_CLICK_EVENT,
} from '../constants';
import bulkFindOrCreateDevopsAdoptionSegmentsMutation from '../graphql/mutations/bulk_find_or_create_devops_adoption_segments.mutation.graphql';
import devopsAdoptionSegmentsQuery from '../graphql/queries/devops_adoption_segments.query.graphql';
......@@ -26,6 +31,9 @@ export default {
GlAlert,
DevopsAdoptionSection,
DevopsAdoptionSegmentModal,
DevopsScore,
GlTabs,
GlTab,
},
inject: {
isGroup: {
......@@ -34,11 +42,22 @@ export default {
groupGid: {
default: null,
},
devopsScoreMetrics: {
default: null,
},
devopsReportDocsPath: {
default: '',
},
noDataImagePath: {
default: '',
},
},
i18n: {
groupLevelLabel: DEVOPS_ADOPTION_GROUP_LEVEL_LABEL,
...DEVOPS_ADOPTION_STRINGS.app,
},
trackDevopsTabClickEvent: TRACK_ADOPTION_TAB_CLICK_EVENT,
trackDevopsScoreTabClickEvent: TRACK_DEVOPS_SCORE_TAB_CLICK_EVENT,
maxSegments: MAX_SEGMENTS,
devopsAdoptionTableConfiguration: DEVOPS_ADOPTION_TABLE_CONFIGURATION,
data() {
......@@ -63,6 +82,9 @@ export default {
directDescendantsOnly: false,
}
: {},
adoptionTabClicked: false,
devopsScoreTabClicked: false,
selectedTab: 0,
};
},
apollo: {
......@@ -88,6 +110,9 @@ export default {
},
},
computed: {
isAdmin() {
return !this.isGroup;
},
hasGroupData() {
return Boolean(this.groups?.nodes?.length);
},
......@@ -121,9 +146,15 @@ export default {
canRenderModal() {
return this.hasGroupData && !this.isLoading;
},
tabIndexValues() {
const tabs = this.$options.devopsAdoptionTableConfiguration.map((item) => item.tab);
return this.isGroup ? tabs : [...tabs, 'devops-score'];
},
},
created() {
this.fetchGroups();
this.selectTab();
},
beforeDestroy() {
clearInterval(this.pollingTableData);
......@@ -219,10 +250,53 @@ export default {
deleteSegmentsFromCache(cache, ids, this.segmentsQueryVariables);
},
selectTab() {
const [value] = getParameterValues('tab');
if (value) {
this.selectedTab = this.tabIndexValues.indexOf(value);
}
},
onTabChange(index) {
if (index > 0) {
if (index !== this.selectedTab) {
const path = mergeUrlParams(
{ tab: this.tabIndexValues[index] },
window.location.pathname,
);
updateHistory({ url: path, title: window.title });
}
} else {
updateHistory({ url: window.location.pathname, title: window.title });
}
this.selectedTab = index;
},
trackDevopsScoreTabClick() {
if (!this.devopsScoreTabClicked) {
API.trackRedisHllUserEvent(this.$options.trackDevopsScoreTabClickEvent);
this.devopsScoreTabClicked = true;
}
},
trackDevopsTabClick() {
if (!this.adoptionTabClicked) {
API.trackRedisHllUserEvent(this.$options.trackDevopsTabClickEvent);
this.adoptionTabClicked = true;
}
},
},
};
</script>
<template>
<div>
<gl-tabs :value="selectedTab" @input="onTabChange">
<gl-tab
v-for="tab in $options.devopsAdoptionTableConfiguration"
:key="tab.title"
data-testid="devops-adoption-tab"
@click="trackDevopsTabClick"
>
<template #title>{{ tab.title }}</template>
<div v-if="hasLoadingError">
<template v-for="(error, key) in errors">
<gl-alert v-if="error" :key="key" variant="danger" :dismissible="false" class="gl-mt-3">
......@@ -231,27 +305,35 @@ export default {
</template>
</div>
<div v-else>
<devops-adoption-segment-modal
v-if="canRenderModal"
ref="addRemoveModal"
:groups="groups.nodes"
:enabled-groups="devopsAdoptionSegments.nodes"
@segmentsAdded="addSegmentsToCache"
@segmentsRemoved="deleteSegmentsFromCache"
@trackModalOpenState="trackModalOpenState"
/>
<devops-adoption-section
v-else
:is-loading="isLoading"
:has-segments-data="hasSegmentsData"
:timestamp="timestamp"
:has-group-data="hasGroupData"
:segment-limit-reached="segmentLimitReached"
:edit-groups-button-label="editGroupsButtonLabel"
:cols="$options.devopsAdoptionTableConfiguration[0].cols"
:cols="tab.cols"
:segments="devopsAdoptionSegments"
@segmentsRemoved="deleteSegmentsFromCache"
@openAddRemoveModal="openAddRemoveModal"
/>
</gl-tab>
<gl-tab v-if="isAdmin" data-testid="devops-score-tab" @click="trackDevopsScoreTabClick">
<template #title>{{ s__('DevopsReport|DevOps Score') }}</template>
<devops-score />
</gl-tab>
</gl-tabs>
<devops-adoption-segment-modal
v-if="canRenderModal"
ref="addRemoveModal"
:groups="groups.nodes"
:enabled-groups="devopsAdoptionSegments.nodes"
@segmentsAdded="addSegmentsToCache"
@segmentsRemoved="deleteSegmentsFromCache"
@trackModalOpenState="trackModalOpenState"
/>
</div>
</template>
......@@ -102,7 +102,8 @@ export const DEVOPS_ADOPTION_GROUP_COL_LABEL = __('Group');
export const DEVOPS_ADOPTION_TABLE_CONFIGURATION = [
{
title: s__('DevopsAdoption|Adoption'),
title: s__('DevopsAdoption|Dev'),
tab: 'dev',
cols: [
{
key: 'issueOpened',
......@@ -122,6 +123,24 @@ export const DEVOPS_ADOPTION_TABLE_CONFIGURATION = [
tooltip: s__('DevopsAdoption|At least 1 approval on an MR'),
testId: 'approvalsCol',
},
],
},
{
title: s__('DevopsAdoption|Sec'),
tab: 'sec',
cols: [
{
key: 'securityScanSucceeded',
label: s__('DevopsAdoption|Scanning'),
tooltip: s__('DevopsAdoption|At least 1 security scan of any type run in pipeline'),
testId: 'scanningCol',
},
],
},
{
title: s__('DevopsAdoption|Ops'),
tab: 'ops',
cols: [
{
key: 'runnerConfigured',
label: s__('DevopsAdoption|Runners'),
......@@ -140,12 +159,10 @@ export const DEVOPS_ADOPTION_TABLE_CONFIGURATION = [
tooltip: s__('DevopsAdoption|At least 1 deploy'),
testId: 'deploysCol',
},
{
key: 'securityScanSucceeded',
label: s__('DevopsAdoption|Scanning'),
tooltip: s__('DevopsAdoption|At least 1 security scan of any type run in pipeline'),
testId: 'scanningCol',
},
],
},
];
export const TRACK_ADOPTION_TAB_CLICK_EVENT = 'i_analytics_dev_ops_adoption';
export const TRACK_DEVOPS_SCORE_TAB_CLICK_EVENT = 'i_analytics_dev_ops_score';
......@@ -8,7 +8,13 @@ export default () => {
if (!el) return false;
const { emptyStateSvgPath, groupId } = el.dataset;
const {
emptyStateSvgPath,
groupId,
devopsScoreMetrics,
devopsReportDocsPath,
noDataImagePath,
} = el.dataset;
const isGroup = Boolean(groupId);
......@@ -19,6 +25,9 @@ export default () => {
emptyStateSvgPath,
isGroup,
groupGid: isGroup ? convertToGraphQLId(TYPE_GROUP, groupId) : null,
devopsScoreMetrics: isGroup ? null : JSON.parse(devopsScoreMetrics),
devopsReportDocsPath,
noDataImagePath,
},
render(h) {
return h(DevopsAdoptionApp);
......
import Api from '~/api';
import { historyPushState } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
const DEVOPS_ADOPTION_PANE = 'devops-adoption';
const DEVOPS_ADOPTION_PANE_TAB_CLICK_EVENT = 'i_analytics_dev_ops_adoption';
const tabClickHandler = (e) => {
const { hash } = e.currentTarget;
let tab = null;
if (hash === `#${DEVOPS_ADOPTION_PANE}`) {
tab = DEVOPS_ADOPTION_PANE;
Api.trackRedisHllUserEvent(DEVOPS_ADOPTION_PANE_TAB_CLICK_EVENT);
}
const newUrl = mergeUrlParams({ tab }, window.location.href);
historyPushState(newUrl);
};
const initTabs = () => {
const tabLinks = document.querySelectorAll('.js-devops-tab-item a');
if (tabLinks.length) {
tabLinks.forEach((tabLink) => {
tabLink.addEventListener('click', (e) => tabClickHandler(e));
});
}
};
export default initTabs;
import initDevopAdoption from 'ee/analytics/devops_report/devops_adoption';
import initTabs from 'ee/analytics/devops_report/tabs';
initTabs();
initDevopAdoption();
......@@ -13,6 +13,10 @@
}
}
.actions-cell {
width: $gl-spacing-scale-6;
}
@include media-breakpoint-down(sm) {
.actions-cell {
div {
......
......@@ -5,11 +5,11 @@ module EE
module DevOpsReportController
extend ActiveSupport::Concern
prepended do
track_redis_hll_event :show, name: 'i_analytics_dev_ops_adoption', if: -> { params[:tab] == 'devops-adoption' }
track_redis_hll_event :show, name: 'i_analytics_dev_ops_adoption', if: -> { params[:tab] != 'devops-score' }
end
def should_track_devops_score?
params[:tab] != 'devops-adoption'
params[:tab] == 'devops-score'
end
def show_adoption?
......
- usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
%h2
= _('DevOps Report')
%ul.nav-links.nav-tabs.nav.js-devops-tabs{ role: 'tablist' }
= render 'tab', active: params[:tab] != 'devops-adoption', title: s_('DevopsReport|DevOps Score'), target: '#devops-score'
= render 'tab', active: params[:tab] == 'devops-adoption', title: s_('DevopsReport|Adoption'), target: '#devops-adoption'
.tab-content
.tab-pane{ id: 'devops-score', class: ('active' if params[:tab] != 'devops-adoption') }
= render_ce 'admin/dev_ops_report/report'
.tab-pane{ id: 'devops-adoption', class: ('active' if params[:tab] == 'devops-adoption') }
.js-devops-adoption{ data: { empty_state_svg_path: image_path('illustrations/monitoring/getting_started.svg') } }
- if usage_ping_enabled && show_callout?('dev_ops_report_intro_callout_dismissed')
= render_ce 'admin/dev_ops_report/callout'
- if !usage_ping_enabled
#js-devops-usage-ping-disabled{ data: { is_admin: current_user&.admin.to_s, empty_state_svg_path: image_path('illustrations/convdev/convdev_no_index.svg'), enable_usage_ping_link: metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), docs_link: help_page_path('development/usage_ping/index.md') } }
- else
.js-devops-adoption{ data: { empty_state_svg_path: image_path('illustrations/monitoring/getting_started.svg'), devops_score_metrics: devops_score_metrics(@metric).to_json, devops_report_docs_path: help_page_path('user/admin_area/analytics/dev_ops_report'), no_data_image_path: image_path('dev_ops_report_no_data.svg') } }
%li.nav-item.js-devops-tab-item{ role: 'presentation' }
%a.nav-link{ href: target, class: active_when(active), data: { toggle: 'tab' }, role: 'tab' }
= title
......@@ -38,13 +38,21 @@ RSpec.describe Admin::DevOpsReportController do
sign_in(user)
end
context 'when devops_adoption tab selected' do
it 'tracks devops_adoption usage event' do
shared_examples 'tracks usage event' do |event, tab|
it "tracks #{event} usage event for #{tab}" do
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
.to receive(:track_event).with('i_analytics_dev_ops_adoption', values: kind_of(String))
.to receive(:track_event).with(event, values: kind_of(String))
get :show, params: { tab: 'devops-adoption' }, format: :html
get :show, params: { tab: tab }, format: :html
end
end
context 'when browsing to specific tabs' do
['', 'dev', 'sec', 'ops'].each do |tab|
it_behaves_like 'tracks usage event', 'i_analytics_dev_ops_adoption', tab
end
it_behaves_like 'tracks usage event', 'i_analytics_dev_ops_score', 'devops-score'
end
end
end
......@@ -3,9 +3,23 @@
require 'spec_helper'
RSpec.describe 'DevOps Report page', :js do
tabs_selector = '.js-devops-tabs'
tab_item_selector = '.js-devops-tab-item'
tabs_selector = '.gl-tabs-nav'
tab_item_selector = '.nav-item'
active_tab_selector = '.nav-link.active'
tabs = [
{
value: 'sec',
text: 'Sec'
},
{
value: 'ops',
text: 'Ops'
},
{
value: 'devops-score',
text: 'DevOps Score'
}
]
before do
admin = create(:admin)
......@@ -40,56 +54,68 @@ RSpec.describe 'DevOps Report page', :js do
visit admin_dev_ops_report_path
within tabs_selector do
expect(page.all(:css, tab_item_selector).length).to be(2)
expect(page).to have_text 'DevOps Score Adoption'
expect(page.all(:css, tab_item_selector).length).to be(4)
expect(page).to have_text 'Dev Sec Ops DevOps Score'
end
end
it 'defaults to the DevOps Score tab' do
it 'defaults to the Dev tab' do
visit admin_dev_ops_report_path
within tabs_selector do
expect(page).to have_selector active_tab_selector, text: 'DevOps Score'
expect(page).to have_selector active_tab_selector, text: 'Dev'
end
end
it 'displays the Adoption tab content when selected' do
shared_examples 'displays tab content' do |tab|
it "displays the #{tab} tab content when selected" do
visit admin_dev_ops_report_path
click_link 'Adoption'
click_link tab
within tabs_selector do
expect(page).to have_selector active_tab_selector, text: 'Adoption'
expect(page).to have_selector active_tab_selector, text: tab
end
end
end
it 'does not add the tab param when the DevOps Score tab is selected' do
tabs.each do |tab|
it_behaves_like 'displays tab content', tab[:text]
end
it 'does not add the tab param when the Dev tab is selected' do
visit admin_dev_ops_report_path
click_link 'DevOps Score'
click_link 'Dev'
expect(page).to have_current_path(admin_dev_ops_report_path)
end
it 'adds the ?tab=devops-adoption param when the Adoption tab is selected' do
shared_examples 'appends the tab param to the url' do |tab, text|
it "adds the ?tab=#{tab} param when the #{text} tab is selected" do
visit admin_dev_ops_report_path
click_link 'Adoption'
click_link text
expect(page).to have_current_path(admin_dev_ops_report_path(tab: tab))
end
end
expect(page).to have_current_path(admin_dev_ops_report_path(tab: 'devops-adoption'))
tabs.each do |tab|
it_behaves_like 'appends the tab param to the url', tab[:value], tab[:text]
end
it 'shows the devops adoption tab when the tab param is set' do
visit admin_dev_ops_report_path(tab: 'devops-adoption')
it 'shows the devops core tab when the tab param is set' do
visit admin_dev_ops_report_path(tab: 'devops-score')
within tabs_selector do
expect(page).to have_selector active_tab_selector, text: 'Adoption'
expect(page).to have_selector active_tab_selector, text: 'DevOps Score'
end
end
context 'the devops score tab' do
it 'has dismissable intro callout' do
visit admin_dev_ops_report_path
visit admin_dev_ops_report_path(tab: 'devops-score')
expect(page).to have_content 'Introducing Your DevOps Report'
......@@ -104,13 +130,13 @@ RSpec.describe 'DevOps Report page', :js do
end
it 'shows empty state' do
visit admin_dev_ops_report_path
visit admin_dev_ops_report_path(tab: 'devops-score')
expect(page).to have_selector(".js-empty-state")
end
it 'hides the intro callout' do
visit admin_dev_ops_report_path
visit admin_dev_ops_report_path(tab: 'devops-score')
expect(page).not_to have_content 'Introducing Your DevOps Report'
end
......@@ -120,7 +146,7 @@ RSpec.describe 'DevOps Report page', :js do
it 'shows empty state' do
stub_application_setting(usage_ping_enabled: true)
visit admin_dev_ops_report_path
visit admin_dev_ops_report_path(tab: 'devops-score')
expect(page).to have_content('Data is still calculating')
end
......@@ -131,7 +157,7 @@ RSpec.describe 'DevOps Report page', :js do
stub_application_setting(usage_ping_enabled: true)
create(:dev_ops_report_metric)
visit admin_dev_ops_report_path
visit admin_dev_ops_report_path(tab: 'devops-score')
expect(page).to have_selector('[data-testid="devops-score-app"]')
end
......
import { GlAlert } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { createLocalVue } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import DevopsAdoptionApp from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_app.vue';
......@@ -9,13 +9,17 @@ import DevopsAdoptionSegmentModal from 'ee/analytics/devops_report/devops_adopti
import {
DEVOPS_ADOPTION_STRINGS,
DEFAULT_POLLING_INTERVAL,
DEVOPS_ADOPTION_TABLE_CONFIGURATION,
} from 'ee/analytics/devops_report/devops_adoption/constants';
import bulkFindOrCreateDevopsAdoptionSegmentsMutation from 'ee/analytics/devops_report/devops_adoption/graphql/mutations/bulk_find_or_create_devops_adoption_segments.mutation.graphql';
import devopsAdoptionSegments from 'ee/analytics/devops_report/devops_adoption/graphql/queries/devops_adoption_segments.query.graphql';
import getGroupsQuery from 'ee/analytics/devops_report/devops_adoption/graphql/queries/get_groups.query.graphql';
import { addSegmentsToCache } from 'ee/analytics/devops_report/devops_adoption/utils/cache_updates';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import DevopsScore from '~/analytics/devops_report/components/devops_score.vue';
import API from '~/api';
import {
groupNodes,
nextGroupNode,
......@@ -84,7 +88,7 @@ describe('DevopsAdoptionApp', () => {
function createComponent(options = {}) {
const { mockApollo, data = {}, provide = {} } = options;
return shallowMount(DevopsAdoptionApp, {
return shallowMountExtended(DevopsAdoptionApp, {
localVue,
apolloProvider: mockApollo,
provide,
......@@ -94,6 +98,8 @@ describe('DevopsAdoptionApp', () => {
});
}
const findDevopsScoreTab = () => wrapper.findByTestId('devops-score-tab');
afterEach(() => {
wrapper.destroy();
wrapper = null;
......@@ -444,4 +450,90 @@ describe('DevopsAdoptionApp', () => {
});
});
});
describe('tabs', () => {
const eventTrackingBehaviour = (testId, event) => {
describe('event tracking', () => {
it(`tracks the ${event} event when clicked`, () => {
jest.spyOn(API, 'trackRedisHllUserEvent');
expect(API.trackRedisHllUserEvent).not.toHaveBeenCalled();
wrapper.findByTestId(testId).vm.$emit('click');
expect(API.trackRedisHllUserEvent).toHaveBeenCalledWith(event);
});
it('only tracks the event once', () => {
jest.spyOn(API, 'trackRedisHllUserEvent');
expect(API.trackRedisHllUserEvent).not.toHaveBeenCalled();
const { vm } = wrapper.findByTestId(testId);
vm.$emit('click');
vm.$emit('click');
expect(API.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
});
});
};
const defaultDevopsAdoptionTabBehavior = () => {
describe('devops adoption tabs', () => {
it('displays the configured number of tabs', () => {
expect(wrapper.findAllByTestId('devops-adoption-tab')).toHaveLength(
DEVOPS_ADOPTION_TABLE_CONFIGURATION.length,
);
});
it('displays the devops section component with the tab', () => {
expect(
wrapper.findByTestId('devops-adoption-tab').find(DevopsAdoptionSection).exists(),
).toBe(true);
});
eventTrackingBehaviour('devops-adoption-tab', 'i_analytics_dev_ops_adoption');
});
};
describe('admin level', () => {
beforeEach(() => {
const mockApollo = createMockApolloProvider();
wrapper = createComponent({ mockApollo });
});
defaultDevopsAdoptionTabBehavior();
describe('devops score tab', () => {
it('displays the devops score tab', () => {
expect(findDevopsScoreTab().exists()).toBe(true);
});
it('displays the devops score component', () => {
expect(findDevopsScoreTab().find(DevopsScore).exists()).toBe(true);
});
eventTrackingBehaviour('devops-score-tab', 'i_analytics_dev_ops_score');
});
});
describe('group level', () => {
beforeEach(() => {
const mockApollo = createMockApolloProvider();
wrapper = createComponent({
mockApollo,
provide: {
isGroup: true,
groupGid: devopsAdoptionSegmentsData.nodes[0].namespace.id,
},
});
});
defaultDevopsAdoptionTabBehavior();
it('does not display the devops score tab', () => {
expect(findDevopsScoreTab().exists()).toBe(false);
});
});
});
});
......@@ -101,26 +101,6 @@ export const devopsAdoptionTableHeaders = [
},
{
index: 4,
label: 'Runners',
tooltip: 'Runner configured for project/group',
},
{
index: 5,
label: 'Pipelines',
tooltip: 'At least 1 pipeline successfully run',
},
{
index: 6,
label: 'Deploys',
tooltip: 'At least 1 deploy',
},
{
index: 7,
label: 'Scanning',
tooltip: 'At least 1 security scan of any type run in pipeline',
},
{
index: 8,
label: '',
tooltip: null,
},
......
import initTabs from 'ee/analytics/devops_report/tabs';
import Api from '~/api';
jest.mock('~/api.js');
jest.mock('~/lib/utils/common_utils');
describe('tabs', () => {
beforeEach(() => {
setFixtures(`
<div>
<div class="js-devops-tab-item">
<a href="#devops-score" data-testid='score-tab'>Score</a>
</div>
<div class="js-devops-tab-item">
<a href="#devops-adoption" data-testid='devops-adoption-tab'>Adoption</a>
</div>
</div`);
initTabs();
});
afterEach(() => {});
describe('tracking', () => {
it('tracks event when adoption tab is clicked', () => {
document.querySelector('[data-testid="devops-adoption-tab"]').click();
expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith('i_analytics_dev_ops_adoption');
});
it('does not track an event when score tab is clicked', () => {
document.querySelector('[data-testid="score-tab"]').click();
expect(Api.trackRedisHllUserEvent).not.toHaveBeenCalled();
});
});
});
......@@ -15,7 +15,7 @@ RSpec.describe 'admin/dev_ops_report/show.html.haml' do
it 'disables the feature' do
render
expect(rendered).not_to have_selector('#devops-adoption')
expect(rendered).not_to have_selector('.js-devops-adoption')
end
end
......@@ -25,7 +25,7 @@ RSpec.describe 'admin/dev_ops_report/show.html.haml' do
render
expect(rendered).to have_selector('#devops-adoption')
expect(rendered).to have_selector('.js-devops-adoption')
end
end
end
......@@ -11342,9 +11342,6 @@ msgstr ""
msgid "DevopsAdoption|Adopted"
msgstr ""
msgid "DevopsAdoption|Adoption"
msgstr ""
msgid "DevopsAdoption|An error occurred while removing the group. Please try again."
msgstr ""
......@@ -11381,6 +11378,9 @@ msgstr ""
msgid "DevopsAdoption|Deploys"
msgstr ""
msgid "DevopsAdoption|Dev"
msgstr ""
msgid "DevopsAdoption|DevOps adoption tracks the use of key features across your favorite groups. Add a group to the table to begin."
msgstr ""
......@@ -11408,6 +11408,9 @@ msgstr ""
msgid "DevopsAdoption|Not adopted"
msgstr ""
msgid "DevopsAdoption|Ops"
msgstr ""
msgid "DevopsAdoption|Pipelines"
msgstr ""
......@@ -11429,6 +11432,9 @@ msgstr ""
msgid "DevopsAdoption|Scanning"
msgstr ""
msgid "DevopsAdoption|Sec"
msgstr ""
msgid "DevopsAdoption|There was an error enabling the current group. Please refresh the page."
msgstr ""
......@@ -11441,9 +11447,6 @@ msgstr ""
msgid "DevopsAdoption|You cannot remove the group you are currently in."
msgstr ""
msgid "DevopsReport|Adoption"
msgstr ""
msgid "DevopsReport|DevOps Score"
msgstr ""
......
......@@ -9,12 +9,6 @@ RSpec.describe Admin::DevOpsReportController do
end
end
describe 'should_track_devops_score?' do
it 'is always true' do
expect(controller.should_track_devops_score?).to be_truthy
end
end
describe 'GET #show' do
context 'as admin' do
let(:user) { create(:admin) }
......@@ -31,6 +25,8 @@ RSpec.describe Admin::DevOpsReportController do
it_behaves_like 'tracking unique visits', :show do
let(:target_id) { 'i_analytics_dev_ops_score' }
let(:request_params) { { tab: 'devops-score' } }
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment