Commit a9f9b885 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch '347409-migrate-package-details-page-to-vue-router-4' into 'master'

Enable frontend routing for package details

See merge request gitlab-org/gitlab!77762
parents c940f3d7 37277b57
......@@ -4,6 +4,7 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import PerformancePlugin from '~/performance/vue_performance_plugin';
import Translate from '~/vue_shared/translate';
import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue';
import { renderBreadcrumb } from '~/packages_and_registries/shared/utils';
import { apolloProvider } from './graphql/index';
import RegistryExplorer from './pages/index.vue';
import createRouter from './router';
......@@ -84,38 +85,8 @@ export default () => {
},
});
const attachBreadcrumb = () => {
const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
const crumbs = [breadCrumbEl.querySelector('h2')];
const nestedBreadcrumbEl = document.createElement('div');
breadCrumbEl.replaceChild(nestedBreadcrumbEl, breadCrumbEl.querySelector('h2'));
return new Vue({
el: nestedBreadcrumbEl,
router,
apolloProvider,
components: {
RegistryBreadcrumb,
},
render(createElement) {
// FIXME(@tnir): this is a workaround until the MR gets merged:
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
const parentEl = breadCrumbEl.parentElement.parentElement;
if (parentEl) {
parentEl.classList.remove('breadcrumbs-container');
parentEl.classList.add('gl-display-flex');
parentEl.classList.add('w-100');
}
// End of FIXME(@tnir)
return createElement('registry-breadcrumb', {
class: breadCrumbEl.className,
props: {
crumbs,
},
});
},
});
return {
attachBreadcrumb: renderBreadcrumb(router, apolloProvider, RegistryBreadcrumb),
attachMainComponent,
};
return { attachBreadcrumb, attachMainComponent };
};
......@@ -26,7 +26,7 @@ export default {
GlSprintf,
GlFormRadioGroup,
},
inject: ['npmPath'],
inject: ['npmInstanceUrl'],
props: {
packageEntity: {
type: Object,
......@@ -66,7 +66,9 @@ export default {
npmSetupCommand(type, endpointType) {
const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/'));
const npmPathForEndpoint =
endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE ? this.npmPath : this.packageEntity.npmUrl;
endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE
? this.npmInstanceUrl
: this.packageEntity.npmUrl;
if (type === NPM_PACKAGE_MANAGER) {
return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`;
......
<script>
import { GlButton, GlLink, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui';
import { GlButton, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import {
......@@ -18,7 +18,6 @@ export default {
name: 'PackageListRow',
components: {
GlButton,
GlLink,
GlSprintf,
GlTruncate,
PackageTags,
......@@ -42,9 +41,8 @@ export default {
packageType() {
return getPackageTypeLabel(this.packageEntity.packageType);
},
packageLink() {
const { project, id } = this.packageEntity;
return `${project?.webUrl}/-/packages/${getIdFromGraphQLId(id)}`;
packageId() {
return getIdFromGraphQLId(this.packageEntity.id);
},
pipeline() {
return this.packageEntity?.pipelines?.nodes[0];
......@@ -61,6 +59,9 @@ export default {
disabledRow() {
return this.packageEntity.status && this.packageEntity.status !== PACKAGE_DEFAULT_STATUS;
},
routerLinkEvent() {
return this.disabledRow ? '' : 'click';
},
},
i18n: {
erroredPackageText: s__('PackageRegistry|Invalid Package: failed metadata extraction'),
......@@ -73,14 +74,15 @@ export default {
<list-item data-qa-selector="package_row" :disabled="disabledRow">
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<gl-link
:href="packageLink"
<router-link
class="gl-text-body gl-min-w-0"
data-testid="details-link"
data-qa-selector="package_link"
:disabled="disabledRow"
:event="routerLinkEvent"
:to="{ name: 'details', params: { id: packageId } }"
>
<gl-truncate :text="packageEntity.name" />
</gl-link>
</router-link>
<gl-button
v-if="showWarningIcon"
......
......@@ -74,6 +74,7 @@ export const FETCH_PACKAGE_DETAILS_ERROR_MESSAGE = s__(
);
export const DELETE_PACKAGE_SUCCESS_MESSAGE = s__('PackageRegistry|Package deleted successfully');
export const PACKAGE_REGISTRY_TITLE = __('Package Registry');
export const PACKAGE_ERROR_STATUS = 'ERROR';
export const PACKAGE_DEFAULT_STATUS = 'DEFAULT';
......
......@@ -2,18 +2,39 @@ import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index';
import PackageRegistry from '~/packages_and_registries/package_registry/pages/index.vue';
import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue';
import { renderBreadcrumb } from '~/packages_and_registries/shared/utils';
import createRouter from './router';
Vue.use(Translate);
export default () => {
const el = document.getElementById('js-vue-packages-list');
const { endpoint, resourceId, fullPath, pageType, emptyListIllustration } = el.dataset;
const router = createRouter(endpoint);
const {
endpoint,
resourceId,
fullPath,
pageType,
emptyListIllustration,
npmInstanceUrl,
projectListUrl,
groupListUrl,
} = el.dataset;
const isGroupPage = pageType === 'groups';
return new Vue({
// This is a mini state to help the breadcrumb have the correct name in the details page
const breadCrumbState = Vue.observable({
name: '',
updateName(value) {
this.name = value;
},
});
const router = createRouter(endpoint, breadCrumbState);
const attachMainComponent = () =>
new Vue({
el,
router,
apolloProvider,
......@@ -22,9 +43,18 @@ export default () => {
fullPath,
emptyListIllustration,
isGroupPage,
npmInstanceUrl,
projectListUrl,
groupListUrl,
breadCrumbState,
},
render(createElement) {
return createElement(PackageRegistry);
},
});
return {
attachBreadcrumb: renderBreadcrumb(router, apolloProvider, RegistryBreadcrumb),
attachMainComponent,
};
};
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import PackagesApp from '~/packages_and_registries/package_registry/components/details/app.vue';
import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index';
import Translate from '~/vue_shared/translate';
Vue.use(Translate);
export default () => {
const el = document.getElementById('js-vue-packages-detail-new');
if (!el) {
return null;
}
const { canDelete, ...datasetOptions } = el.dataset;
return new Vue({
el,
apolloProvider,
provide: {
canDelete: parseBoolean(canDelete),
...datasetOptions,
},
render(createElement) {
return createElement(PackagesApp);
},
});
};
......@@ -68,7 +68,7 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
inject: ['packageId', 'svgPath', 'projectListUrl', 'groupListUrl'],
inject: ['emptyListIllustration', 'projectListUrl', 'groupListUrl', 'breadCrumbState'],
trackingActions: {
DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
......@@ -100,12 +100,20 @@ export default {
error,
});
},
result() {
this.breadCrumbState.updateName(
`${this.packageEntity?.name} v ${this.packageEntity?.version}`,
);
},
},
},
computed: {
projectName() {
return this.packageEntity.project?.name;
},
packageId() {
return this.$route.params.id;
},
queryVariables() {
return {
id: convertToGraphQLId('Packages::Package', this.packageId),
......@@ -229,7 +237,7 @@ export default {
v-if="!isValidPackage"
:title="s__('PackageRegistry|Unable to load package')"
:description="s__('PackageRegistry|There was a problem fetching the details for this package.')"
:svg-path="svgPath"
:svg-path="emptyListIllustration"
/>
<div v-else-if="!isLoading" class="packages-app">
<package-title :package-entity="packageEntity">
......
import Vue from 'vue';
import VueRouter from 'vue-router';
import List from '~/packages_and_registries/package_registry/pages/list.vue';
import Details from '~/packages_and_registries/package_registry/pages/details.vue';
import { PACKAGE_REGISTRY_TITLE } from '~/packages_and_registries/package_registry/constants';
Vue.use(VueRouter);
export default function createRouter(base) {
export default function createRouter(base, breadCrumbState) {
const router = new VueRouter({
base,
mode: 'history',
......@@ -13,9 +15,25 @@ export default function createRouter(base) {
name: 'list',
path: '/',
component: List,
meta: {
nameGenerator: () => PACKAGE_REGISTRY_TITLE,
root: true,
},
},
{
name: 'details',
path: '/:id',
component: Details,
meta: {
nameGenerator: () => breadCrumbState.name,
},
},
],
});
router.afterEach(() => {
breadCrumbState.updateName('');
});
return router;
}
......@@ -20,8 +20,11 @@ export default {
isRootRoute() {
return this.$route.name === this.rootRoute.name;
},
detailsRouteName() {
return this.detailsRoute.meta.nameGenerator();
},
isLoaded() {
return this.isRootRoute || this.$store?.state.imageDetails?.name;
return this.isRootRoute || this.detailsRouteName;
},
allCrumbs() {
const crumbs = [
......@@ -32,7 +35,7 @@ export default {
];
if (!this.isRootRoute) {
crumbs.push({
text: this.detailsRoute.meta.nameGenerator(),
text: this.detailsRouteName,
href: this.detailsRoute.meta.path,
});
}
......@@ -45,7 +48,9 @@ export default {
<template>
<gl-breadcrumb :key="isLoaded" :items="allCrumbs">
<template #separator>
<span class="gl-mx-n5">
<gl-icon name="angle-right" :size="8" />
</span>
</template>
</gl-breadcrumb>
</template>
import Vue from 'vue';
import { queryToObject } from '~/lib/utils/url_utility';
import { FILTERED_SEARCH_TERM } from './constants';
......@@ -38,3 +39,37 @@ export const getCommitLink = ({ project_path: projectPath, pipeline = {} }, isGr
return `../commit/${pipeline.sha}`;
};
export const renderBreadcrumb = (router, apolloProvider, RegistryBreadcrumb) => () => {
const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
const lastCrumb = breadCrumbEl.children[0];
const crumbs = [lastCrumb];
const nestedBreadcrumbEl = document.createElement('div');
breadCrumbEl.replaceChild(nestedBreadcrumbEl, lastCrumb);
return new Vue({
el: nestedBreadcrumbEl,
router,
apolloProvider,
components: {
RegistryBreadcrumb,
},
render(createElement) {
// FIXME(@tnir): this is a workaround until the MR gets merged:
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
const parentEl = breadCrumbEl.parentElement.parentElement;
if (parentEl) {
parentEl.classList.remove('breadcrumbs-container');
parentEl.classList.add('gl-display-flex');
parentEl.classList.add('w-100');
}
// End of FIXME(@tnir)
return createElement('registry-breadcrumb', {
class: breadCrumbEl.className,
props: {
crumbs,
},
});
},
});
};
import packageApp from '~/packages_and_registries/package_registry/index';
packageApp();
const app = packageApp();
if (app) {
app.attachBreadcrumb();
app.attachMainComponent();
}
import packageApp from '~/packages_and_registries/package_registry/index';
packageApp();
const app = packageApp();
if (app) {
app.attachBreadcrumb();
app.attachMainComponent();
}
import initPackageDetails from '~/packages_and_registries/package_registry/pages/details';
initPackageDetails();
......@@ -6,6 +6,11 @@ module Groups
feature_category :package_registry
# The show action renders index to allow frontend routing to work on page refresh
def show
render :index
end
private
def verify_packages_enabled!
......
......@@ -7,8 +7,9 @@ module Projects
feature_category :package_registry
# The show action renders index to allow frontend routing to work on page refresh
def show
@package = project.packages.find(params[:id])
render :index
end
end
end
......
......@@ -7,4 +7,7 @@
full_path: @group.full_path,
endpoint: group_packages_path(@group),
page_type: 'groups',
empty_list_illustration: image_path('illustrations/no-packages.svg'), } }
empty_list_illustration: image_path('illustrations/no-packages.svg'),
npm_instance_url: package_registry_instance_url(:npm),
project_list_url: '',
group_list_url: group_packages_path(@group) } }
......@@ -7,4 +7,7 @@
full_path: @project.full_path,
endpoint: project_packages_path(@project),
page_type: 'projects',
empty_list_illustration: image_path('illustrations/no-packages.svg'), } }
empty_list_illustration: image_path('illustrations/no-packages.svg'),
npm_instance_url: package_registry_instance_url(:npm),
project_list_url: project_packages_path(@project),
group_list_url: '' } }
- add_to_breadcrumbs _("Package Registry"), project_packages_path(@project)
- add_to_breadcrumbs @package.name, project_packages_path(@project)
- breadcrumb_title @package.version
- page_title _("Package Registry")
- @content_class = "limit-container-width" unless fluid_layout
.row
.col-12
#js-vue-packages-detail-new{ data: package_details_data(@project, @package) }
......@@ -64,7 +64,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
post :toggle_subscription, on: :member
end
resources :packages, only: [:index]
resources :packages, only: [:index, :show]
resources :milestones, constraints: { id: %r{[^/]+} } do
member do
......
......@@ -5,7 +5,7 @@ module QA
module Project
module Packages
class Show < QA::Page::Base
view 'app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue' do
view 'app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue' do
element :delete_button
element :delete_modal_button
element :package_information_content
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::PackagesController do
let_it_be(:group) { create(:group) }
let(:page) { :index }
let(:additional_parameters) { {} }
subject do
get page, params: additional_parameters.merge({
group_id: group
})
end
context 'GET #index' do
it_behaves_like 'returning response status', :ok
end
context 'GET #show' do
let(:page) { :show }
let(:additional_parameters) { { id: 1 } }
it_behaves_like 'returning response status', :ok
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Packages::PackagesController do
let_it_be(:project) { create(:project, :public) }
let(:page) { :index }
let(:additional_parameters) { {} }
subject do
get page, params: additional_parameters.merge({
project_id: project,
namespace_id: project.namespace
})
end
context 'GET #index' do
it_behaves_like 'returning response status', :ok
end
context 'GET #show' do
let(:page) { :show }
let(:additional_parameters) { { id: 1 } }
it_behaves_like 'returning response status', :ok
end
end
......@@ -42,6 +42,9 @@ RSpec.describe 'Group Packages' do
let_it_be(:maven_package) { create(:maven_package, project: second_project, name: 'aaa', created_at: 2.days.ago, version: '2.0.0') }
let_it_be(:packages) { [npm_package, maven_package] }
let(:package) { packages.first }
let(:package_details_path) { group_package_path(group, package) }
it_behaves_like 'packages list', check_project_name: true
it_behaves_like 'package details link'
......
......@@ -35,6 +35,9 @@ RSpec.describe 'Packages' do
let_it_be(:maven_package) { create(:maven_package, project: project, name: 'aaa', created_at: 2.days.ago, version: '2.0.0') }
let_it_be(:packages) { [npm_package, maven_package] }
let(:package) { packages.first }
let(:package_details_path) { project_package_path(project, package) }
it_behaves_like 'packages list'
it_behaves_like 'package details link'
......
......@@ -32,7 +32,7 @@ exports[`NpmInstallation renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy npm setup command"
instruction="echo @gitlab-org:registry=npmPath/ >> .npmrc"
instruction="echo @gitlab-org:registry=npmInstanceUrl/ >> .npmrc"
label=""
trackingaction="copy_npm_setup_command"
trackinglabel="code_instruction"
......
......@@ -35,7 +35,7 @@ describe('NpmInstallation', () => {
function createComponent({ data = {} } = {}) {
wrapper = shallowMountExtended(NpmInstallation, {
provide: {
npmPath: 'npmPath',
npmInstanceUrl: 'npmInstanceUrl',
},
propsData: {
packageEntity,
......@@ -117,7 +117,7 @@ describe('NpmInstallation', () => {
it('renders the correct setup command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo @gitlab-org:registry=npmPath/ >> .npmrc',
instruction: 'echo @gitlab-org:registry=npmInstanceUrl/ >> .npmrc',
multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
......@@ -139,7 +139,7 @@ describe('NpmInstallation', () => {
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo @gitlab-org:registry=npmPath/ >> .npmrc`,
instruction: `echo @gitlab-org:registry=npmInstanceUrl/ >> .npmrc`,
multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
......@@ -161,7 +161,7 @@ describe('NpmInstallation', () => {
it('renders the correct registry command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo \\"@gitlab-org:registry\\" \\"npmPath/\\" >> .yarnrc',
instruction: 'echo \\"@gitlab-org:registry\\" \\"npmInstanceUrl/\\" >> .yarnrc',
multiline: false,
trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND,
});
......@@ -183,7 +183,7 @@ describe('NpmInstallation', () => {
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo \\"@gitlab-org:registry\\" \\"npmPath/\\" >> .yarnrc',
instruction: 'echo \\"@gitlab-org:registry\\" \\"npmInstanceUrl/\\" >> .yarnrc',
multiline: false,
trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND,
});
......
......@@ -22,16 +22,20 @@ exports[`packages_list_row renders 1`] = `
<div
class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"
>
<gl-link-stub
<router-link-stub
ariacurrentvalue="page"
class="gl-text-body gl-min-w-0"
data-qa-selector="package_link"
href="http://gdk.test:3000/gitlab-org/gitlab-test/-/packages/111"
data-testid="details-link"
event="click"
tag="a"
to="[object Object]"
>
<gl-truncate-stub
position="end"
text="@gitlab-org/package-15"
/>
</gl-link-stub>
</router-link-stub>
<!---->
......
import { GlLink, GlSprintf } from '@gitlab/ui';
import { GlSprintf } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import VueRouter from 'vue-router';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue';
import PackagePath from '~/packages_and_registries/shared/components/package_path.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
......@@ -13,6 +17,9 @@ import { PACKAGE_ERROR_STATUS } from '~/packages_and_registries/package_registry
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import { packageData, packagePipelines, packageProject, packageTags } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(VueRouter);
describe('packages_list_row', () => {
let wrapper;
......@@ -28,7 +35,7 @@ describe('packages_list_row', () => {
const findDeleteButton = () => wrapper.findByTestId('action-delete');
const findPackageIconAndName = () => wrapper.find(PackageIconAndName);
const findListItem = () => wrapper.findComponent(ListItem);
const findPackageLink = () => wrapper.findComponent(GlLink);
const findPackageLink = () => wrapper.findByTestId('details-link');
const findWarningIcon = () => wrapper.findByTestId('warning-icon');
const findLeftSecondaryInfos = () => wrapper.findByTestId('left-secondary-infos');
const findPublishMethod = () => wrapper.findComponent(PublishMethod);
......@@ -40,6 +47,7 @@ describe('packages_list_row', () => {
provide = defaultProvide,
} = {}) => {
wrapper = shallowMountExtended(PackagesListRow, {
localVue,
provide,
stubs: {
ListItem,
......@@ -63,6 +71,15 @@ describe('packages_list_row', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('has a link to navigate to the details page', () => {
mountComponent();
expect(findPackageLink().props()).toMatchObject({
event: 'click',
to: { name: 'details', params: { id: getIdFromGraphQLId(packageWithoutTags.id) } },
});
});
describe('tags', () => {
it('renders package tags when a package has tags', () => {
mountComponent({ packageEntity: packageWithTags });
......@@ -120,7 +137,7 @@ describe('packages_list_row', () => {
});
it('details link is disabled', () => {
expect(findPackageLink().attributes('disabled')).toBe('true');
expect(findPackageLink().props('event')).toBe('');
});
it('has a warning icon', () => {
......
......@@ -9,7 +9,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import PackagesApp from '~/packages_and_registries/package_registry/components/details/app.vue';
import PackagesApp from '~/packages_and_registries/package_registry/pages/details.vue';
import DependencyRow from '~/packages_and_registries/package_registry/components/details/dependency_row.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
......@@ -36,7 +36,7 @@ import {
packageFiles,
packageDestroyFileMutation,
packageDestroyFileMutationError,
} from '../../mock_data';
} from '../mock_data';
jest.mock('~/flash');
useMockLocationHelper();
......@@ -47,18 +47,22 @@ describe('PackagesApp', () => {
let wrapper;
let apolloProvider;
const breadCrumbState = {
updateName: jest.fn(),
};
const provide = {
packageId: '111',
svgPath: 'svgPath',
npmPath: 'npmPath',
npmHelpPath: 'npmHelpPath',
emptyListIllustration: 'svgPath',
projectListUrl: 'projectListUrl',
groupListUrl: 'groupListUrl',
breadCrumbState,
};
function createComponent({
resolver = jest.fn().mockResolvedValue(packageDetailsQuery()),
fileDeleteMutationResolver = jest.fn().mockResolvedValue(packageDestroyFileMutation()),
routeId = '1',
} = {}) {
localVue.use(VueApollo);
......@@ -84,6 +88,13 @@ describe('PackagesApp', () => {
GlTabs,
GlTab,
},
mocks: {
$route: {
params: {
id: routeId,
},
},
},
});
}
......@@ -172,6 +183,15 @@ describe('PackagesApp', () => {
});
});
it('calls the appropriate function to set the breadcrumbState', async () => {
const { name, version } = packageData();
createComponent();
await waitForPromises();
expect(breadCrumbState.updateName).toHaveBeenCalledWith(`${name} v ${version}`);
});
describe('delete package', () => {
const originalReferrer = document.referrer;
const setReferrer = (value = packageDetailsQuery().data.package.project.name) => {
......
......@@ -23,6 +23,9 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
<span
class="gl-breadcrumb-separator"
data-testid="separator"
>
<span
class="gl-mx-n5"
>
<svg
aria-hidden="true"
......@@ -35,6 +38,7 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
/>
</svg>
</span>
</span>
</a>
</li>
<li
......
......@@ -19,14 +19,12 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
end
RSpec.shared_examples 'package details link' do |property|
let(:package) { packages.first }
it 'navigates to the correct url' do
page.within(packages_table_selector) do
click_link package.name
end
expect(page).to have_current_path(project_package_path(package.project, package))
expect(page).to have_current_path(package_details_path)
expect(page).to have_css('.packages-app h2[data-testid="title"]', text: package.name)
......
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