diff --git a/app/assets/javascripts/work_items/components/work_item_loading.vue b/app/assets/javascripts/work_items/components/work_item_loading.vue
new file mode 100644
index 00000000000..23e24daf445
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_loading.vue
@@ -0,0 +1,135 @@
+
+
+
diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue
index 4b6e77c6f18..a8370b74610 100644
--- a/app/assets/javascripts/work_items/components/work_item_notes.vue
+++ b/app/assets/javascripts/work_items/components/work_item_notes.vue
@@ -349,14 +349,14 @@ export default {
/>
diff --git a/app/assets/stylesheets/page_bundles/projects.scss b/app/assets/stylesheets/page_bundles/projects.scss
index fd65485f2be..e178db79f89 100644
--- a/app/assets/stylesheets/page_bundles/projects.scss
+++ b/app/assets/stylesheets/page_bundles/projects.scss
@@ -392,10 +392,6 @@
margin-left: $gl-spacing-scale-3;
margin-right: 0;
}
-
- .user-access-role {
- line-height: $gl-line-height-14;
- }
}
@include media-breakpoint-down(md) {
@@ -476,7 +472,7 @@
.form-control {
min-width: 100px;
}
-
+
&.form-group {
@include media-breakpoint-up(sm) {
margin-bottom: 0;
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 9efe14b83a5..8aaed1e04af 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -80,18 +80,6 @@ table.pipeline-project-metrics tr td {
}
}
-.user-access-role {
- padding-left: $gl-spacing-scale-3;
- padding-right: $gl-spacing-scale-3;
- display: inline-block;
- color: $gl-text-color-secondary;
- font-size: 12px;
- line-height: 20px;
- border: 1px solid $border-color;
- border-radius: $label-border-radius;
- font-weight: $gl-font-weight-normal;
-}
-
.groups-list-tree-container {
> .group-list-tree > .group-row.has-children:first-child {
border-top: 0;
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 7daaf340580..f63d53fec30 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -120,8 +120,11 @@ module Auth
end
end
- # Return the project path (lowercase) as metadata
- { project_path: project&.full_path&.downcase }
+ {
+ project_path: project&.full_path&.downcase,
+ project_id: project&.id,
+ root_namespace_id: project&.root_ancestor&.id
+ }
end
private
diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml
index bf7a6c8ebd4..e4ab1bafdf2 100644
--- a/app/views/projects/notes/_actions.html.haml
+++ b/app/views/projects/notes/_actions.html.haml
@@ -1,10 +1,10 @@
- access = note_human_max_access(note)
- if note.noteable_author?(@noteable)
- %span{ class: 'note-role user-access-role has-tooltip d-none d-md-inline-block', title: _("This user is the author of this %{noteable}.") % { noteable: @noteable.human_class_name } }= _("Author")
+ = render Pajamas::BadgeComponent.new(_("Author"), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip gl-display-none gl-md-display-inline-block', title: _("This user is the author of this %{noteable}.") % { noteable: @noteable.human_class_name })
- if access
- %span{ class: 'note-role user-access-role has-tooltip', title: _("This user has the %{access} role in the %{name} project.") % { access: access.downcase, name: note.project_name } }= access
+ = render Pajamas::BadgeComponent.new(access, variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip', title: _("This user has the %{access} role in the %{name} project.") % { access: access.downcase, name: note.project_name })
- elsif note.contributor?
- %span{ class: 'note-role user-access-role has-tooltip', title: _("This user has previously committed to the %{name} project.") % { name: note.project_name } }= _("Contributor")
+ = render Pajamas::BadgeComponent.new(_("Contributor"), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip', title: _("This user has previously committed to the %{name} project.") % { name: note.project_name })
- if can?(current_user, :award_emoji, note)
- if note.emoji_awardable?
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index 6e1ebdeedf0..db21c20056a 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -4,5 +4,5 @@
%h1.page-title.gl-font-size-h-display
= _("Edit Snippet")
-%hr
+
= render "shared/snippets/form", url: project_snippet_path(@project, @snippet)
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index cdcbee1bb72..24d50341780 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -9,7 +9,7 @@
= link_to group.full_name, group, class: 'group-name'
- if access&.nonzero?
- %span.user-access-role= Gitlab::Access.human_access(access)
+ = render Pajamas::BadgeComponent.new(Gitlab::Access.human_access(access), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100!')
- if group.description.present?
.description
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 37ec6ec2bb4..408b512bc1a 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -61,4 +61,4 @@
- if show_roles
.controls.member-controls.gl-align-items-center
= render_if_exists 'shared/members/ee/ldap_tag', can_override: member.can_override?
- %span.member-access-text.user-access-role= member.human_access
+ = render Pajamas::BadgeComponent.new(member.human_access, variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100!')
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 4024e72d7a2..56484cb9604 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -50,7 +50,7 @@
- if !explore_projects_tab? && access&.nonzero?
-# haml-lint:disable UnnecessaryStringOutput
= ' ' # prevent haml from eating the space between elements
- %span.user-access-role.gl-display-block.gl-m-0{ data: { testid: 'user-role-content' } }= localized_project_human_access(access)
+ = render Pajamas::BadgeComponent.new(localized_project_human_access(access), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100!', data: { testid: 'user-access-role' })
- if !explore_projects_tab?
= render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: project, additional_classes: 'gl-ml-2!'
diff --git a/data/deprecations/16-10-linux-hosted-runners-breaking-changes.yml b/data/deprecations/16-10-linux-hosted-runners-breaking-changes.yml
new file mode 100644
index 00000000000..227a411c097
--- /dev/null
+++ b/data/deprecations/16-10-linux-hosted-runners-breaking-changes.yml
@@ -0,0 +1,67 @@
+# Use this template to announce a feature deprecation or other
+# important planned changes at least three releases prior to removal.
+# Breaking changes must happen in a major release.
+#
+# See the deprecation guidelines to confirm your understanding of GitLab's definitions:
+# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology
+#
+# If an End of Support period applies, see the OPTIONAL section below.
+#
+# For more information, see the handbook:
+# https://handbook.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-and-other-planned-breaking-change-announcements
+
+# ===================
+# REQUIRED FIELDS
+# ===================
+
+# ----- DELETE EVERYTHING ABOVE THIS LINE -----
+
+- title: "Hosted Runners on Linux operating system upgrade"
+ # The milestones for the deprecation announcement, and the removal.
+ removal_milestone: "17.0"
+ announcement_milestone: "16.10"
+ # Change breaking_change to false if needed.
+ breaking_change: true
+ # The stage and GitLab username of the person reporting the change,
+ # and a link to the deprecation issue
+ reporter: tmaczukin
+ stage: ci
+ issue_url: https://gitlab.com/gitlab-org/ci-cd/shared-runners/infrastructure/-/issues/60
+ impact: low # Can be one of: [critical, high, medium, low]
+ scope: instance # Can be one or a combination of: [instance, group, project]
+ resolution_role: Developer # Can be one of: [Admin, Owner, Maintainer, Developer]
+ manual_task: true # Can be true or false. Use this to denote whether a resolution action must be performed manually (true), or if it can be automated by using the API or other automation (false).
+ body: | # (required) Don't change this line.
+ With GitLab 17.0 we're upgrading the container-optimized operating system ([COS](https://cloud.google.com/container-optimized-os/docs))
+ of the ephemeral VMs used to execute jobs for [Hosted Runners on Linux](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html).
+ The COS upgrade includes a Docker Engine upgrade from Version 19.03.15 to Version 23.0.5, which introduced
+ a known compatibility issue.
+
+ GitLab CI/CD jobs [using Docker-in-Docker-based jobs](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker)
+ with a Docker-in-Docker service version prior to 20.10 and GitLab CI/CD jobs [using Kaniko to build container images](https://docs.gitlab.com/ee/ci/docker/using_kaniko.html)
+ with a Kaniko service version older than `v1.9.0` will start failing.
+
+ To fix these issues, an update of service version in `.gitlab-ci.yml` is required.
+
+ Both issues, including a detailed explanation of how they affect jobs and how to fix
+ the issue, are described
+ [in the announcement blog post](https://about.gitlab.com/blog/2023/10/04/updating-the-os-version-of-saas-runners-on-linux/).
+
+# ==============================
+# OPTIONAL END-OF-SUPPORT FIELDS
+# ==============================
+#
+# If an End of Support period applies:
+# 1) Share this announcement in the `#spt_managers` Support channel in Slack
+# 2) Mention `@gitlab-com/support` in this merge request.
+#
+ # When support for this feature ends, in XX.YY milestone format.
+ end_of_support_milestone:
+ # Array of tiers the feature is currently available to,
+ # like [Free, Silver, Gold, Core, Premium, Ultimate]
+ tiers:
+ # Links to documentation and thumbnail image
+ documentation_url:
+ image_url:
+ # Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ video_url:
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index ed023fc85c7..b87068fd8c7 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -1241,6 +1241,33 @@ set `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` to `heroku/builder:20`.
+### Hosted Runners on Linux operating system upgrade
+
+
+- Announced in GitLab 16.10
+- Removal in GitLab 17.0 ([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/ci-cd/shared-runners/infrastructure/-/issues/60).
+
+
+With GitLab 17.0 we're upgrading the container-optimized operating system ([COS](https://cloud.google.com/container-optimized-os/docs))
+of the ephemeral VMs used to execute jobs for [Hosted Runners on Linux](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html).
+The COS upgrade includes a Docker Engine upgrade from Version 19.03.15 to Version 23.0.5, which introduced
+a known compatibility issue.
+
+GitLab CI/CD jobs [using Docker-in-Docker-based jobs](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker)
+with a Docker-in-Docker service version prior to 20.10 and GitLab CI/CD jobs [using Kaniko to build container images](https://docs.gitlab.com/ee/ci/docker/using_kaniko.html)
+with a Kaniko service version older than `v1.9.0` will start failing.
+
+To fix these issues, an update of service version in `.gitlab-ci.yml` is required.
+
+Both issues, including a detailed explanation of how they affect jobs and how to fix
+the issue, are described
+[in the announcement blog post](https://about.gitlab.com/blog/2023/10/04/updating-the-os-version-of-saas-runners-on-linux/).
+
+
+
+
+
### Internal container registry API tag deletion endpoint
diff --git a/doc/user/project/integrations/google_artifact_registry.md b/doc/user/project/integrations/google_artifact_registry.md
index 3869b4570a1..0601ab5ebb5 100644
--- a/doc/user/project/integrations/google_artifact_registry.md
+++ b/doc/user/project/integrations/google_artifact_registry.md
@@ -165,10 +165,26 @@ pull-image:
- docker pull $GOOGLE_ARTIFACT_REGISTRY_REPOSITORY_LOCATION-docker.pkg.dev/$GOOGLE_ARTIFACT_REGISTRY_PROJECT_ID/$GOOGLE_ARTIFACT_REGISTRY_REPOSITORY_NAME/app:v0.1.0
```
-### Copy an image from the GitLab Container Registry with Docker
+#### Copy an image by using a CI/CD component
-The following example shows how to replicate an image from the GitLab Container Registry to the Google Artifact Registry.
-To do this, pull the image from GitLab, re-tag it, and then push to the Google Artifact Registry.
+Google provides the [`upload-artifact-registry`](https://gitlab.com/explore/catalog/google-gitlab-components/artifact-registry) CI/CD component, which you can use to copy an image from the GitLab container registry to Artifact Registry.
+
+To use the `upload-artifact-registry` component, add the following to your `.gitlab-ci.yml`:
+
+```yaml
+include:
+ - component: gitlab.com/google-gitlab-components/artifact-registry/upload-artifact-registry@
+ inputs:
+ stage: deploy
+ source: $CI_REGISTRY_IMAGE:v0.1.0
+ target: $GOOGLE_ARTIFACT_REGISTRY_REPOSITORY_LOCATION-docker.pkg.dev/$GOOGLE_ARTIFACT_REGISTRY_PROJECT_ID/$GOOGLE_ARTIFACT_REGISTRY_REPOSITORY_NAME/app:v0.1.0
+```
+
+For details, see [the component documentation](https://gitlab.com/explore/catalog/google-gitlab-components/artifact-registry).
+
+Using the `upload-artifact-registry` component simplifies copying images to Artifact Registry and is the intended method for this integration. If you want to use Docker or Crane, see the following examples.
+
+#### Copy an image by using Docker
In the following example, the `gcloud` CLI is used to set up the Docker authentication, as an alternative to the [standalone Docker credential helper](https://cloud.google.com/artifact-registry/docs/docker/authentication#standalone-helper).
@@ -191,7 +207,7 @@ copy-image:
- docker push $TARGET_IMAGE
```
-### Copy an image from the GitLab Container Registry with crane
+#### Copy an image by using Crane
```yaml
copy-image:
diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb
index 32e3af117fc..437877b5e63 100644
--- a/qa/qa/page/dashboard/projects.rb
+++ b/qa/qa/page/dashboard/projects.rb
@@ -10,7 +10,7 @@ module QA
view 'app/views/shared/projects/_project.html.haml' do
element 'project-content'
- element 'user-role-content'
+ element 'user-access-role'
end
view 'app/views/dashboard/_projects_head.html.haml' do
@@ -62,7 +62,7 @@ module QA
def has_project_with_access_role?(project_name, access_role)
within_element('project-content', text: project_name) do
- has_element?('user-role-content', text: access_role)
+ has_element?('user-access-role', text: access_role)
end
end
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index bd878c5e3ab..3c02171bf43 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
it 'shows role badge' do
visit dashboard_projects_path
- page.within '.user-access-role' do
+ within_testid('user-access-role') do
expect(page).to have_content('Developer')
end
end
@@ -39,7 +39,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
it 'displays the right role' do
visit dashboard_projects_path
- page.within '.user-access-role' do
+ within_testid('user-access-role') do
expect(page).to have_content('Developer')
end
@@ -47,7 +47,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
visit dashboard_projects_path
- page.within '.user-access-role' do
+ within_testid('user-access-role') do
expect(page).to have_content('Maintainer')
end
end
diff --git a/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap
index b6299a9a4f2..686227badd3 100644
--- a/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap
+++ b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap
@@ -1,12 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SidebarTodo template renders component container element with proper data attributes 1`] = `
-
+
`;
diff --git a/spec/frontend/sidebar/components/todo_toggle/todo_spec.js b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js
index 6b096090dcf..de7946fdb60 100644
--- a/spec/frontend/sidebar/components/todo_toggle/todo_spec.js
+++ b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js
@@ -1,4 +1,4 @@
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
+import { GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
@@ -20,14 +20,15 @@ describe('SidebarTodo', () => {
},
});
};
+ const findButton = () => wrapper.findComponent(GlButton);
it.each`
state | classes
- ${false} | ${['gl-button', 'btn', 'btn-default', 'btn-todo', 'issuable-header-btn', 'gl-float-right']}
- ${true} | ${['btn-blank', 'btn-todo', 'sidebar-collapsed-icon', 'js-dont-change-state']}
+ ${false} | ${['issuable-header-btn', 'gl-float-right']}
+ ${true} | ${['sidebar-collapsed-icon', 'js-dont-change-state']}
`('returns todo button classes for when `collapsed` prop is `$state`', ({ state, classes }) => {
createComponent({ collapsed: state });
- expect(wrapper.find('button').classes()).toStrictEqual(classes);
+ expect(findButton().classes()).toStrictEqual(classes);
});
it.each`
@@ -41,14 +42,14 @@ describe('SidebarTodo', () => {
expect(wrapper.findComponent(GlIcon).classes().join(' ')).toStrictEqual(iconClass);
expect(wrapper.findComponent(GlIcon).props('name')).toStrictEqual(icon);
- expect(wrapper.find('button').text()).toBe(label);
+ expect(findButton().text()).toBe(label);
},
);
describe('template', () => {
it('emits `toggleTodo` event when clicked on button', async () => {
createComponent();
- wrapper.find('button').trigger('click');
+ findButton().vm.$emit('click');
await nextTick();
expect(wrapper.emitted().toggleTodo).toHaveLength(1);
diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
index fac3232842d..5519c7597c2 100644
--- a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
+++ b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
@@ -32,18 +32,18 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
valuefield="value"
>
@@ -52,18 +52,18 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
@@ -72,18 +72,18 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
@@ -94,7 +94,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 95664ebf3cd..46c29ff237a 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlSkeletonLoader, GlEmptyState } from '@gitlab/ui';
+import { GlAlert, GlEmptyState } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -7,6 +7,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import setWindowLocation from 'helpers/set_window_location_helper';
import { stubComponent } from 'helpers/stub_component';
+import WorkItemLoading from '~/work_items/components/work_item_loading.vue';
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
import WorkItemAncestors from '~/work_items/components/work_item_ancestors/work_item_ancestors.vue';
@@ -72,7 +73,7 @@ describe('WorkItemDetail component', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
- const findSkeleton = () => wrapper.findComponent(GlSkeletonLoader);
+ const findWorkItemLoading = () => wrapper.findComponent(WorkItemLoading);
const findWorkItemActions = () => wrapper.findComponent(WorkItemActions);
const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle);
const findCreatedUpdated = () => wrapper.findComponent(WorkItemCreatedUpdated);
@@ -181,7 +182,7 @@ describe('WorkItemDetail component', () => {
});
it('renders skeleton loader', () => {
- expect(findSkeleton().exists()).toBe(true);
+ expect(findWorkItemLoading().exists()).toBe(true);
expect(findWorkItemTitle().exists()).toBe(false);
});
});
@@ -193,7 +194,7 @@ describe('WorkItemDetail component', () => {
});
it('does not render skeleton', () => {
- expect(findSkeleton().exists()).toBe(false);
+ expect(findWorkItemLoading().exists()).toBe(false);
expect(findWorkItemTitle().exists()).toBe(true);
});
diff --git a/spec/frontend/work_items/components/work_item_loading_spec.js b/spec/frontend/work_items/components/work_item_loading_spec.js
new file mode 100644
index 00000000000..4da372f6eb9
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_loading_spec.js
@@ -0,0 +1,73 @@
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import WorkItemLoading from '~/work_items/components/work_item_loading.vue';
+
+describe('Work Item Loading spec', () => {
+ let wrapper;
+
+ const findWorkItemTwoColumnLoading = () => wrapper.findByTestId('work-item-two-column-loading');
+ const findWorkItemSingleColumnLoading = () =>
+ wrapper.findByTestId('work-item-single-column-loading');
+ const findWorkItemTitleMetaLoading = () => wrapper.findByTestId('work-title-and-meta-loading');
+ const findWorkItemDescriptionLoading = () =>
+ wrapper.findByTestId('work-item-description-loading');
+ const findWorkItemAttributesXsSmLoading = () =>
+ wrapper.findByTestId('work-item-attributes-xssm-loading');
+ const findWorkItemAttributesMdUpLoading = () =>
+ wrapper.findByTestId('work-item-attributes-mdup-loading');
+ const findWorkItemActivityPlaceholder = () =>
+ wrapper.findByTestId('work-item-activity-placeholder-loading');
+ const findWorkItemNotesLoading = () => wrapper.findByTestId('work-item-notes-loading');
+ const findLoaders = () => findWorkItemAttributesXsSmLoading().findAllComponents(GlSkeletonLoader);
+
+ const createComponent = ({ twoColumnView = false } = {}) => {
+ wrapper = shallowMountExtended(WorkItemLoading, {
+ propsData: {
+ twoColumnView,
+ },
+ });
+ };
+
+ describe('Work Item Single Column loading view', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the single column loading', () => {
+ expect(findWorkItemSingleColumnLoading().exists()).toBe(true);
+ expect(findWorkItemTwoColumnLoading().exists()).toBe(false);
+ });
+ });
+
+ describe('Work Item Two Column loading view', () => {
+ beforeEach(() => {
+ createComponent({ twoColumnView: true });
+ });
+
+ it('renders the two column loading', () => {
+ expect(findWorkItemTwoColumnLoading().exists()).toBe(true);
+ });
+
+ it('renders the title and meta loading skeleton', () => {
+ expect(findWorkItemTitleMetaLoading().exists()).toBe(true);
+ });
+
+ it('renders the description loading skeleton', () => {
+ expect(findWorkItemDescriptionLoading().exists()).toBe(true);
+ });
+
+ it('renders the attributes loading skeleton', () => {
+ expect(findWorkItemAttributesXsSmLoading().exists()).toBe(true);
+ expect(findWorkItemAttributesMdUpLoading().exists()).toBe(true);
+ expect(findLoaders()).toHaveLength(WorkItemLoading.loader.attributesRepeat);
+ });
+
+ it('renders the activity placeholder loading skeleton', () => {
+ expect(findWorkItemActivityPlaceholder().exists()).toBe(true);
+ });
+
+ it('renders the notes loading skeleton', () => {
+ expect(findWorkItemNotesLoading().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/support/shared_examples/features/wiki/autocomplete_shared_examples.rb b/spec/support/shared_examples/features/wiki/autocomplete_shared_examples.rb
index 79de2aedf3b..64308e3114e 100644
--- a/spec/support/shared_examples/features/wiki/autocomplete_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/autocomplete_shared_examples.rb
@@ -9,6 +9,7 @@ RSpec.shared_examples 'autocompletes items' do
create(:milestone, project: project, title: 'My Cool Milestone')
project.add_maintainer(create(:user, name: 'JohnDoe123'))
+ project.add_maintainer(create(:user, name: 'ReallyLongUsername1234567890'))
else # group wikis
project = create(:project, group: group)
@@ -18,6 +19,7 @@ RSpec.shared_examples 'autocompletes items' do
create(:milestone, group: group, title: 'My Cool Milestone')
project.add_maintainer(create(:user, name: 'JohnDoe123'))
+ project.add_maintainer(create(:user, name: 'ReallyLongUsername1234567890'))
end
end
@@ -40,4 +42,10 @@ RSpec.shared_examples 'autocompletes items' do
fill_in :wiki_content, with: ':smil'
expect(page).to have_text 'smile_cat'
end
+
+ it 'autocompletes items with long names' do
+ fill_in :wiki_content, with: "@ReallyLongUsername1234567"
+
+ expect(page).to have_text 'ReallyLongUsername1234567890'
+ end
end
diff --git a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
index 152219b5ab9..6f23f9bdfa5 100644
--- a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
@@ -176,6 +176,7 @@ RSpec.shared_examples 'User creates wiki page' do
end
it_behaves_like 'wiki file attachments'
+ it_behaves_like 'autocompletes items'
end
context "when wiki is not empty", :js do
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
index a76a792002c..934b496b822 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -505,6 +505,9 @@ RSpec.shared_examples 'work items description' do
expect(page).to have_text(expected_warning)
+ page.find('summary', text: 'View current version').click
+ expect(find_by_testid('conflicted-description').value).to eq('oh no!')
+
click_button s_('WorkItem|Save and overwrite')
expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 2b365e0e3d4..99372787f4c 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -86,7 +86,11 @@ RSpec.shared_examples 'an accessible' do
[{ 'type' => 'repository',
'name' => project.full_path,
'actions' => actions,
- 'meta' => { 'project_path' => project.full_path } }]
+ 'meta' => {
+ 'project_path' => project.full_path,
+ 'project_id' => project.id,
+ 'root_namespace_id' => project.root_ancestor.id
+ } }]
end
it_behaves_like 'a valid token'
@@ -252,13 +256,21 @@ RSpec.shared_examples 'a container registry auth service' do
'type' => 'repository',
'name' => project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => project.full_path }
+ 'meta' => {
+ 'project_path' => project.full_path,
+ 'project_id' => project.id,
+ 'root_namespace_id' => project.root_ancestor.id
+ }
},
{
'type' => 'repository',
'name' => "#{project.full_path}/*",
'actions' => ['pull'],
- 'meta' => { 'project_path' => project.full_path }
+ 'meta' => {
+ 'project_path' => project.full_path,
+ 'project_id' => project.id,
+ 'root_namespace_id' => project.root_ancestor.id
+ }
}
]
end
@@ -880,19 +892,35 @@ RSpec.shared_examples 'a container registry auth service' do
{ 'type' => 'repository',
'name' => internal_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => internal_project.full_path } },
+ 'meta' => {
+ 'project_path' => internal_project.full_path,
+ 'project_id' => internal_project.id,
+ 'root_namespace_id' => internal_project.root_ancestor.id
+ } },
{ 'type' => 'repository',
'name' => private_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => private_project.full_path } },
+ 'meta' => {
+ 'project_path' => private_project.full_path,
+ 'project_id' => private_project.id,
+ 'root_namespace_id' => private_project.root_ancestor.id
+ } },
{ 'type' => 'repository',
'name' => public_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => public_project.full_path } },
+ 'meta' => {
+ 'project_path' => public_project.full_path,
+ 'project_id' => public_project.id,
+ 'root_namespace_id' => public_project.root_ancestor.id
+ } },
{ 'type' => 'repository',
'name' => public_project_private_container_registry.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => public_project_private_container_registry.full_path } }
+ 'meta' => {
+ 'project_path' => public_project_private_container_registry.full_path,
+ 'project_id' => public_project_private_container_registry.id,
+ 'root_namespace_id' => public_project_private_container_registry.root_ancestor.id
+ } }
]
end
end
@@ -907,11 +935,19 @@ RSpec.shared_examples 'a container registry auth service' do
{ 'type' => 'repository',
'name' => internal_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => internal_project.full_path } },
+ 'meta' => {
+ 'project_path' => internal_project.full_path,
+ 'project_id' => internal_project.id,
+ 'root_namespace_id' => internal_project.root_ancestor.id
+ } },
{ 'type' => 'repository',
'name' => public_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => public_project.full_path } }
+ 'meta' => {
+ 'project_path' => public_project.full_path,
+ 'project_id' => public_project.id,
+ 'root_namespace_id' => public_project.root_ancestor.id
+ } }
]
end
end
@@ -926,7 +962,11 @@ RSpec.shared_examples 'a container registry auth service' do
{ 'type' => 'repository',
'name' => public_project.full_path,
'actions' => ['pull'],
- 'meta' => { 'project_path' => public_project.full_path } }
+ 'meta' => {
+ 'project_path' => public_project.full_path,
+ 'project_id' => public_project.id,
+ 'root_namespace_id' => public_project.root_ancestor.id
+ } }
]
end
end