Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
02bb524d05
commit
ae68f3fe9c
|
|
@ -239,6 +239,7 @@ e2e:test-product-analytics:
|
|||
PIPELINE_NAME: E2E Product Analytics
|
||||
GDK_IMAGE: "${CI_REGISTRY_IMAGE}/gitlab-qa-gdk:${CI_COMMIT_SHA}"
|
||||
GITLAB_QA_IMAGE: "${CI_REGISTRY_IMAGE}/gitlab-ee-qa:${CI_COMMIT_SHA}"
|
||||
UPSTREAM_COMMIT_SHA: $CI_COMMIT_SHA
|
||||
needs:
|
||||
- build-gdk-image
|
||||
- build-qa-image
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ include:
|
|||
inputs:
|
||||
cng_path: 'charts/components/images'
|
||||
- project: 'gitlab-org/quality/pipeline-common'
|
||||
ref: '8.22.0'
|
||||
ref: '9.0.0'
|
||||
file: ci/base.gitlab-ci.yml
|
||||
|
||||
stages:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
a2ca345cd681ef39094623d8f4b6ed65996de57d
|
||||
a3988141dd517bc75af2775a168033df1bed742c
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ export default {
|
|||
onAttributeUpdated: this.onAttributeUpdated,
|
||||
onIssuableDeleted: this.refetchActiveIssuableLists,
|
||||
onStateUpdated: this.onStateUpdated,
|
||||
modalWorkItemFullPath: this.modalWorkItemFullPath,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { GlLoadingIcon, GlKeysetPagination } from '@gitlab/ui';
|
|||
import projectsEmptyStateSvgPath from '@gitlab/svgs/dist/illustrations/empty-state/empty-projects-md.svg?url';
|
||||
import { s__, __ } from '~/locale';
|
||||
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import { ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
import { DEFAULT_PER_PAGE } from '~/api';
|
||||
import { deleteProject } from '~/rest_api';
|
||||
|
|
@ -10,7 +11,6 @@ import { createAlert } from '~/alert';
|
|||
import {
|
||||
renderDeleteSuccessToast,
|
||||
deleteParams,
|
||||
formatProjects,
|
||||
timestampType,
|
||||
} from 'ee_else_ce/organizations/shared/utils';
|
||||
import { SORT_ITEM_NAME, SORT_DIRECTION_ASC } from '../constants';
|
||||
|
|
@ -109,7 +109,7 @@ export default {
|
|||
},
|
||||
}) {
|
||||
return {
|
||||
nodes: formatProjects(nodes),
|
||||
nodes: formatGraphQLProjects(nodes),
|
||||
pageInfo,
|
||||
};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "ee_else_ce/organizations/shared/graphql/fragments/project.fragment.graphql"
|
||||
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
|
||||
|
||||
query getOrganizationProjects(
|
||||
$id: OrganizationsOrganizationID!
|
||||
|
|
|
|||
|
|
@ -13,20 +13,6 @@ import {
|
|||
QUERY_PARAM_START_CURSOR,
|
||||
} from './constants';
|
||||
|
||||
const availableProjectActions = (userPermissions) => {
|
||||
const baseActions = [];
|
||||
|
||||
if (userPermissions.viewEditPage) {
|
||||
baseActions.push(ACTION_EDIT);
|
||||
}
|
||||
|
||||
if (userPermissions.removeProject) {
|
||||
baseActions.push(ACTION_DELETE);
|
||||
}
|
||||
|
||||
return baseActions;
|
||||
};
|
||||
|
||||
const availableGroupActions = (userPermissions) => {
|
||||
const baseActions = [];
|
||||
|
||||
|
|
@ -41,37 +27,6 @@ const availableGroupActions = (userPermissions) => {
|
|||
return baseActions;
|
||||
};
|
||||
|
||||
export const formatProjects = (projects) =>
|
||||
projects.map(
|
||||
({
|
||||
id,
|
||||
nameWithNamespace,
|
||||
mergeRequestsAccessLevel,
|
||||
issuesAccessLevel,
|
||||
forkingAccessLevel,
|
||||
webUrl,
|
||||
userPermissions,
|
||||
maxAccessLevel: accessLevel,
|
||||
organizationEditPath: editPath,
|
||||
...project
|
||||
}) => ({
|
||||
...project,
|
||||
id: getIdFromGraphQLId(id),
|
||||
name: nameWithNamespace,
|
||||
mergeRequestsAccessLevel: mergeRequestsAccessLevel.stringValue,
|
||||
issuesAccessLevel: issuesAccessLevel.stringValue,
|
||||
forkingAccessLevel: forkingAccessLevel.stringValue,
|
||||
webUrl,
|
||||
isForked: false,
|
||||
accessLevel,
|
||||
editPath,
|
||||
availableActions: availableProjectActions(userPermissions),
|
||||
actionLoadingStates: {
|
||||
[ACTION_DELETE]: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export const formatGroups = (groups) =>
|
||||
groups.map(
|
||||
({
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
<script>
|
||||
import { GlTabs, GlTab, GlBadge, GlSprintf } from '@gitlab/ui';
|
||||
import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { TIMESTAMP_TYPE_UPDATED_AT } from '~/vue_shared/components/resource_lists/constants';
|
||||
import {
|
||||
PROJECT_DASHBOARD_TABS,
|
||||
CONTRIBUTED_TAB,
|
||||
CUSTOM_DASHBOARD_ROUTE_NAMES,
|
||||
PROJECT_DASHBOARD_TABS,
|
||||
} from 'ee_else_ce/projects/your_work/constants';
|
||||
import TabView from './tab_view.vue';
|
||||
|
||||
export default {
|
||||
name: 'YourWorkProjectsApp',
|
||||
TIMESTAMP_TYPE_UPDATED_AT,
|
||||
i18n: {
|
||||
heading: __('Projects'),
|
||||
activeTab: __('Active tab: %{tab}'),
|
||||
},
|
||||
components: {
|
||||
GlTabs,
|
||||
GlTab,
|
||||
GlBadge,
|
||||
GlSprintf,
|
||||
TabView,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -54,7 +56,7 @@ export default {
|
|||
<h1 class="page-title gl-mt-5 gl-text-size-h-display">{{ $options.i18n.heading }}</h1>
|
||||
|
||||
<gl-tabs :value="activeTabIndex" @input="onTabUpdate">
|
||||
<gl-tab v-for="tab in formattedTabs" :key="tab.text">
|
||||
<gl-tab v-for="tab in formattedTabs" :key="tab.text" lazy>
|
||||
<template #title>
|
||||
<span data-testid="projects-dashboard-tab-title">
|
||||
<span>{{ tab.text }}</span>
|
||||
|
|
@ -62,11 +64,8 @@ export default {
|
|||
</span>
|
||||
</template>
|
||||
|
||||
<gl-sprintf :message="$options.i18n.activeTab">
|
||||
<template #tab>
|
||||
{{ tab.text }}
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<tab-view v-if="tab.query" :tab="tab" />
|
||||
<template v-else>{{ tab.text }}</template>
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { get } from 'lodash';
|
||||
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
|
||||
import { __ } from '~/locale';
|
||||
import { createAlert } from '~/alert';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import { TIMESTAMP_TYPE_UPDATED_AT } from '~/vue_shared/components/resource_lists/constants';
|
||||
|
||||
export default {
|
||||
name: 'YourWorkProjectsTabView',
|
||||
TIMESTAMP_TYPE_UPDATED_AT,
|
||||
i18n: {
|
||||
errorMessage: __(
|
||||
'An error occurred loading the projects. Please refresh the page to try again.',
|
||||
),
|
||||
},
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
ProjectsList,
|
||||
},
|
||||
props: {
|
||||
tab: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
projects: {},
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
projects() {
|
||||
return {
|
||||
query: this.tab.query,
|
||||
update(response) {
|
||||
const { nodes, pageInfo } = get(response, this.tab.queryPath);
|
||||
|
||||
return {
|
||||
nodes: formatGraphQLProjects(nodes),
|
||||
pageInfo,
|
||||
};
|
||||
},
|
||||
error(error) {
|
||||
createAlert({ message: this.$options.i18n.errorMessage, error, captureError: true });
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
nodes() {
|
||||
return this.projects.nodes || [];
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries.projects.loading;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-loading-icon v-if="isLoading" class="gl-mt-5" size="md" />
|
||||
<projects-list
|
||||
v-else-if="nodes.length"
|
||||
:projects="nodes"
|
||||
show-project-icon
|
||||
list-item-class="gl-px-5"
|
||||
:timestamp-type="$options.TIMESTAMP_TYPE_UPDATED_AT"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
import { __ } from '~/locale';
|
||||
import contributedProjectsQuery from './graphql/queries/contributed_projects.query.graphql';
|
||||
|
||||
export const CONTRIBUTED_TAB = {
|
||||
text: __('Contributed'),
|
||||
value: 'contributed',
|
||||
query: contributedProjectsQuery,
|
||||
queryPath: 'currentUser.contributedProjects',
|
||||
};
|
||||
|
||||
export const STARRED_TAB = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
|
||||
|
||||
query getContributedProjects {
|
||||
currentUser {
|
||||
id
|
||||
contributedProjects {
|
||||
nodes {
|
||||
...Project
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import routes from './routes';
|
||||
import YourWorkProjectsApp from './components/app.vue';
|
||||
|
||||
|
|
@ -20,9 +22,14 @@ export const initYourWorkProjects = () => {
|
|||
|
||||
if (!el) return false;
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
router: createRouter(),
|
||||
apolloProvider,
|
||||
name: 'YourWorkProjectsRoot',
|
||||
render(createElement) {
|
||||
return createElement(YourWorkProjectsApp);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
|
||||
const availableGraphQLProjectActions = (userPermissions) => {
|
||||
const baseActions = [];
|
||||
|
||||
if (userPermissions.viewEditPage) {
|
||||
baseActions.push(ACTION_EDIT);
|
||||
}
|
||||
|
||||
if (userPermissions.removeProject) {
|
||||
baseActions.push(ACTION_DELETE);
|
||||
}
|
||||
|
||||
return baseActions;
|
||||
};
|
||||
|
||||
export const formatGraphQLProjects = (projects) =>
|
||||
projects.map(
|
||||
({
|
||||
id,
|
||||
nameWithNamespace,
|
||||
mergeRequestsAccessLevel,
|
||||
issuesAccessLevel,
|
||||
forkingAccessLevel,
|
||||
webUrl,
|
||||
userPermissions,
|
||||
maxAccessLevel: accessLevel,
|
||||
organizationEditPath: editPath,
|
||||
...project
|
||||
}) => ({
|
||||
...project,
|
||||
id: getIdFromGraphQLId(id),
|
||||
name: nameWithNamespace,
|
||||
mergeRequestsAccessLevel: mergeRequestsAccessLevel.stringValue,
|
||||
issuesAccessLevel: issuesAccessLevel.stringValue,
|
||||
forkingAccessLevel: forkingAccessLevel.stringValue,
|
||||
webUrl,
|
||||
isForked: false,
|
||||
accessLevel,
|
||||
editPath,
|
||||
availableActions: availableGraphQLProjectActions(userPermissions),
|
||||
actionLoadingStates: {
|
||||
[ACTION_DELETE]: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
.service-data-payload-container {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
|
@ -252,8 +252,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
}
|
||||
}
|
||||
|
||||
.note-created-ago,
|
||||
.note-updated-at {
|
||||
.note-created-ago {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
|
|
@ -301,11 +300,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
.timeline-entry-inner {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.dummy-avatar {
|
||||
background-color: $gray-100;
|
||||
border: 1px solid darken($gray-100, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
.editing-spinner {
|
||||
|
|
@ -949,10 +943,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
}
|
||||
}
|
||||
|
||||
.note-role {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line note button on the side of diffs
|
||||
*/
|
||||
|
|
@ -1032,11 +1022,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
color: $note-disabled-comment-color;
|
||||
padding: $gl-padding-8 0;
|
||||
|
||||
&.discussion-locked {
|
||||
border: 0;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
a:not(.learn-more) {
|
||||
color: $blue-600;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
- add_page_specific_style 'page_bundles/admin/application_settings_metrics_and_profiling'
|
||||
|
||||
- breadcrumb_title _("Metrics and profiling")
|
||||
- page_title _("Metrics and profiling")
|
||||
- add_page_specific_style 'page_bundles/settings'
|
||||
|
|
|
|||
|
|
@ -289,7 +289,6 @@ module Gitlab
|
|||
config.assets.precompile << "mailers/notify.css"
|
||||
config.assets.precompile << "mailers/notify_enhanced.css"
|
||||
config.assets.precompile << "page_bundles/_mixins_and_variables_and_functions.css"
|
||||
config.assets.precompile << "page_bundles/admin/application_settings_metrics_and_profiling.css"
|
||||
config.assets.precompile << "page_bundles/admin/elasticsearch_form.css"
|
||||
config.assets.precompile << "page_bundles/admin/geo_sites.css"
|
||||
config.assets.precompile << "page_bundles/admin/geo_replicable.css"
|
||||
|
|
|
|||
|
|
@ -481,8 +481,6 @@
|
|||
- 2
|
||||
- - mailers
|
||||
- 2
|
||||
- - members_destroy
|
||||
- 1
|
||||
- - members_destroyer_clean_up_group_protected_branch_rules
|
||||
- 1
|
||||
- - members_expiring_email_notification
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
- title: "Public use of Secure container registries is deprecated"
|
||||
removal_milestone: "18.0"
|
||||
announcement_milestone: "17.4"
|
||||
breaking_change: true
|
||||
reporter: thiagocsf
|
||||
stage: secure
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470641
|
||||
impact: low
|
||||
scope: instance
|
||||
resolution_role: Developer
|
||||
manual_task: true
|
||||
body: | # (required) Don't change this line.
|
||||
Container registries under `registry.gitlab.com/gitlab-org/security-products/`
|
||||
are no longer accessible in GitLab 18.0. [Since GitLab 14.8](https://docs.gitlab.com/ee/update/deprecations.html#secure-and-protect-analyzer-images-published-in-new-location)
|
||||
the correct location is under `registry.gitlab.com/security-products` (note the absence of
|
||||
`gitlab-org` in the address).
|
||||
|
||||
This change improves the security of the release process for GitLab [vulnerability scanners](https://docs.gitlab.com/ee/user/application_security/#vulnerability-scanner-maintenance).
|
||||
|
||||
Users are advised to use the equivalent registry under `registry.gitlab.com/security-products/`,
|
||||
which is the canonical location for GitLab security scanner images. The relevant GitLab CI
|
||||
templates already use this location, so no changes should be necessary for users that use the
|
||||
unmodified templates.
|
||||
|
||||
Offline deployments should review the [specific scanner instructions](https://docs.gitlab.com/ee/user/application_security/offline_deployments/#specific-scanner-instructions)
|
||||
to ensure the correct locations are being used to mirror the required scanner images.
|
||||
tiers: [Free, Premium, Ultimate]
|
||||
documentation_url: https://docs.gitlab.com/ee/user/application_security/index.html#vulnerability-scanner-maintenance
|
||||
|
|
@ -128,7 +128,7 @@ Accessing Git repositories directly is done at your own risk and is not supporte
|
|||
|
||||
The following shows GitLab set up to use direct access to Gitaly:
|
||||
|
||||

|
||||

|
||||
|
||||
In this example:
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ customers.
|
|||
The following shows GitLab set up to access `storage-1`, a virtual storage provided by Gitaly
|
||||
Cluster:
|
||||
|
||||

|
||||

|
||||
|
||||
In this example:
|
||||
|
||||
|
|
@ -456,7 +456,7 @@ Gitaly Cluster consists of multiple components:
|
|||
Praefect is a router and transaction manager for Gitaly, and a required
|
||||
component for running a Gitaly Cluster.
|
||||
|
||||

|
||||

|
||||
|
||||
For more information, see [Gitaly High Availability (HA) Design](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/design_ha.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ test-job1:
|
|||
script:
|
||||
- echo "$BUILD_VERSION" # Output is: 'v1.0.0'
|
||||
dependencies:
|
||||
- build
|
||||
- build-job1
|
||||
|
||||
test-job2:
|
||||
stage: test
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ We make the following assumption with regards to automatically being considered
|
|||
- Team members working on a specific feature (for example, search) are considered domain experts for that feature.
|
||||
|
||||
We default to assigning reviews to team members with domain expertise for code reviews. UX reviews default to the recommended reviewer from the Review Roulette. Due to designer capacity limits, areas not supported by a Product Designer will no longer require a UX review unless it is a community contribution.
|
||||
When a suitable [domain expert](#domain-experts) isn't available, you can choose any team member to review the MR, or follow the [Reviewer roulette](#reviewer-roulette) recommendation (see above for UX reviews).
|
||||
When a suitable [domain expert](#domain-experts) isn't available, you can choose any team member to review the MR, or follow the [Reviewer roulette](#reviewer-roulette) recommendation (see above for UX reviews). Double check if the person is OOO before assigning them.
|
||||
|
||||
To find a domain expert:
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ This saves reviewers time and helps authors catch mistakes earlier.
|
|||
|
||||
Reviewers are responsible for reviewing the specifics of the chosen solution.
|
||||
|
||||
If you are unavailable to review an assigned merge request:
|
||||
If you are unavailable to review an assigned merge request within the [Review-response SLO](https://handbook.gitlab.com/handbook/engineering/workflow/code-review/#review-response-slo):
|
||||
|
||||
1. Inform the author that you're not available.
|
||||
1. Use the [GitLab Review Workload Dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) to select a new reviewer.
|
||||
|
|
|
|||
|
|
@ -458,6 +458,35 @@ The project page will be removed entirely from the group settings in 18.0.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### Public use of Secure container registries is deprecated
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
||||
- Announced in GitLab <span class="milestone">17.4</span>
|
||||
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/470641).
|
||||
|
||||
</div>
|
||||
|
||||
Container registries under `registry.gitlab.com/gitlab-org/security-products/`
|
||||
are no longer accessible in GitLab 18.0. [Since GitLab 14.8](https://docs.gitlab.com/ee/update/deprecations.html#secure-and-protect-analyzer-images-published-in-new-location)
|
||||
the correct location is under `registry.gitlab.com/security-products` (note the absence of
|
||||
`gitlab-org` in the address).
|
||||
|
||||
This change improves the security of the release process for GitLab [vulnerability scanners](https://docs.gitlab.com/ee/user/application_security/#vulnerability-scanner-maintenance).
|
||||
|
||||
Users are advised to use the equivalent registry under `registry.gitlab.com/security-products/`,
|
||||
which is the canonical location for GitLab security scanner images. The relevant GitLab CI
|
||||
templates already use this location, so no changes should be necessary for users that use the
|
||||
unmodified templates.
|
||||
|
||||
Offline deployments should review the [specific scanner instructions](https://docs.gitlab.com/ee/user/application_security/offline_deployments/#specific-scanner-instructions)
|
||||
to ensure the correct locations are being used to mirror the required scanner images.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### Rate limits for common User, Project, and Group API endpoints
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
|
|||
|
|
@ -3095,9 +3095,6 @@ msgstr ""
|
|||
msgid "Active project access tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active tab: %{tab}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Activity"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -5810,6 +5807,9 @@ msgstr ""
|
|||
msgid "An error occurred fetching the public deploy keys. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred loading the projects. Please refresh the page to try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred previewing the blob"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28133,12 +28133,18 @@ msgstr ""
|
|||
msgid "InProductMarketing|%{upper_start}Free 30-day trial%{upper_end} %{lower_start}GitLab Ultimate%{lower_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|%{upper_start}Free 60-day trial%{upper_end} %{lower_start}GitLab Ultimate & GitLab Duo Enterprise%{lower_end} %{last_start}Sign up for a free trial of the most comprehensive AI-powered DevSecOps Platform%{last_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Accelerate your digital transformation"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Blog"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Boost efficiency and collaboration"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Build in security"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28148,6 +28154,9 @@ msgstr ""
|
|||
msgid "InProductMarketing|Deliver software faster"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|End-to-end security and compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Ensure compliance"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28157,6 +28166,9 @@ msgstr ""
|
|||
msgid "InProductMarketing|Free guest users"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|GitLab Duo Enterprise: AI across the software development lifecycle"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|If you don't want to receive marketing emails directly from GitLab, %{marketing_preference_link}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28175,6 +28187,12 @@ msgstr ""
|
|||
msgid "InProductMarketing|No credit card required."
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|One platform for Dev, Sec, and Ops teams"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Ship secure software faster"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Start a Self-Managed trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ gem 'rainbow', '~> 3.1.1'
|
|||
gem 'rspec-parameterized', '~> 1.0.2'
|
||||
gem 'octokit', '~> 9.1.0', require: false
|
||||
gem "faraday-retry", "~> 2.2", ">= 2.2.1"
|
||||
gem 'zeitwerk', '~> 2.6', '>= 2.6.17'
|
||||
gem 'zeitwerk', '~> 2.6', '>= 2.6.18'
|
||||
gem 'influxdb-client', '~> 3.1'
|
||||
gem 'terminal-table', '~> 3.0.2', require: false
|
||||
gem 'slack-notifier', '~> 2.4', require: false
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ GEM
|
|||
wisper (2.0.1)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.17)
|
||||
zeitwerk (2.6.18)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
|
@ -400,7 +400,7 @@ DEPENDENCIES
|
|||
slack-notifier (~> 2.4)
|
||||
terminal-table (~> 3.0.2)
|
||||
warning (~> 1.4)
|
||||
zeitwerk (~> 2.6, >= 2.6.17)
|
||||
zeitwerk (~> 2.6, >= 2.6.18)
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.11
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
|
|||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_content('Projects')
|
||||
expect(page).to have_content('Active tab: Contributed')
|
||||
expect(page).to have_selector('a[aria-selected="true"]', text: 'Contributed')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,12 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller, feature_cate
|
|||
include ApiHelpers
|
||||
include JavaScriptFixturesHelpers
|
||||
|
||||
runners_token = 'runnerstoken:intabulasreferre'
|
||||
|
||||
let(:namespace) { create(:namespace, name: 'frontend-fixtures') }
|
||||
let(:project) do
|
||||
create(
|
||||
:project,
|
||||
namespace: namespace,
|
||||
path: 'builds-project',
|
||||
runners_token: runners_token,
|
||||
avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')
|
||||
)
|
||||
end
|
||||
|
|
@ -28,15 +25,6 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller, feature_cate
|
|||
)
|
||||
end
|
||||
|
||||
let(:project_variable_populated) do
|
||||
create(
|
||||
:project,
|
||||
namespace: namespace,
|
||||
path: 'builds-project2',
|
||||
runners_token: runners_token
|
||||
)
|
||||
end
|
||||
|
||||
let(:user) { project.first_owner }
|
||||
|
||||
render_views
|
||||
|
|
@ -70,28 +58,6 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller, feature_cate
|
|||
end
|
||||
end
|
||||
|
||||
describe GraphQL::Query, type: :request do
|
||||
include GraphqlHelpers
|
||||
|
||||
context 'for access token projects query' do
|
||||
before do
|
||||
project_variable_populated.add_maintainer(user)
|
||||
end
|
||||
|
||||
base_input_path = 'access_tokens/graphql/queries/'
|
||||
base_output_path = 'graphql/projects/access_tokens/'
|
||||
query_name = 'get_projects.query.graphql'
|
||||
|
||||
it "#{base_output_path}#{query_name}.json" do
|
||||
query = get_graphql_query_as_string("#{base_input_path}#{query_name}")
|
||||
|
||||
post_graphql(query, current_user: user, variables: { search: '', first: 2 })
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Storage', feature_category: :consumables_cost_management do
|
||||
describe GraphQL::Query, type: :request do
|
||||
include GraphqlHelpers
|
||||
|
|
@ -152,3 +118,68 @@ RSpec.describe API::Projects, '(JavaScript fixtures)', type: :request, feature_c
|
|||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.describe GraphQL::Query, type: :request, feature_category: :groups_and_projects do
|
||||
include JavaScriptFixturesHelpers
|
||||
include GraphqlHelpers
|
||||
|
||||
runners_token = 'runnerstoken:intabulasreferre'
|
||||
|
||||
let_it_be(:project_variable_populated) do
|
||||
create(
|
||||
:project,
|
||||
runners_token: runners_token
|
||||
)
|
||||
end
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:project2) { create(:project) }
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'for access token projects query' do
|
||||
before_all do
|
||||
project_variable_populated.add_maintainer(user)
|
||||
end
|
||||
|
||||
base_input_path = 'access_tokens/graphql/queries/'
|
||||
base_output_path = 'graphql/projects/access_tokens/'
|
||||
query_name = 'get_projects.query.graphql'
|
||||
|
||||
it "#{base_output_path}#{query_name}.json" do
|
||||
query = get_graphql_query_as_string("#{base_input_path}#{query_name}")
|
||||
|
||||
post_graphql(query, current_user: user, variables: { search: '', first: 2 })
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'for your work -> projects -> contributed' do
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
project2.add_maintainer(user)
|
||||
end
|
||||
|
||||
before do
|
||||
create(:push_event, project: project, author: user)
|
||||
create(:push_event, project: project2, author: user)
|
||||
end
|
||||
|
||||
base_input_path = 'projects/your_work/graphql/queries/'
|
||||
base_output_path = 'graphql/projects/your_work/'
|
||||
query_name = 'contributed_projects.query.graphql'
|
||||
|
||||
it "#{base_output_path}#{query_name}.json" do
|
||||
query = get_graphql_query_as_string("#{base_input_path}#{query_name}")
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,12 +7,9 @@ import { SORT_DIRECTION_ASC, SORT_ITEM_NAME } from '~/organizations/shared/const
|
|||
import NewProjectButton from '~/organizations/shared/components/new_project_button.vue';
|
||||
import GroupsAndProjectsEmptyState from '~/organizations/shared/components/groups_and_projects_empty_state.vue';
|
||||
import projectsQuery from '~/organizations/shared/graphql/queries/projects.query.graphql';
|
||||
import {
|
||||
renderDeleteSuccessToast,
|
||||
deleteParams,
|
||||
formatProjects,
|
||||
} from 'ee_else_ce/organizations/shared/utils';
|
||||
import { renderDeleteSuccessToast, deleteParams } from 'ee_else_ce/organizations/shared/utils';
|
||||
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import { ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
import { TIMESTAMP_TYPE_CREATED_AT } from '~/vue_shared/components/resource_lists/constants';
|
||||
import { createAlert } from '~/alert';
|
||||
|
|
@ -185,7 +182,7 @@ describe('ProjectsView', () => {
|
|||
await waitForPromises();
|
||||
|
||||
expect(findProjectsList().props()).toMatchObject({
|
||||
projects: formatProjects(nodes),
|
||||
projects: formatGraphQLProjects(nodes),
|
||||
showProjectIcon: true,
|
||||
listItemClass: defaultPropsData.listItemClass,
|
||||
timestampType: TIMESTAMP_TYPE_CREATED_AT,
|
||||
|
|
@ -330,7 +327,7 @@ describe('ProjectsView', () => {
|
|||
});
|
||||
|
||||
describe('Deleting project', () => {
|
||||
const MOCK_PROJECT = formatProjects(nodes)[0];
|
||||
const MOCK_PROJECT = formatGraphQLProjects(nodes)[0];
|
||||
|
||||
describe('when API call is successful', () => {
|
||||
beforeEach(async () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import organizationGroupsGraphQlResponse from 'test_fixtures/graphql/organizations/groups.query.graphql.json';
|
||||
import organizationProjectsGraphQlResponse from 'test_fixtures/graphql/organizations/projects.query.graphql.json';
|
||||
import {
|
||||
formatProjects,
|
||||
formatGroups,
|
||||
onPageChange,
|
||||
deleteParams,
|
||||
|
|
@ -11,6 +10,7 @@ import {
|
|||
import { SORT_CREATED_AT, SORT_UPDATED_AT, SORT_NAME } from '~/organizations/shared/constants';
|
||||
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import toast from '~/vue_shared/plugins/global_toast';
|
||||
import {
|
||||
TIMESTAMP_TYPE_CREATED_AT,
|
||||
|
|
@ -35,47 +35,6 @@ const {
|
|||
},
|
||||
} = organizationProjectsGraphQlResponse;
|
||||
|
||||
describe('formatProjects', () => {
|
||||
it('correctly formats the projects', () => {
|
||||
const [firstMockProject] = organizationProjects;
|
||||
const formattedProjects = formatProjects(organizationProjects);
|
||||
const [firstFormattedProject] = formattedProjects;
|
||||
|
||||
expect(firstFormattedProject).toMatchObject({
|
||||
id: getIdFromGraphQLId(firstMockProject.id),
|
||||
name: firstMockProject.nameWithNamespace,
|
||||
mergeRequestsAccessLevel: firstMockProject.mergeRequestsAccessLevel.stringValue,
|
||||
issuesAccessLevel: firstMockProject.issuesAccessLevel.stringValue,
|
||||
forkingAccessLevel: firstMockProject.forkingAccessLevel.stringValue,
|
||||
accessLevel: {
|
||||
integerValue: 50,
|
||||
},
|
||||
availableActions: [ACTION_EDIT, ACTION_DELETE],
|
||||
actionLoadingStates: {
|
||||
[ACTION_DELETE]: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(formattedProjects.length).toBe(organizationProjects.length);
|
||||
});
|
||||
|
||||
describe('when project does not have delete permissions', () => {
|
||||
const nonDeletableFormattedProject = formatProjects(organizationProjects)[1];
|
||||
|
||||
it('does not include delete action in `availableActions`', () => {
|
||||
expect(nonDeletableFormattedProject.availableActions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when project does not have edit permissions', () => {
|
||||
const nonEditableFormattedProject = formatProjects(organizationProjects)[1];
|
||||
|
||||
it('does not include edit action in `availableActions`', () => {
|
||||
expect(nonEditableFormattedProject.availableActions).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatGroups', () => {
|
||||
it('correctly formats the groups with edit and delete permissions', () => {
|
||||
const [firstMockGroup] = organizationGroups;
|
||||
|
|
@ -150,7 +109,7 @@ describe('onPageChange', () => {
|
|||
});
|
||||
|
||||
describe('renderDeleteSuccessToast', () => {
|
||||
const [MOCK_PROJECT] = formatProjects(organizationProjects);
|
||||
const [MOCK_PROJECT] = formatGraphQLProjects(organizationProjects);
|
||||
const MOCK_TYPE = 'Project';
|
||||
|
||||
it('calls toast correctly', () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import VueRouter from 'vue-router';
|
|||
import { GlTabs } from '@gitlab/ui';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import YourWorkProjectsApp from '~/projects/your_work/components/app.vue';
|
||||
import TabView from '~/projects/your_work/components/tab_view.vue';
|
||||
import { createRouter } from '~/projects/your_work';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import {
|
||||
ROOT_ROUTE_NAME,
|
||||
DASHBOARD_ROUTE_NAME,
|
||||
|
|
@ -31,13 +33,16 @@ describe('YourWorkProjectsApp', () => {
|
|||
|
||||
wrapper = mountExtended(YourWorkProjectsApp, {
|
||||
router,
|
||||
stubs: {
|
||||
TabView: stubComponent(TabView),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findPageTitle = () => wrapper.find('h1');
|
||||
const findGlTabs = () => wrapper.findComponent(GlTabs);
|
||||
const findAllTabTitles = () => wrapper.findAllByTestId('projects-dashboard-tab-title');
|
||||
const findActiveTab = () => wrapper.find('.tab-pane.active');
|
||||
const findActiveTab = () => wrapper.findByRole('tab', { selected: true });
|
||||
|
||||
afterEach(() => {
|
||||
router = null;
|
||||
|
|
@ -81,6 +86,12 @@ describe('YourWorkProjectsApp', () => {
|
|||
it('initializes to the correct tab', () => {
|
||||
expect(findActiveTab().text()).toContain(expectedTab.text);
|
||||
});
|
||||
|
||||
if (expectedTab.query) {
|
||||
it('renders `TabView` component and passes `tab` prop', () => {
|
||||
expect(wrapper.findComponent(TabView).props('tab')).toMatchObject(expectedTab);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('onTabUpdate', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import Vue from 'vue';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import contributedProjectsGraphQlResponse from 'test_fixtures/graphql/projects/your_work/contributed_projects.query.graphql.json';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import TabView from '~/projects/your_work/components/tab_view.vue';
|
||||
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
|
||||
import contributedProjectsQuery from '~/projects/your_work/graphql/queries/contributed_projects.query.graphql';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import { createAlert } from '~/alert';
|
||||
import { CONTRIBUTED_TAB } from 'ee_else_ce/projects/your_work/constants';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
describe('TabView', () => {
|
||||
let wrapper;
|
||||
let mockApollo;
|
||||
|
||||
const createComponent = ({ handler, propsData }) => {
|
||||
mockApollo = createMockApollo([handler]);
|
||||
|
||||
wrapper = mountExtended(TabView, {
|
||||
apolloProvider: mockApollo,
|
||||
propsData,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
mockApollo = null;
|
||||
});
|
||||
|
||||
describe.each`
|
||||
tab | handler | expectedProjects
|
||||
${CONTRIBUTED_TAB} | ${[contributedProjectsQuery, jest.fn().mockResolvedValue(contributedProjectsGraphQlResponse)]} | ${contributedProjectsGraphQlResponse.data.currentUser.contributedProjects.nodes}
|
||||
`('onMount when route name is $tab.value', ({ tab, handler, expectedProjects }) => {
|
||||
describe('when GraphQL request is loading', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ handler, propsData: { tab } });
|
||||
});
|
||||
|
||||
it('shows loading icon', () => {
|
||||
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when GraphQL request is successful', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ handler, propsData: { tab } });
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('passes projects to `ProjectsList` component', () => {
|
||||
expect(wrapper.findComponent(ProjectsList).props('projects')).toEqual(
|
||||
formatGraphQLProjects(expectedProjects),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when GraphQL request is not successful', () => {
|
||||
const error = new Error();
|
||||
|
||||
beforeEach(async () => {
|
||||
createComponent({
|
||||
handler: [handler[0], jest.fn().mockRejectedValue(error)],
|
||||
propsData: { tab },
|
||||
});
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays error alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred loading the projects. Please refresh the page to try again.',
|
||||
error,
|
||||
captureError: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import projectsGraphQLResponse from 'test_fixtures/graphql/organizations/projects.query.graphql.json';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/utils';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
|
||||
const {
|
||||
data: {
|
||||
organization: {
|
||||
projects: { nodes: projects },
|
||||
},
|
||||
},
|
||||
} = projectsGraphQLResponse;
|
||||
|
||||
describe('formatGraphQLProjects', () => {
|
||||
it('correctly formats the projects', () => {
|
||||
const [firstMockProject] = projects;
|
||||
const formattedProjects = formatGraphQLProjects(projects);
|
||||
const [firstFormattedProject] = formattedProjects;
|
||||
|
||||
expect(firstFormattedProject).toMatchObject({
|
||||
id: getIdFromGraphQLId(firstMockProject.id),
|
||||
name: firstMockProject.nameWithNamespace,
|
||||
mergeRequestsAccessLevel: firstMockProject.mergeRequestsAccessLevel.stringValue,
|
||||
issuesAccessLevel: firstMockProject.issuesAccessLevel.stringValue,
|
||||
forkingAccessLevel: firstMockProject.forkingAccessLevel.stringValue,
|
||||
accessLevel: {
|
||||
integerValue: 50,
|
||||
},
|
||||
availableActions: [ACTION_EDIT, ACTION_DELETE],
|
||||
actionLoadingStates: {
|
||||
[ACTION_DELETE]: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(formattedProjects.length).toBe(projects.length);
|
||||
});
|
||||
|
||||
describe('when project does not have delete permissions', () => {
|
||||
const nonDeletableFormattedProject = formatGraphQLProjects(projects)[1];
|
||||
|
||||
it('does not include delete action in `availableActions`', () => {
|
||||
expect(nonDeletableFormattedProject.availableActions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when project does not have edit permissions', () => {
|
||||
const nonEditableFormattedProject = formatGraphQLProjects(projects)[1];
|
||||
|
||||
it('does not include edit action in `availableActions`', () => {
|
||||
expect(nonEditableFormattedProject.availableActions).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -4,7 +4,6 @@ import Vue, { nextTick } from 'vue';
|
|||
import VueApollo from 'vue-apollo';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
|
||||
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
|
||||
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
|
||||
|
|
@ -47,10 +46,13 @@ describe('WorkItemDetailModal component', () => {
|
|||
},
|
||||
provide: {
|
||||
fullPath: 'group/project',
|
||||
reportAbusePath: 'report/abuse',
|
||||
groupPath: '',
|
||||
hasSubepicsFeature: false,
|
||||
},
|
||||
stubs: {
|
||||
GlModal,
|
||||
WorkItemDetail: stubComponent(WorkItemDetail),
|
||||
WorkItemDetail,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,15 +34,13 @@ describe('WorkItemPrefetch component', () => {
|
|||
},
|
||||
scopedSlots: {
|
||||
default: `
|
||||
<template #default="{ prefetchWorkItem, clearPrefetching }">
|
||||
<span
|
||||
@mouseover="prefetchWorkItem"
|
||||
@mouseleave="clearPrefetching"
|
||||
data-testid="prefetch-trigger"
|
||||
>
|
||||
Hover item
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
@mouseover="props.prefetchWorkItem"
|
||||
@mouseleave="props.clearPrefetching"
|
||||
data-testid="prefetch-trigger"
|
||||
>
|
||||
Hover item
|
||||
</span>
|
||||
`,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue