Commit 262aa940 authored by Mark Florian's avatar Mark Florian

Merge branch '199066-add-correct-anchor-link-to-new-policy-tab' into 'master'

Link license management button to license compliance policies section

See merge request gitlab-org/gitlab!30344
parents 91dc8ac6 66c54a54
......@@ -16,6 +16,7 @@ import DetectedLicensesTable from './detected_licenses_table.vue';
import PipelineInfo from './pipeline_info.vue';
import LicenseManagement from 'ee/vue_shared/license_compliance/license_management.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getLocationHash } from '~/lib/utils/url_utility';
export default {
name: 'LicenseComplianceApp',
......@@ -45,9 +46,10 @@ export default {
},
data() {
return {
tabIndex: 0,
tabIndex: this.activeTabIndex(),
};
},
tabNames: ['licenses', 'policies'],
computed: {
...mapState(LICENSE_LIST, ['initialized', 'licenses', 'reportInfo', 'listTypes', 'pageInfo']),
...mapState(LICENSE_MANAGEMENT, ['managedLicenses']),
......@@ -68,11 +70,25 @@ export default {
return this.tabIndex === 0;
},
},
watch: {
tabIndex: {
handler(newTabIndex) {
window.location.hash = this.$options.tabNames[newTabIndex];
},
// this ensures that the hash will be set on creation if it is empty
immediate: true,
},
},
created() {
this.fetchLicenses();
},
methods: {
...mapActions(LICENSE_LIST, ['fetchLicenses']),
activeTabIndex() {
const activeTabIndex = this.$options.tabNames.indexOf(getLocationHash());
return activeTabIndex !== -1 ? activeTabIndex : 0;
},
},
};
</script>
......@@ -121,18 +137,18 @@ export default {
<!-- TODO: Remove feature flag -->
<template v-if="hasLicensePolicyList">
<gl-tabs v-model="tabIndex" content-class="pt-0">
<gl-tab>
<gl-tab data-testid="licensesTab">
<template #title>
{{ s__('Licenses|Detected in Project') }}
<span data-testid="licensesTabTitle">{{ s__('Licenses|Detected in Project') }}</span>
<gl-badge pill>{{ licenseCount }}</gl-badge>
</template>
<detected-licenses-table />
</gl-tab>
<gl-tab>
<gl-tab data-testid="policiesTab">
<template #title>
{{ s__('Licenses|Policies') }}
<span data-testid="policiesTabTitle">{{ s__('Licenses|Policies') }}</span>
<gl-badge pill>{{ policyCount }}</gl-badge>
</template>
......
......@@ -35,7 +35,7 @@ module EE
end
def license_management_settings_path(project)
project_settings_ci_cd_path(project, anchor: 'js-license-management')
project_licenses_path(project, anchor: 'policies')
end
def vulnerability_path(entity, *args)
......
---
title: Link license management button to license compliance policies section
merge_request: 30344
author:
type: changed
......@@ -4,6 +4,7 @@ import Vuex from 'vuex';
import { GlEmptyState, GlLoadingIcon, GlTab, GlTabs, GlAlert, GlBadge } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import setWindowLocation from 'helpers/set_window_location_helper';
import { REPORT_STATUS } from 'ee/license_compliance/store/modules/list/constants';
......@@ -83,6 +84,8 @@ const createComponent = ({ state, props, options }) => {
});
};
const findByTestId = testId => wrapper.find(`[data-testid="${testId}"]`);
describe('Project Licenses', () => {
afterEach(() => {
wrapper.destroy();
......@@ -195,6 +198,50 @@ describe('Project Licenses', () => {
expect(wrapper.find(PipelineInfo).exists()).toBe(true);
});
describe.each`
givenLocationHash | expectedActiveTab
${'#licenses'} | ${'licenses'}
${'#policies'} | ${'policies'}
`(
'when window.location contains the hash "$givenLocationHash"',
({ givenLocationHash, expectedActiveTab }) => {
const originalLocation = window.location;
beforeEach(() => {
setWindowLocation(`http://foo.com/index${givenLocationHash}`);
createComponent({
state: {
initialized: true,
isLoading: false,
licenses: [
{
name: 'MIT',
classification: LICENSE_APPROVAL_CLASSIFICATION.DENIED,
components: [],
},
],
pageInfo: 1,
},
options: {
provide: {
glFeatures: { licensePolicyList: true },
},
mount: true,
},
});
});
afterEach(() => {
window.location = originalLocation;
});
it(`sets the active tab to be "${expectedActiveTab}"`, () => {
expect(findByTestId(`${expectedActiveTab}Tab`).classes()).toContain('active');
});
},
);
describe('when the tabs are rendered', () => {
const pageInfo = {
total: 1,
......@@ -228,6 +275,21 @@ describe('Project Licenses', () => {
});
});
it.each`
givenActiveTab | expectedLocationHash
${'policies'} | ${'#policies'}
${'licenses'} | ${'#licenses'}
`(
'sets the location hash to "$expectedLocationHash" when the "$givenTab" tab is activate',
({ givenActiveTab, expectedLocationHash }) => {
findByTestId(`${givenActiveTab}TabTitle`).trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(window.location.hash).toBe(expectedLocationHash);
});
},
);
it('it renders the correct count in "Detected in project" tab', () => {
expect(
wrapper
......
......@@ -87,6 +87,14 @@ describe EE::GitlabRoutingHelper do
end
end
describe '#license_management_settings_path' do
it 'generates a path to the license compliance page' do
result = helper.license_management_settings_path(project)
expect(result).to eq('/foo/bar/-/licenses#policies')
end
end
describe '#user_group_saml_omniauth_metadata_path' do
subject do
helper.user_group_saml_omniauth_metadata_path(group)
......
/**
* setWindowLocation allows for setting `window.location`
* (doing so directly is causing an error in jsdom)
*
* Example usage:
* assert(window.location.hash === undefined);
* setWindowLocation('http://example.com#foo')
* assert(window.location.hash === '#foo');
*
* More information:
* https://github.com/facebook/jest/issues/890
*
* @param url
*/
export default function setWindowLocation(url) {
const parsedUrl = new URL(url);
const newLocationValue = [
'hash',
'host',
'hostname',
'href',
'origin',
'pathname',
'port',
'protocol',
'search',
].reduce(
(location, prop) => ({
...location,
[prop]: parsedUrl[prop],
}),
{},
);
Object.defineProperty(window, 'location', {
value: newLocationValue,
writable: true,
});
}
import setWindowLocation from './set_window_location_helper';
describe('setWindowLocation', () => {
const originalLocation = window.location;
afterEach(() => {
window.location = originalLocation;
});
it.each`
url | property | value
${'https://gitlab.com#foo'} | ${'hash'} | ${'#foo'}
${'http://gitlab.com'} | ${'host'} | ${'gitlab.com'}
${'http://gitlab.org'} | ${'hostname'} | ${'gitlab.org'}
${'http://gitlab.org/foo#bar'} | ${'href'} | ${'http://gitlab.org/foo#bar'}
${'http://gitlab.com'} | ${'origin'} | ${'http://gitlab.com'}
${'http://gitlab.com/foo/bar/baz'} | ${'pathname'} | ${'/foo/bar/baz'}
${'https://gitlab.com'} | ${'protocol'} | ${'https:'}
${'http://gitlab.com#foo'} | ${'protocol'} | ${'http:'}
${'http://gitlab.com:8080'} | ${'port'} | ${'8080'}
${'http://gitlab.com?foo=bar&bar=foo'} | ${'search'} | ${'?foo=bar&bar=foo'}
`(
'sets "window.location.$property" to be "$value" when called with: "$url"',
({ url, property, value }) => {
expect(window.location).toBe(originalLocation);
setWindowLocation(url);
expect(window.location[property]).toBe(value);
},
);
it.each([null, 1, undefined, false, '', 'gitlab.com'])(
'throws an error when called with an invalid url: "%s"',
invalidUrl => {
expect(() => setWindowLocation(invalidUrl)).toThrow(new TypeError('Invalid URL'));
expect(window.location).toBe(originalLocation);
},
);
});
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