From 04b866f03b967470e20d5dd106fd493bebe40909 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Sat, 4 Nov 2023 00:07:28 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .haml-lint_todo.yml | 43 --- .../environments/graphql/resolvers/flux.js | 95 +++++- app/assets/javascripts/pages/users/index.js | 4 + app/assets/javascripts/pages/users/user_tabs.js | 3 + .../components/source_viewer/source_viewer_new.vue | 44 +-- .../vue_shared/components/source_viewer/utils.js | 2 +- app/assets/stylesheets/framework/layout.scss | 6 - app/helpers/application_helper.rb | 1 - app/helpers/nav_helper.rb | 4 +- app/services/ci/destroy_pipeline_service.rb | 2 + .../application_settings/_localization.html.haml | 2 +- app/views/profiles/two_factor_auths/show.html.haml | 4 +- .../_merge_request_merge_commit_template.html.haml | 2 +- ...ge_request_merge_suggestions_settings.html.haml | 2 +- ..._merge_request_squash_commit_template.html.haml | 2 +- app/views/projects/usage_quotas/index.html.haml | 2 +- ...auto_devops_implicitly_enabled_banner.html.haml | 2 +- ...gistration_features_discovery_message.html.haml | 2 +- app/views/shared/_service_ping_consent.html.haml | 2 +- app/views/shared/deploy_tokens/_form.html.haml | 2 +- app/views/shared/deploy_tokens/_table.html.haml | 2 +- app/views/shared/empty_states/_snippets.html.haml | 2 +- app/views/shared/web_hooks/_form.html.haml | 2 +- app/views/users/show.html.haml | 2 + .../development/super_sidebar_logged_out.yml | 8 - .../audit_event_streaming/audit_event_types.md | 1 + doc/ci/environments/kubernetes_dashboard.md | 2 +- jest.config.base.js | 9 + jest.config.integration.js | 4 + jest.config.js | 6 +- jest.config.scripts.js | 8 + lib/sidebars/projects/menus/ci_cd_menu.rb | 2 +- lib/sidebars/projects/menus/scope_menu.rb | 2 +- locale/gitlab.pot | 2 +- package.json | 1 + scripts/lib/glfm/update_example_snapshots.rb | 2 +- scripts/review_apps/base-config.yaml | 6 +- spec/features/calendar_spec.rb | 3 +- spec/features/contextual_sidebar_spec.rb | 32 +- ...ard_with_external_authorization_service_spec.rb | 43 --- spec/features/dashboard/issuables_counter_spec.rb | 36 ++- spec/features/dashboard/snippets_spec.rb | 2 +- spec/features/global_search_spec.rb | 17 +- spec/features/groups/merge_requests_spec.rb | 6 +- spec/features/monitor_sidebar_link_spec.rb | 102 +++--- spec/features/profiles/two_factor_auths_spec.rb | 2 +- .../features/projects/files/user_find_file_spec.rb | 14 +- spec/features/projects/forks/fork_list_spec.rb | 2 +- spec/features/projects/pipelines/pipelines_spec.rb | 4 +- spec/features/projects/snippets/show_spec.rb | 59 +++- .../features/projects/work_items/work_item_spec.rb | 4 +- .../search/user_uses_header_search_field_spec.rb | 76 ++--- spec/features/snippets/show_spec.rb | 66 ++-- .../environments/graphql/resolvers/flux_spec.js | 341 ++++++++++++++++----- .../source_viewer/source_viewer_new_spec.js | 2 + spec/helpers/application_helper_spec.rb | 8 - spec/helpers/nav_helper_spec.rb | 22 +- .../lib/sidebars/projects/menus/scope_menu_spec.rb | 2 +- spec/support/helpers/search_helpers.rb | 6 +- spec/support/rspec_order_todo.yml | 2 - ..._features_apply_to_issuables_shared_examples.rb | 4 +- .../features/snippets_shared_examples.rb | 26 +- .../features/work_items_shared_examples.rb | 6 +- spec/views/layouts/application.html.haml_spec.rb | 1 - .../_super_sidebar_logged_out.html.haml_spec.rb | 1 - .../layouts/nav/sidebar/_project.html.haml_spec.rb | 2 +- 66 files changed, 706 insertions(+), 472 deletions(-) delete mode 100644 .haml-lint_todo.yml delete mode 100644 config/feature_flags/development/super_sidebar_logged_out.yml create mode 100644 jest.config.scripts.js delete mode 100644 spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml deleted file mode 100644 index c587e299c62..00000000000 --- a/.haml-lint_todo.yml +++ /dev/null @@ -1,43 +0,0 @@ -# This configuration was generated by -# `haml-lint --auto-gen-config` -# on 2023-10-30 15:10:05 +0100 using Haml-Lint version 0.40.1. -# The point is for the user to remove these configuration records -# one by one as the lints are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of Haml-Lint, may require this file to be generated again. - -linters: - - # Offense count: 37 - DocumentationLinks: - exclude: - - "app/views/admin/application_settings/_localization.html.haml" - - "app/views/profiles/two_factor_auths/show.html.haml" - - "app/views/projects/settings/merge_requests/_merge_request_merge_commit_template.html.haml" - - "app/views/projects/settings/merge_requests/_merge_request_merge_suggestions_settings.html.haml" - - "app/views/projects/settings/merge_requests/_merge_request_squash_commit_template.html.haml" - - "app/views/projects/usage_quotas/index.html.haml" - - "app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml" - - "app/views/shared/_registration_features_discovery_message.html.haml" - - "app/views/shared/_service_ping_consent.html.haml" - - "app/views/shared/deploy_tokens/_form.html.haml" - - "app/views/shared/deploy_tokens/_table.html.haml" - - "app/views/shared/empty_states/_snippets.html.haml" - - "app/views/shared/web_hooks/_form.html.haml" - - "ee/app/views/admin/application_settings/_custom_templates_form.html.haml" - - "ee/app/views/admin/application_settings/_elasticsearch_form.html.haml" - - "ee/app/views/admin/application_settings/_microsoft_application.haml" - - "ee/app/views/admin/application_settings/_templates.html.haml" - - "ee/app/views/admin/push_rules/_merge_request_approvals.html.haml" - - "ee/app/views/admin/push_rules/_merge_request_approvals_fields.html.haml" - - "ee/app/views/compliance_management/compliance_framework/_project_settings.html.haml" - - "ee/app/views/groups/_custom_project_templates_setting.html.haml" - - "ee/app/views/groups/_templates_setting.html.haml" - - "ee/app/views/profiles/preferences/_code_suggestions_settings_self_assignment.html.haml" - - "ee/app/views/projects/settings/ci_cd/_auto_rollback.html.haml" - - "ee/app/views/projects/settings/ci_cd/_pipeline_subscriptions.html.haml" - - "ee/app/views/projects/settings/ci_cd/_protected_environments.html.haml" - - "ee/app/views/projects/settings/merge_requests/_merge_request_approvals_settings.html.haml" - - "ee/app/views/projects/settings/merge_requests/_target_branch_rules_settings.html.haml" - - "ee/app/views/shared/_new_user_signups_cap_reached_alert.html.haml" - - "ee/app/views/shared/promotions/_promote_mobile_devops.html.haml" \ No newline at end of file diff --git a/app/assets/javascripts/environments/graphql/resolvers/flux.js b/app/assets/javascripts/environments/graphql/resolvers/flux.js index d39b1bed7b6..627737276db 100644 --- a/app/assets/javascripts/environments/graphql/resolvers/flux.js +++ b/app/assets/javascripts/environments/graphql/resolvers/flux.js @@ -1,12 +1,18 @@ +import { Configuration, WatchApi, EVENT_DATA } from '@gitlab/cluster-client'; import axios from '~/lib/utils/axios_utils'; import { HELM_RELEASES_RESOURCE_TYPE, KUSTOMIZATIONS_RESOURCE_TYPE, } from '~/environments/constants'; +import fluxKustomizationStatusQuery from '../queries/flux_kustomization_status.query.graphql'; +import fluxHelmReleaseStatusQuery from '../queries/flux_helm_release_status.query.graphql'; const helmReleasesApiVersion = 'helm.toolkit.fluxcd.io/v2beta1'; const kustomizationsApiVersion = 'kustomize.toolkit.fluxcd.io/v1beta1'; +const helmReleaseField = 'fluxHelmReleaseStatus'; +const kustomizationField = 'fluxKustomizationStatus'; + const handleClusterError = (err) => { const error = err?.response?.data?.message ? new Error(err.response.data.message) : err; throw error; @@ -22,14 +28,57 @@ const buildFluxResourceUrl = ({ return `${basePath}/apis/${apiVersion}/namespaces/${namespace}/${resourceType}/${environmentName}`; }; -const getFluxResourceStatus = (configuration, url) => { - const { headers } = configuration; +const buildFluxResourceWatchPath = ({ namespace, apiVersion, resourceType }) => { + return `/apis/${apiVersion}/namespaces/${namespace}/${resourceType}`; +}; + +const watchFluxResource = ({ watchPath, resourceName, query, variables, field, client }) => { + const config = new Configuration(variables.configuration); + const watcherApi = new WatchApi(config); + const fieldSelector = `metadata.name=${decodeURIComponent(resourceName)}`; + + watcherApi + .subscribeToStream(watchPath, { watch: true, fieldSelector }) + .then((watcher) => { + let result = []; + + watcher.on(EVENT_DATA, (data) => { + result = data[0]?.status?.conditions; + + client.writeQuery({ + query, + variables, + data: { [field]: result }, + }); + }); + }) + .catch((err) => { + handleClusterError(err); + }); +}; + +const getFluxResourceStatus = ({ url, watchPath, query, variables, field, client }) => { + const { headers } = variables.configuration; const withCredentials = true; return axios .get(url, { withCredentials, headers }) .then((res) => { - return res?.data?.status?.conditions || []; + const fluxData = res?.data; + const resourceName = fluxData?.metadata?.name; + + if (gon.features?.k8sWatchApi && resourceName) { + watchFluxResource({ + watchPath, + resourceName, + query, + variables, + field, + client, + }); + } + + return fluxData?.status?.conditions || []; }) .catch((err) => { handleClusterError(err); @@ -62,7 +111,16 @@ const getFluxResources = (configuration, url) => { }; export default { - fluxKustomizationStatus(_, { configuration, namespace, environmentName, fluxResourcePath = '' }) { + fluxKustomizationStatus( + _, + { configuration, namespace, environmentName, fluxResourcePath = '' }, + { client }, + ) { + const watchPath = buildFluxResourceWatchPath({ + namespace, + apiVersion: kustomizationsApiVersion, + resourceType: KUSTOMIZATIONS_RESOURCE_TYPE, + }); let url; if (fluxResourcePath) { @@ -76,9 +134,25 @@ export default { environmentName, }); } - return getFluxResourceStatus(configuration, url); + return getFluxResourceStatus({ + url, + watchPath, + query: fluxKustomizationStatusQuery, + variables: { configuration, namespace, environmentName, fluxResourcePath }, + field: kustomizationField, + client, + }); }, - fluxHelmReleaseStatus(_, { configuration, namespace, environmentName, fluxResourcePath }) { + fluxHelmReleaseStatus( + _, + { configuration, namespace, environmentName, fluxResourcePath }, + { client }, + ) { + const watchPath = buildFluxResourceWatchPath({ + namespace, + apiVersion: helmReleasesApiVersion, + resourceType: HELM_RELEASES_RESOURCE_TYPE, + }); let url; if (fluxResourcePath) { @@ -92,7 +166,14 @@ export default { environmentName, }); } - return getFluxResourceStatus(configuration, url); + return getFluxResourceStatus({ + url, + watchPath, + query: fluxHelmReleaseStatusQuery, + variables: { configuration, namespace, environmentName, fluxResourcePath }, + field: helmReleaseField, + client, + }); }, fluxKustomizations(_, { configuration, namespace }) { const url = buildFluxResourceUrl({ diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js index 4215cfbf409..2e479da09e8 100644 --- a/app/assets/javascripts/pages/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -5,6 +5,10 @@ import { initProfileTabs } from '~/profile'; import UserTabs from './user_tabs'; function initUserProfile(action) { + // TODO: Remove both Vue and legacy JS tabs code/feature flag uses with the + // removal of the old navigation. + // See https://gitlab.com/groups/gitlab-org/-/epics/11875. + if (gon.features?.profileTabsVue) { initProfileTabs(); } else { diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index d368c76b6cc..79eb3902116 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -1,3 +1,6 @@ +// TODO: Remove this with the removal of the old navigation. +// See https://gitlab.com/groups/gitlab-org/-/epics/11875. + import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; import Activities from '~/activities'; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue index fa309f89d89..dcefa66c403 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue @@ -51,7 +51,6 @@ export default { lineHighlighter: new LineHighlighter(), blameData: [], renderedChunks: [], - overlappingBlameRequested: false, }; }, computed: { @@ -72,6 +71,7 @@ export default { showBlame: { handler(shouldShow) { toggleBlameClasses(this.blameData, shouldShow); + this.requestBlameInfo(this.renderedChunks[0]); }, immediate: true, }, @@ -92,32 +92,36 @@ export default { this.selectLine(); }, methods: { - async handleChunkAppear(chunkIndex) { - const chunk = this.chunks[chunkIndex]; - + async handleChunkAppear(chunkIndex, handleOverlappingChunk = true) { if (!this.renderedChunks.includes(chunkIndex)) { this.renderedChunks.push(chunkIndex); + await this.requestBlameInfo(chunkIndex); - const { data } = await this.$apollo.query({ - query: blameDataQuery, - variables: { - fullPath: this.projectPath, - filePath: this.blob.path, - fromLine: chunk.startingFrom + 1, - toLine: chunk.startingFrom + chunk.totalLines, - }, - }); - - const blob = data?.project?.repository?.blobs?.nodes[0]; - const blameGroups = blob?.blame?.groups; - if (blameGroups) this.blameData.push(...blameGroups); - if (chunkIndex > 0 && !this.overlappingBlameRequested) { + if (chunkIndex > 0 && handleOverlappingChunk) { // request the blame information for overlapping chunk incase it is visible in the DOM - this.handleChunkAppear(chunkIndex - 1); - this.overlappingBlameRequested = true; + this.handleChunkAppear(chunkIndex - 1, false); } } }, + async requestBlameInfo(chunkIndex) { + const chunk = this.chunks[chunkIndex]; + if (!this.showBlame || !chunk) return; + + const { data } = await this.$apollo.query({ + query: blameDataQuery, + variables: { + fullPath: this.projectPath, + filePath: this.blob.path, + fromLine: chunk.startingFrom + 1, + toLine: chunk.startingFrom + chunk.totalLines, + }, + }); + + const blob = data?.project?.repository?.blobs?.nodes[0]; + const blameGroups = blob?.blame?.groups; + const isDuplicate = this.blameData.includes(blameGroups[0]); + if (blameGroups && !isDuplicate) this.blameData.push(...blameGroups); + }, async selectLine() { await this.$nextTick(); this.lineHighlighter.highlightHash(this.$route.hash); diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/utils.js b/app/assets/javascripts/vue_shared/components/source_viewer/utils.js index 71a6db1a72a..596829b51a4 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/utils.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/utils.js @@ -10,7 +10,7 @@ const findLineContentElement = (lineNumber) => document.getElementById(`LC${line export const calculateBlameOffset = (lineNumber) => { if (lineNumber === 1) return '0px'; const blobViewerOffset = document.querySelector(VIEWER_SELECTOR)?.getBoundingClientRect().top; - const lineContentOffset = findLineContentElement(lineNumber).getBoundingClientRect().top; + const lineContentOffset = findLineContentElement(lineNumber)?.getBoundingClientRect().top; return `${lineContentOffset - blobViewerOffset}px`; }; diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 171f070d776..33c8a0254fd 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -4,12 +4,6 @@ html { &.touch .tooltip { display: none !important; } - - @include media-breakpoint-up(sm) { - &.logged-out-marketing-header { - --header-height: 72px; - } - } } body { diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 541a71d3302..732f5a177a9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -318,7 +318,6 @@ module ApplicationHelper class_names << 'with-header' if !show_super_sidebar? || !current_user class_names << 'with-top-bar' if show_super_sidebar? && !@hide_top_bar_padding class_names << system_message_class - class_names << 'logged-out-marketing-header' if !current_user && ::Gitlab.com? && !show_super_sidebar? class_names end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 21c81eefce2..f1e05b43cd3 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -80,9 +80,7 @@ module NavHelper def show_super_sidebar?(user = current_user) # The new sidebar is not enabled for anonymous use - # Once we enable the new sidebar by default, this - # should return true - return Feature.enabled?(:super_sidebar_logged_out) unless user + return true unless user # Users who get the new nav unless they explicitly # opt-out via the toggle diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb index a9d2e17657e..7adf573687a 100644 --- a/app/services/ci/destroy_pipeline_service.rb +++ b/app/services/ci/destroy_pipeline_service.rb @@ -28,3 +28,5 @@ module Ci end end end + +Ci::DestroyPipelineService.prepend_mod diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml index e1eb0ba84eb..62849a81633 100644 --- a/app/views/admin/application_settings/_localization.html.haml +++ b/app/views/admin/application_settings/_localization.html.haml @@ -11,7 +11,7 @@ .form-group = f.label :time_tracking, _('Time tracking'), class: 'label-bold' - - time_tracking_help_link = help_page_path('user/project/time_tracking.md') + - time_tracking_help_link = help_page_path('user/project/time_tracking') - time_tracking_help_link_start = ''.html_safe % { url: time_tracking_help_link } = f.gitlab_ui_checkbox_component :time_tracking_limit_to_hours, _('Limit display of time tracking units to hours.'), help_text: _('Display time tracking in issues in total hours only. %{link_start}What is time tracking?%{link_end}').html_safe % { link_start: time_tracking_help_link_start, link_end: ''.html_safe } diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index ff0b31da022..7c42053a376 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -41,7 +41,7 @@ alert_options: { class: 'gl-mb-3' }, dismissible: false) do |c| - c.with_body do - = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer' + = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer' - if current_password_required? .form-group @@ -130,7 +130,7 @@ alert_options: { class: 'gl-mb-3' }, dismissible: false) do |c| - c.with_body do - = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer' + = link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer' .js-manage-two-factor-form{ data: { current_password_required: current_password_required?.to_s, profile_two_factor_auth_path: profile_two_factor_auth_path, profile_two_factor_auth_method: 'delete', codes_profile_two_factor_auth_path: codes_profile_two_factor_auth_path, codes_profile_two_factor_auth_method: 'post' } } - else %p diff --git a/app/views/projects/settings/merge_requests/_merge_request_merge_commit_template.html.haml b/app/views/projects/settings/merge_requests/_merge_request_merge_commit_template.html.haml index da1965f549c..0a6f940e41a 100644 --- a/app/views/projects/settings/merge_requests/_merge_request_merge_commit_template.html.haml +++ b/app/views/projects/settings/merge_requests/_merge_request_merge_commit_template.html.haml @@ -9,5 +9,5 @@ %p.form-text.text-muted = s_('ProjectSettings|Leave empty to use default template.') = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_COMMIT_TEMPLATE_LENGTH }) - - link = link_to('', help_page_path('user/project/merge_requests/commit_templates.md'), target: '_blank', rel: 'noopener noreferrer') + - link = link_to('', help_page_path('user/project/merge_requests/commit_templates'), target: '_blank', rel: 'noopener noreferrer') = safe_format(s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}'), tag_pair(link, :link_start, :link_end)) diff --git a/app/views/projects/settings/merge_requests/_merge_request_merge_suggestions_settings.html.haml b/app/views/projects/settings/merge_requests/_merge_request_merge_suggestions_settings.html.haml index 501288f727b..5aa7449c72f 100644 --- a/app/views/projects/settings/merge_requests/_merge_request_merge_suggestions_settings.html.haml +++ b/app/views/projects/settings/merge_requests/_merge_request_merge_suggestions_settings.html.haml @@ -9,5 +9,5 @@ %p.form-text.text-muted = s_('ProjectSettings|Leave empty to use default template.') = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_SUGGESTIONS_TEMPLATE_LENGTH }) - - link = link_to('', help_page_path('user/project/merge_requests/reviews/suggestions.md', anchor: 'configure-the-commit-message-for-applied-suggestions'), target: '_blank', rel: 'noopener noreferrer') + - link = link_to('', help_page_path('user/project/merge_requests/reviews/suggestions', anchor: 'configure-the-commit-message-for-applied-suggestions'), target: '_blank', rel: 'noopener noreferrer') = safe_format(s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}'), tag_pair(link, :link_start, :link_end)) diff --git a/app/views/projects/settings/merge_requests/_merge_request_squash_commit_template.html.haml b/app/views/projects/settings/merge_requests/_merge_request_squash_commit_template.html.haml index bc6530b927c..26b038f1bf7 100644 --- a/app/views/projects/settings/merge_requests/_merge_request_squash_commit_template.html.haml +++ b/app/views/projects/settings/merge_requests/_merge_request_squash_commit_template.html.haml @@ -9,5 +9,5 @@ %p.form-text.text-muted = s_('ProjectSettings|Leave empty to use default template.') = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_COMMIT_TEMPLATE_LENGTH }) - - link = link_to('', help_page_path('user/project/merge_requests/commit_templates.md'), target: '_blank', rel: 'noopener noreferrer') + - link = link_to('', help_page_path('user/project/merge_requests/commit_templates'), target: '_blank', rel: 'noopener noreferrer') = safe_format(s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}'), tag_pair(link, :link_start, :link_end)) diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml index 6f2a2aacf66..039df9738ff 100644 --- a/app/views/projects/usage_quotas/index.html.haml +++ b/app/views/projects/usage_quotas/index.html.haml @@ -14,7 +14,7 @@ .col-sm-12 %p.gl-text-secondary = s_('UsageQuota|Usage of project resources across the %{strong_start}%{project_name}%{strong_end} project').html_safe % { strong_start: ''.html_safe, strong_end: ''.html_safe, project_name: @project.name } + '.' - %a{ href: help_page_path('user/usage_quotas.md'), target: '_blank', rel: 'noopener noreferrer' } + %a{ href: help_page_path('user/usage_quotas'), target: '_blank', rel: 'noopener noreferrer' } = s_('UsageQuota|Learn more about usage quotas') + '.' = gl_tabs_nav({ id: 'js-project-usage-quotas-tabs' }) do diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml index 79a9bafc4f0..0ff2ee935cc 100644 --- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml +++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml @@ -9,4 +9,4 @@ = _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work.') - c.with_actions do = link_button_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link', variant: :confirm - = link_button_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link gl-ml-3' + = link_button_to _('More information'), help_page_path('topics/autodevops/index'), target: '_blank', class: 'alert-link gl-ml-3' diff --git a/app/views/shared/_registration_features_discovery_message.html.haml b/app/views/shared/_registration_features_discovery_message.html.haml index 6e386866dfb..5fa554171aa 100644 --- a/app/views/shared/_registration_features_discovery_message.html.haml +++ b/app/views/shared/_registration_features_discovery_message.html.haml @@ -1,5 +1,5 @@ - feature_title = local_assigns.fetch(:feature_title, s_('RegistrationFeatures|use this feature')) -- registration_features_docs_path = help_page_path('administration/settings/usage_statistics.md', anchor: 'registration-features-program') +- registration_features_docs_path = help_page_path('administration/settings/usage_statistics', anchor: 'registration-features-program') - registration_features_link_start = ''.html_safe % { url: registration_features_docs_path } %div diff --git a/app/views/shared/_service_ping_consent.html.haml b/app/views/shared/_service_ping_consent.html.haml index a5e63896367..b65808bfcd2 100644 --- a/app/views/shared/_service_ping_consent.html.haml +++ b/app/views/shared/_service_ping_consent.html.haml @@ -1,7 +1,7 @@ - if session[:ask_for_usage_stats_consent] = render Pajamas::AlertComponent.new(alert_options: { class: 'service-ping-consent-message' }) do |c| - c.with_body do - - docs_link = link_to '', help_page_path('administration/settings/usage_statistics.md'), class: 'gl-link' + - docs_link = link_to '', help_page_path('administration/settings/usage_statistics'), class: 'gl-link' - settings_link = link_to '', metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), class: 'gl-link' = safe_format s_('ServicePing|To help improve GitLab, we would like to periodically %{link_start}collect usage information%{link_end}.'), tag_pair(docs_link, :link_start, :link_end) = safe_format s_('ServicePing|This can be changed at any time in %{link_start}your settings%{link_end}.'), tag_pair(settings_link, :link_start, :link_end) diff --git a/app/views/shared/deploy_tokens/_form.html.haml b/app/views/shared/deploy_tokens/_form.html.haml index bb7e0d774cc..b172e3bf94f 100644 --- a/app/views/shared/deploy_tokens/_form.html.haml +++ b/app/views/shared/deploy_tokens/_form.html.haml @@ -1,5 +1,5 @@ %p - - link = link_to('', help_page_path('user/project/deploy_tokens/index.md'), target: '_blank', rel: 'noopener noreferrer') + - link = link_to('', help_page_path('user/project/deploy_tokens/index'), target: '_blank', rel: 'noopener noreferrer') = safe_format(s_('DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}'), tag_pair(link, :link_start, :link_end)) = gitlab_ui_form_for token, url: create_deploy_token_path(group_or_project, anchor: 'js-deploy-tokens'), method: :post, remote: true do |f| diff --git a/app/views/shared/deploy_tokens/_table.html.haml b/app/views/shared/deploy_tokens/_table.html.haml index 3b351387d41..0b8a97a34f2 100644 --- a/app/views/shared/deploy_tokens/_table.html.haml +++ b/app/views/shared/deploy_tokens/_table.html.haml @@ -16,7 +16,7 @@ packages_registry_enabled: packages_registry_enabled?(group_or_project), create_new_token_path: create_deploy_token_path(group_or_project), token_type: group_or_project.is_a?(Group) ? 'group' : 'project', - deploy_tokens_help_url: help_page_path('user/project/deploy_tokens/index.md') + deploy_tokens_help_url: help_page_path('user/project/deploy_tokens/index') } } - if active_tokens.present? diff --git a/app/views/shared/empty_states/_snippets.html.haml b/app/views/shared/empty_states/_snippets.html.haml index a2457fb0810..800cfe8b0d1 100644 --- a/app/views/shared/empty_states/_snippets.html.haml +++ b/app/views/shared/empty_states/_snippets.html.haml @@ -13,6 +13,6 @@ .gl-mt-3< - if button_path = link_button_to s_('SnippetsEmptyState|New snippet'), button_path, title: s_('SnippetsEmptyState|New snippet'), id: 'new_snippet_link', data: { testid: 'create-first-snippet-link' }, variant: :confirm - = link_button_to s_('SnippetsEmptyState|Documentation'), help_page_path('user/snippets.md'), title: s_('SnippetsEmptyState|Documentation') + = link_button_to s_('SnippetsEmptyState|Documentation'), help_page_path('user/snippets'), title: s_('SnippetsEmptyState|Documentation') - else %h4.gl-text-center= s_('SnippetsEmptyState|There are no snippets to show.') diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml index 7c713e63cd7..a3dfc6eb042 100644 --- a/app/views/shared/web_hooks/_form.html.haml +++ b/app/views/shared/web_hooks/_form.html.haml @@ -66,7 +66,7 @@ help_text: s_('Webhooks|A release is created, updated, or deleted.') - if Feature.enabled?(:emoji_webhooks, hook.parent) %li.gl-pb-5 - - emoji_help_link = link_to s_('Which emoji events trigger webhooks'), help_page_path('user/project/integrations/webhook_events.md', anchor: 'emoji-events') + - emoji_help_link = link_to s_('Which emoji events trigger webhooks'), help_page_path('user/project/integrations/webhook_events', anchor: 'emoji-events') = form.gitlab_ui_checkbox_component :emoji_events, integration_webhook_event_human_name(:emoji_events), help_text: s_('Webhooks|An emoji is awarded or revoked. %{help_link}?').html_safe % { help_link: emoji_help_link } diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index c4a55a7d7ed..e23555428aa 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -114,6 +114,8 @@ %p.profile-user-bio.gl-mb-3 = @user.bio + -# TODO: Remove this with the removal of the old navigation. + -# See https://gitlab.com/groups/gitlab-org/-/epics/11875. - if !profile_tabs.empty? && !Feature.enabled?(:profile_tabs_vue, current_user) .scrolling-tabs-container{ class: [('gl-display-none' if show_super_sidebar?)] } %button.fade-left{ type: 'button', title: _('Scroll left'), 'aria-label': _('Scroll left') } diff --git a/config/feature_flags/development/super_sidebar_logged_out.yml b/config/feature_flags/development/super_sidebar_logged_out.yml deleted file mode 100644 index 8deeb63b537..00000000000 --- a/config/feature_flags/development/super_sidebar_logged_out.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: super_sidebar_logged_out -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127756 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/419936 -milestone: '16.3' -type: development -group: group::foundations -default_enabled: false diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md index 6e1436284d0..ac630c18717 100644 --- a/doc/administration/audit_event_streaming/audit_event_types.md +++ b/doc/administration/audit_event_streaming/audit_event_types.md @@ -172,6 +172,7 @@ Audit event types belong to the following product categories. | [`ci_variable_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91983) | Triggered when a CI variable is created at a project level| **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.2](https://gitlab.com/gitlab-org/gitlab/-/issues/363090) | | [`ci_variable_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91983) | Triggered when a project's CI variable is deleted| **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.2](https://gitlab.com/gitlab-org/gitlab/-/issues/363090) | | [`ci_variable_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91983) | Triggered when a project's CI variable is updated| **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.2](https://gitlab.com/gitlab-org/gitlab/-/issues/363090) | +| [`destroy_pipeline`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135255) | Event triggered when a pipeline is deleted| **{check-circle}** Yes | **{check-circle}** Yes | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/339041) | ### Deployment management diff --git a/doc/ci/environments/kubernetes_dashboard.md b/doc/ci/environments/kubernetes_dashboard.md index fe2e44f92f2..4062fbee25e 100644 --- a/doc/ci/environments/kubernetes_dashboard.md +++ b/doc/ci/environments/kubernetes_dashboard.md @@ -64,7 +64,7 @@ On GitLab.com, this feature is not available. View a dashboard to see the status of any connected clusters. If the `k8s_watch_api` feature flag is enabled, the status of your -pods updates in real time. +pods and Flux reconciliation updates in real time. To view a configured dashboard: diff --git a/jest.config.base.js b/jest.config.base.js index 9183b83487a..18c13293275 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -17,6 +17,9 @@ module.exports = (path, options = {}) => { moduleNameMapper: extModuleNameMapper = {}, moduleNameMapperEE: extModuleNameMapperEE = {}, moduleNameMapperJH: extModuleNameMapperJH = {}, + roots: extRoots = [], + rootsEE: extRootsEE = [], + rootsJH: extRootsJH = [], } = options; const reporters = ['default']; @@ -266,5 +269,11 @@ module.exports = (path, options = {}) => { '/spec/frontend/__helpers__/html_string_serializer.js', '/spec/frontend/__helpers__/clean_html_element_serializer.js', ], + roots: [ + '/app/assets/javascripts/', + ...extRoots, + ...(IS_EE ? ['/ee/app/assets/javascripts/', ...extRootsEE] : []), + ...(IS_JH ? ['/jh/app/assets/javascripts/', ...extRootsJH] : []), + ], }; }; diff --git a/jest.config.integration.js b/jest.config.integration.js index 0693a500990..919c299000b 100644 --- a/jest.config.integration.js +++ b/jest.config.integration.js @@ -23,6 +23,10 @@ module.exports = { moduleNameMapperJH: { '^jh_else_ce_test_helpers(/.*)$': '/jh/spec/frontend_integration/test_helpers$1', }, + // We need to include spec/frontend in `roots` for the __mocks__ to be found + roots: ['/spec/frontend_integration/', '/spec/frontend/'], + rootsEE: ['/ee/spec/frontend_integration/'], + rootsJH: ['/jh/spec/frontend_integration/'], }), fakeTimers: { enableGlobally: false, diff --git a/jest.config.js b/jest.config.js index 96a62b18d8f..3f3e1abbf0c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,6 +9,10 @@ if (IS_JH && fs.existsSync('./jh/jest.config.js')) { module.exports = require('./jh/jest.config'); } else { module.exports = { - ...baseConfig('spec/frontend'), + ...baseConfig('spec/frontend', { + roots: ['/spec/frontend'], + rootsEE: ['/ee/spec/frontend'], + rootsJH: ['/jh/spec/frontend'], + }), }; } diff --git a/jest.config.scripts.js b/jest.config.scripts.js new file mode 100644 index 00000000000..dd824bd01bf --- /dev/null +++ b/jest.config.scripts.js @@ -0,0 +1,8 @@ +const baseConfig = require('./jest.config.base'); + +module.exports = { + ...baseConfig('spec/frontend', { + roots: ['/scripts/lib', '/spec/frontend'], + }), + testMatch: [], +}; diff --git a/lib/sidebars/projects/menus/ci_cd_menu.rb b/lib/sidebars/projects/menus/ci_cd_menu.rb index 02596b16cfa..c77e8e996b0 100644 --- a/lib/sidebars/projects/menus/ci_cd_menu.rb +++ b/lib/sidebars/projects/menus/ci_cd_menu.rb @@ -18,7 +18,7 @@ module Sidebars override :extra_container_html_options def extra_container_html_options { - class: 'shortcuts-pipelines rspec-link-pipelines' + class: 'shortcuts-pipelines' } end diff --git a/lib/sidebars/projects/menus/scope_menu.rb b/lib/sidebars/projects/menus/scope_menu.rb index f388c814bd7..d03abfdfb7e 100644 --- a/lib/sidebars/projects/menus/scope_menu.rb +++ b/lib/sidebars/projects/menus/scope_menu.rb @@ -22,7 +22,7 @@ module Sidebars override :extra_container_html_options def extra_container_html_options { - class: 'shortcuts-project rspec-project-link' + class: 'shortcuts-project' } end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 58d123638b1..6edb19a1df3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -50388,7 +50388,7 @@ msgstr "" msgid "Tracing|Time range" msgstr "" -msgid "Tracing|Toggle children spans" +msgid "Tracing|Toggle child spans" msgstr "" msgid "Tracing|Trace ID" diff --git a/package.json b/package.json index 192aa370cee..c5900e31e33 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "jest:ci:predictive": "jest --config jest.config.js --ci --coverage --findRelatedTests $(cat $RSPEC_CHANGED_FILES_PATH) $(cat $RSPEC_MATCHING_JS_FILES_PATH) --passWithNoTests --testSequencer ./scripts/frontend/parallel_ci_sequencer.js", "jest:contract": "PACT_DO_NOT_TRACK=true jest --config jest.config.contract.js --runInBand", "jest:integration": "jest --config jest.config.integration.js", + "jest:scripts": "jest --config jest.config.scripts.js", "jest:quarantine": "grep -r 'quarantine:' spec/frontend ee/spec/frontend", "lint:eslint": "node scripts/frontend/eslint.js", "lint:eslint:fix": "node scripts/frontend/eslint.js --fix", diff --git a/scripts/lib/glfm/update_example_snapshots.rb b/scripts/lib/glfm/update_example_snapshots.rb index 793f7521283..01760c23a68 100644 --- a/scripts/lib/glfm/update_example_snapshots.rb +++ b/scripts/lib/glfm/update_example_snapshots.rb @@ -289,7 +289,7 @@ module Glfm wysiwyg_html_and_json_tempfile_path = Dir::Tmpname.create(WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME) {} ENV['OUTPUT_WYSIWYG_HTML_AND_JSON_TEMPFILE_PATH'] = wysiwyg_html_and_json_tempfile_path - cmd = "yarn jest --testMatch '**/render_wysiwyg_html_and_json.js' #{__dir__}/render_wysiwyg_html_and_json.js" + cmd = "yarn jest:scripts #{__dir__}/render_wysiwyg_html_and_json.js" run_external_cmd(cmd) output("Reading generated WYSIWYG HTML and prosemirror JSON from tempfile " \ diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml index a425aecc86b..721733f6f68 100644 --- a/scripts/review_apps/base-config.yaml +++ b/scripts/review_apps/base-config.yaml @@ -56,10 +56,10 @@ gitlab: # Based on https://console.cloud.google.com/monitoring/metrics-explorer;duration=P14D?pageState=%7B%22xyChart%22:%7B%22constantLines%22:%5B%5D,%22dataSets%22:%5B%7B%22plotType%22:%22LINE%22,%22targetAxis%22:%22Y1%22,%22timeSeriesFilter%22:%7B%22aggregations%22:%5B%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22groupByFields%22:%5B%5D,%22perSeriesAligner%22:%22ALIGN_RATE%22%7D,%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22groupByFields%22:%5B%5D,%22perSeriesAligner%22:%22ALIGN_MEAN%22%7D%5D,%22apiSource%22:%22DEFAULT_CLOUD%22,%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22filter%22:%22metric.type%3D%5C%22kubernetes.io%2Fcontainer%2Fcpu%2Fcore_usage_time%5C%22%20resource.type%3D%5C%22k8s_container%5C%22%20resource.label.%5C%22container_name%5C%22%3D%5C%22gitlab-shell%5C%22%22,%22groupByFields%22:%5B%5D,%22minAlignmentPeriod%22:%2260s%22,%22perSeriesAligner%22:%22ALIGN_RATE%22,%22secondaryCrossSeriesReducer%22:%22REDUCE_NONE%22,%22secondaryGroupByFields%22:%5B%5D%7D%7D%5D,%22options%22:%7B%22mode%22:%22STATS%22%7D,%22y1Axis%22:%7B%22label%22:%22%22,%22scale%22:%22LINEAR%22%7D%7D%7D&project=gitlab-review-apps cpu: 12m # Based on https://console.cloud.google.com/monitoring/metrics-explorer;duration=P14D?pageState=%7B%22xyChart%22:%7B%22constantLines%22:%5B%5D,%22dataSets%22:%5B%7B%22plotType%22:%22LINE%22,%22targetAxis%22:%22Y1%22,%22timeSeriesFilter%22:%7B%22aggregations%22:%5B%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22groupByFields%22:%5B%5D,%22perSeriesAligner%22:%22ALIGN_MEAN%22%7D%5D,%22apiSource%22:%22DEFAULT_CLOUD%22,%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22filter%22:%22metric.type%3D%5C%22kubernetes.io%2Fcontainer%2Fmemory%2Fused_bytes%5C%22%20resource.type%3D%5C%22k8s_container%5C%22%20resource.label.%5C%22container_name%5C%22%3D%5C%22gitlab-shell%5C%22%22,%22groupByFields%22:%5B%5D,%22minAlignmentPeriod%22:%2260s%22,%22perSeriesAligner%22:%22ALIGN_MEAN%22%7D%7D%5D,%22options%22:%7B%22mode%22:%22STATS%22%7D,%22y1Axis%22:%7B%22label%22:%22%22,%22scale%22:%22LINEAR%22%7D%7D%7D&project=gitlab-review-apps - memory: 20Mi + memory: 50Mi limits: - cpu: 90m - memory: 40Mi + cpu: 24m + memory: 100Mi minReplicas: 1 maxReplicas: 1 hpa: diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index e22ae4f51fb..291c40f0f6b 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do include MobileHelpers - let(:user) { create(:user, :no_super_sidebar) } + let(:user) { create(:user) } let(:contributed_project) { create(:project, :public, :repository) } let(:issue_note) { create(:note, project: contributed_project) } @@ -83,7 +83,6 @@ RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do shared_context 'when user page is visited' do before do visit user.username - page.click_link('Overview') wait_for_requests end end diff --git a/spec/features/contextual_sidebar_spec.rb b/spec/features/contextual_sidebar_spec.rb index ab322f18240..dffc87c2028 100644 --- a/spec/features/contextual_sidebar_spec.rb +++ b/spec/features/contextual_sidebar_spec.rb @@ -4,39 +4,19 @@ require 'spec_helper' RSpec.describe 'Contextual sidebar', :js, feature_category: :remote_development do context 'when context is a project' do - let_it_be(:user) { create(:user, :no_super_sidebar) } + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository, namespace: user.namespace) } before do sign_in(user) + visit project_path(project) end - context 'when analyzing the menu' do - before do - visit project_path(project) - end + it 'shows flyout menu on other section on hover' do + expect(page).not_to have_link('Pipelines', href: project_pipelines_path(project)) - it 'shows flyout navs when collapsed or expanded apart from on the active item when expanded', :aggregate_failures do - expect(page).not_to have_selector('.js-sidebar-collapsed') - - find('.rspec-link-pipelines').hover - - expect(page).to have_selector('.is-showing-fly-out') - - find('.rspec-project-link').hover - - expect(page).not_to have_selector('.is-showing-fly-out') - - find('.rspec-toggle-sidebar').click - - find('.rspec-link-pipelines').hover - - expect(page).to have_selector('.is-showing-fly-out') - - find('.rspec-project-link').hover - - expect(page).to have_selector('.is-showing-fly-out') - end + find_button('Build').hover + expect(page).to have_link('Pipelines', href: project_pipelines_path(project)) end end end diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb deleted file mode 100644 index a00666c2376..00000000000 --- a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'The group dashboard', :js, feature_category: :groups_and_projects do - include ExternalAuthorizationServiceHelpers - include Features::TopNavSpecHelpers - - let(:user) { create(:user, :no_super_sidebar) } - - before do - sign_in user - end - - describe 'The top navigation' do - it 'has all the expected links' do - visit dashboard_groups_path - - open_top_nav - - within_top_nav do - expect(page).to have_button('Projects') - expect(page).to have_button('Groups') - expect(page).to have_link('Your work') - expect(page).to have_link('Explore') - end - end - - it 'hides some links when an external authorization service is enabled' do - enable_external_authorization_service_check - visit dashboard_groups_path - - open_top_nav - - within_top_nav do - expect(page).to have_button('Projects') - expect(page).to have_button('Groups') - expect(page).to have_link('Your work') - expect(page).to have_link('Explore') - end - end - end -end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 501405c5662..d34f8cb3e18 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, feature_category: :team_planning do - let(:user) { create(:user, :no_super_sidebar) } + let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, source_project: project) } @@ -17,33 +17,29 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, it 'reflects dashboard issues count', :js do visit issues_path - expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1) + expect_issue_count(1) issue.update!(assignees: []) - Users::AssignedIssuesCountService.new(current_user: user).delete_cache + user.invalidate_cache_counts - travel_to(3.minutes.from_now) do - visit issues_path + visit issues_path - expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0) - end + expect_issue_count(0) end it 'reflects dashboard merge requests count', :js do visit merge_requests_path - expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) + expect_merge_request_count(1) merge_request.update!(assignees: []) user.invalidate_cache_counts - travel_to(3.minutes.from_now) do - visit merge_requests_path + visit merge_requests_path - expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) - end + expect_merge_request_count(0) end def issues_path @@ -54,11 +50,21 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, merge_requests_dashboard_path(assignee_username: user.username) end - def expect_counters(issuable_type, count, badge_label) + def expect_issue_count(count) dashboard_count = find('.gl-tabs-nav li a.active') + expect(dashboard_count).to have_content(count) + within_testid('super-sidebar') do + expect(page).to have_link("Issues #{count}") + end + end + + def expect_merge_request_count(count) + dashboard_count = find('.gl-tabs-nav li a.active') expect(dashboard_count).to have_content(count) - expect(page).to have_css(".dashboard-shortcuts-#{issuable_type}", visible: :all, text: count) - expect(page).to have_css("span[aria-label='#{badge_label}']", visible: :all, text: count) + + within_testid('super-sidebar') do + expect(page).to have_button("Merge requests #{count}") + end end end diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb index d1fde4ba0f0..5ab5a27171c 100644 --- a/spec/features/dashboard/snippets_spec.rb +++ b/spec/features/dashboard/snippets_spec.rb @@ -55,7 +55,7 @@ RSpec.describe 'Dashboard snippets', :js, feature_category: :source_code_managem it 'shows documentation button in main comment area' do parent_element = page.find('.row.empty-state') - expect(parent_element).to have_link('Documentation', href: help_page_path('user/snippets.md')) + expect(parent_element).to have_link('Documentation', href: help_page_path('user/snippets')) end end diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb index dfafacf48e2..7d6d1648ff5 100644 --- a/spec/features/global_search_spec.rb +++ b/spec/features/global_search_spec.rb @@ -3,9 +3,7 @@ require 'spec_helper' RSpec.describe 'Global search', :js, feature_category: :global_search do - include AfterNextHelpers - - let_it_be(:user) { create(:user, :no_super_sidebar) } + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, namespace: user.namespace) } before do @@ -18,17 +16,18 @@ RSpec.describe 'Global search', :js, feature_category: :global_search do visit dashboard_projects_path end - it 'renders updated search bar' do - expect(page).to have_no_selector('.search-form') - expect(page).to have_selector('#js-header-search') + it 'renders search button' do + expect(page).to have_button('Search or go to…') end - it 'focuses search input when shortcut "s" is pressed' do - expect(page).not_to have_selector('#search:focus') + it 'opens search modal when shortcut "s" is pressed' do + search_selector = 'input[type="search"]:focus' + + expect(page).not_to have_selector(search_selector) find('body').native.send_key('s') - expect(page).to have_selector('#search:focus') + expect(page).to have_selector(search_selector) end end end diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index bbb7d322b9a..0a830e6715c 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -26,8 +26,10 @@ RSpec.describe 'Group merge requests page', feature_category: :code_review_workf expect(page).not_to have_content(issuable_archived.title) end - it 'ignores archived merge request count badges in navbar' do - expect(first(:link, text: 'Merge requests').find('.badge').text).to eq("1") + it 'ignores archived merge request count badges in navbar', :js do + within_testid('super-sidebar') do + expect(find_link(text: 'Merge requests').find('.badge').text).to eq("1") + end end it 'ignores archived merge request count badges in state-filters' do diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb index 1d39f749ca7..1855379825b 100644 --- a/spec/features/monitor_sidebar_link_spec.rb +++ b/spec/features/monitor_sidebar_link_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' -RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category: :shared do +RSpec.describe 'Monitor dropdown sidebar', :js, feature_category: :shared do let_it_be_with_reload(:project) { create(:project, :internal, :repository) } - let_it_be(:user) { create(:user, :no_super_sidebar) } + let_it_be(:user) { create(:user) } let(:role) { nil } @@ -13,7 +13,7 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category sign_in(user) end - shared_examples 'shows Monitor menu based on the access level' do + shared_examples 'shows common Monitor menu item based on the access level' do using RSpec::Parameterized::TableSyntax let(:enabled) { Featurable::PRIVATE } @@ -30,10 +30,14 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category visit project_issues_path(project) - if render - expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') - else - expect(page).not_to have_selector('a.shortcuts-monitor') + click_button('Monitor') + + within_testid('super-sidebar') do + if render + expect(page).to have_link('Incidents') + else + expect(page).not_to have_link('Incidents', visible: :all) + end end end end @@ -44,32 +48,35 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category before do project.project_feature.update_attribute(:monitor_access_level, access_level) + visit project_issues_path(project) + click_button('Monitor') end - it 'has the correct `Monitor` menu items', :aggregate_failures do - visit project_issues_path(project) - expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') + it 'has the correct `Monitor` and `Operate` menu items' do expect(page).to have_link('Incidents', href: project_incidents_path(project)) + + click_button('Operate') + expect(page).to have_link('Environments', href: project_environments_path(project)) - expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) - expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) - expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) + expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project), visible: :all) + expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project), visible: :all) + expect(page).not_to have_link('Kubernetes clusters', href: project_clusters_path(project), visible: :all) end context 'when monitor project feature is PRIVATE' do let(:access_level) { ProjectFeature::PRIVATE } - it 'does not show the `Monitor` menu' do - expect(page).not_to have_selector('a.shortcuts-monitor') + it 'does not show common items of the `Monitor` menu' do + expect(page).not_to have_link('Error Tracking', href: project_incidents_path(project), visible: :all) end end context 'when monitor project feature is DISABLED' do let(:access_level) { ProjectFeature::DISABLED } - it 'does not show the `Monitor` menu' do - expect(page).not_to have_selector('a.shortcuts-monitor') + it 'does not show the `Incidents` menu' do + expect(page).not_to have_link('Error Tracking', href: project_incidents_path(project), visible: :all) end end end @@ -77,63 +84,86 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category context 'when user has guest role' do let(:role) { :guest } - it 'has the correct `Monitor` menu items' do + it 'has the correct `Monitor` and `Operate` menu items' do visit project_issues_path(project) - expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') + + click_button('Monitor') + expect(page).to have_link('Incidents', href: project_incidents_path(project)) + + click_button('Operate') + expect(page).to have_link('Environments', href: project_environments_path(project)) - expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) - expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) - expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) + expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project), visible: :all) + expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project), visible: :all) + expect(page).not_to have_link('Kubernetes clusters', href: project_clusters_path(project), visible: :all) end - it_behaves_like 'shows Monitor menu based on the access level' + it_behaves_like 'shows common Monitor menu item based on the access level' end context 'when user has reporter role' do let(:role) { :reporter } - it 'has the correct `Monitor` menu items' do + it 'has the correct `Monitor` and `Operate` menu items' do visit project_issues_path(project) + + click_button('Monitor') + expect(page).to have_link('Incidents', href: project_incidents_path(project)) - expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) - expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) - expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) + click_button('Operate') + + expect(page).to have_link('Environments', href: project_environments_path(project)) + + expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project), visible: :all) + expect(page).not_to have_link('Kubernetes clusters', href: project_clusters_path(project), visible: :all) end - it_behaves_like 'shows Monitor menu based on the access level' + it_behaves_like 'shows common Monitor menu item based on the access level' end context 'when user has developer role' do let(:role) { :developer } - it 'has the correct `Monitor` menu items' do + it 'has the correct `Monitor` and `Operate` menu items' do visit project_issues_path(project) + + click_button('Monitor') + expect(page).to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project)) - expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) - expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) + + click_button('Operate') + + expect(page).to have_link('Environments', href: project_environments_path(project)) + expect(page).to have_link('Kubernetes clusters', href: project_clusters_path(project)) end - it_behaves_like 'shows Monitor menu based on the access level' + it_behaves_like 'shows common Monitor menu item based on the access level' end context 'when user has maintainer role' do let(:role) { :maintainer } - it 'has the correct `Monitor` menu items' do + it 'has the correct `Monitor` and `Operate` menu items' do visit project_issues_path(project) + + click_button('Monitor') + expect(page).to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project)) - expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) - expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) + + click_button('Operate') + + expect(page).to have_link('Environments', href: project_environments_path(project)) + expect(page).to have_link('Kubernetes clusters', href: project_clusters_path(project)) end - it_behaves_like 'shows Monitor menu based on the access level' + it_behaves_like 'shows common Monitor menu item based on the access level' end end diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb index b52f66cfcee..15ab79684d9 100644 --- a/spec/features/profiles/two_factor_auths_spec.rb +++ b/spec/features/profiles/two_factor_auths_spec.rb @@ -59,7 +59,7 @@ RSpec.describe 'Two factor auths', feature_category: :user_profile do fill_in 'pin_code', with: '123' click_button 'Register with two-factor app' - expect(page).to have_link('Try the troubleshooting steps here.', href: help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting')) + expect(page).to have_link('Try the troubleshooting steps here.', href: help_page_path('user/profile/account/two_factor_authentication', anchor: 'troubleshooting')) end end diff --git a/spec/features/projects/files/user_find_file_spec.rb b/spec/features/projects/files/user_find_file_spec.rb index 005a870bea0..b6e739e8082 100644 --- a/spec/features/projects/files/user_find_file_spec.rb +++ b/spec/features/projects/files/user_find_file_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'User find project file', feature_category: :groups_and_projects do include ListboxHelpers - let(:user) { create :user, :no_super_sidebar } + let(:user) { create :user } let(:project) { create :project, :repository } before do @@ -15,29 +15,25 @@ RSpec.describe 'User find project file', feature_category: :groups_and_projects visit project_tree_path(project, project.repository.root_ref) end - def active_main_tab - find('.sidebar-top-level-items > li.active') - end - def find_file(text) fill_in 'file_find', with: text end def ref_selector_dropdown - find('.gl-button-text') + find('.ref-selector .gl-button-text') end it 'navigates to find file by shortcut', :js do find('body').native.send_key('t') - expect(active_main_tab).to have_content('Repository') + expect(page).to have_active_sub_navigation('Repository') expect(page).to have_selector('.file-finder-holder', count: 1) end - it 'navigates to find file' do + it 'navigates to find file', :js do click_link 'Find file' - expect(active_main_tab).to have_content('Repository') + expect(page).to have_active_sub_navigation('Repository') expect(page).to have_selector('.file-finder-holder', count: 1) end diff --git a/spec/features/projects/forks/fork_list_spec.rb b/spec/features/projects/forks/fork_list_spec.rb index 86e4e03259e..966147637f5 100644 --- a/spec/features/projects/forks/fork_list_spec.rb +++ b/spec/features/projects/forks/fork_list_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'listing forks of a project', feature_category: :groups_and_proje let(:source) { create(:project, :public, :repository) } let!(:fork) { fork_project(source, nil, repository: true) } - let(:user) { create(:user, :no_super_sidebar) } + let(:user) { create(:user) } before do source.add_maintainer(user) diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index be9e99be36f..30d3303dfbb 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do let(:expected_detached_mr_tag) { 'merge request' } context 'when user is logged in' do - let(:user) { create(:user, :no_super_sidebar) } + let(:user) { create(:user) } before do sign_in(user) @@ -650,7 +650,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do # header expect(page).to have_text("##{pipeline.id}") - expect(page).to have_link(pipeline.user.name, href: user_path(pipeline.user)) + expect(page).to have_link(pipeline.user.name, href: /#{user_path(pipeline.user)}$/) # stages expect(page).to have_text('build') diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb index 12018b4b9d7..e5836739c57 100644 --- a/spec/features/projects/snippets/show_spec.rb +++ b/spec/features/projects/snippets/show_spec.rb @@ -3,35 +3,62 @@ require 'spec_helper' RSpec.describe 'Projects > Snippets > Project snippet', :js, feature_category: :source_code_management do - let_it_be(:user) { create(:user) } + let_it_be(:author) { create(:author) } let_it_be(:project) do - create(:project, creator: user).tap do |p| - p.add_maintainer(user) + create(:project, :public, creator: author).tap do |p| + p.add_maintainer(author) end end - let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) } + let_it_be(:snippet) { create(:project_snippet, :public, :repository, project: project, author: author) } + let(:anchor) { nil } + let(:file_path) { 'files/ruby/popen.rb' } + + def visit_page + visit project_snippet_path(project, snippet, anchor: anchor) + end before do - sign_in(user) + # rubocop: disable RSpec/AnyInstanceOf -- TODO: The usage of let_it_be forces us + allow_any_instance_of(Snippet).to receive(:blobs) + .and_return([snippet.repository.blob_at('master', file_path)]) + # rubocop: enable RSpec/AnyInstanceOf end - it_behaves_like 'show and render proper snippet blob' do - let(:anchor) { nil } + context 'when signed in' do + before do + sign_in(user) + visit_page + end - subject do - visit project_snippet_path(project, snippet, anchor: anchor) + context 'as project member' do + let(:user) { author } - wait_for_requests + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does show New Snippet button' end - end - # it_behaves_like 'showing user status' do - # This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394 + context 'as external user' do + let_it_be(:user) { create(:user, :external) } + + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does not show New Snippet button' + end - it_behaves_like 'does not show New Snippet button' do - let(:file_path) { 'files/ruby/popen.rb' } + context 'as another user' do + let_it_be(:user) { create(:user) } + + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does not show New Snippet button' + end + end + + context 'when unauthenticated' do + before do + visit_page + end - subject { visit project_snippet_path(project, snippet) } + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does not show New Snippet button' end end diff --git a/spec/features/projects/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb index 876444e451a..33153d21575 100644 --- a/spec/features/projects/work_items/work_item_spec.rb +++ b/spec/features/projects/work_items/work_item_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' RSpec.describe 'Work item', :js, feature_category: :team_planning do include ListboxHelpers - let_it_be_with_reload(:user) { create(:user, :no_super_sidebar) } - let_it_be_with_reload(:user2) { create(:user, :no_super_sidebar, name: 'John') } + let_it_be_with_reload(:user) { create(:user) } + let_it_be_with_reload(:user2) { create(:user, name: 'John') } let_it_be(:project) { create(:project, :public) } let_it_be(:work_item) { create(:work_item, project: project) } diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 3f2a71b63dc..8381642cd4d 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -6,8 +6,8 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat include FilteredSearchHelpers let_it_be(:project) { create(:project, :repository) } - let_it_be(:reporter) { create(:user, :no_super_sidebar) } - let_it_be(:developer) { create(:user, :no_super_sidebar) } + let_it_be(:reporter) { create(:user) } + let_it_be(:developer) { create(:user) } let(:user) { reporter } @@ -46,31 +46,34 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat context 'when using the keyboard shortcut' do before do - find('#search') find('body').native.send_keys('s') - wait_for_all_requests end - it 'shows the category search dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/250285' do - expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i) + it 'shows the search modal' do + expect(page).to have_selector(search_modal_results, visible: :visible) end end - context 'when clicking the search field' do + context 'when clicking the search button' do before do - page.find('#search').click + within_testid('super-sidebar') do + click_button "Search or go to…" + end wait_for_all_requests end - it 'shows category search dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/250285' do - expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i) + it 'shows search scope badge' do + fill_in 'search', with: 'text' + within('#super-sidebar-search-modal') do + expect(page).to have_selector('.search-scope-help', text: scope_name) + end end context 'when clicking issues', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332317' do let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } it 'shows assigned issues' do - find('[data-testid="header-search-dropdown-menu"]').click_link('Issues assigned to me') + find(search_modal_results).click_link('Issues assigned to me') expect(page).to have_selector('.issues-list .issue') expect_tokens([assignee_token(user.name)]) @@ -78,7 +81,7 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat end it 'shows created issues' do - find('[data-testid="header-search-dropdown-menu"]').click_link("Issues I've created") + find(search_modal_results).click_link("Issues I've created") expect(page).to have_selector('.issues-list .issue') expect_tokens([author_token(user.name)]) @@ -90,7 +93,7 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) } it 'shows assigned merge requests' do - find('[data-testid="header-search-dropdown-menu"]').click_link('Merge requests assigned to me') + find(search_modal_results).click_link('Merge requests assigned to me') expect(page).to have_selector('.mr-list .merge-request') expect_tokens([assignee_token(user.name)]) @@ -98,7 +101,7 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat end it 'shows created merge requests' do - find('[data-testid="header-search-dropdown-menu"]').click_link("Merge requests I've created") + find(search_modal_results).click_link("Merge requests I've created") expect(page).to have_selector('.mr-list .merge-request') expect_tokens([author_token(user.name)]) @@ -119,7 +122,7 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat context 'when user is in a global scope' do include_examples 'search field examples' do let(:url) { root_path } - let(:scope_name) { 'All GitLab' } + let(:scope_name) { 'in all GitLab' } end it 'displays search options', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/251076' do @@ -136,11 +139,13 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat end it 'displays result counts for all categories' do - expect(page).to have_content('Projects 1') - expect(page).to have_content('Issues 1') - expect(page).to have_content('Merge requests 0') - expect(page).to have_content('Milestones 0') - expect(page).to have_content('Users 0') + within_testid('super-sidebar') do + expect(page).to have_link('Projects 1') + expect(page).to have_link('Issues 1') + expect(page).to have_link('Merge requests 0') + expect(page).to have_link('Milestones 0') + expect(page).to have_link('Users 0') + end end end end @@ -162,9 +167,8 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat it 'displays search options' do fill_in_search('test') - expect(page).to have_selector(scoped_search_link('test', search_code: true)) expect(page).to have_selector(scoped_search_link('test', group_id: group.id, search_code: true)) - expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id, search_code: true)) + expect(page).to have_selector(scoped_search_link('test', search_code: true)) end end @@ -176,26 +180,25 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat it 'displays search options' do fill_in_search('test') - sleep 0.5 - expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master')) + expect(page).not_to have_selector(scoped_search_link('test', search_code: true, group_id: project.namespace_id, repository_ref: 'master')) - expect(page).to have_selector(scoped_search_link('test', search_code: true, project_id: project.id, repository_ref: 'master')) + expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master')) end it 'displays a link to project merge requests' do fill_in_search('Merge') - within(dashboard_search_options_popup_menu) do - expect(page).to have_text('Merge requests') + within(search_modal_results) do + expect(page).to have_link('Merge requests') end end it 'does not display a link to project feature flags' do fill_in_search('Feature') - within(dashboard_search_options_popup_menu) do - expect(page).to have_text('Feature in all GitLab') - expect(page).to have_no_text('Feature Flags') + within(search_modal_results) do + expect(page).to have_link('in all GitLab Feature') + expect(page).not_to have_link('Feature Flags') end end @@ -205,8 +208,8 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat it 'displays a link to project feature flags' do fill_in_search('Feature') - within(dashboard_search_options_popup_menu) do - expect(page).to have_text('Feature Flags') + within(search_modal_results) do + expect(page).to have_link('Feature Flags') end end end @@ -228,8 +231,8 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat it 'displays search options' do fill_in_search('test') + expect(page).to have_selector(scoped_search_link('test')) - expect(page).to have_selector(scoped_search_link('test', group_id: group.id)) expect(page).not_to have_selector(scoped_search_link('test', project_id: project.id)) end end @@ -253,7 +256,6 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat fill_in_search('test') expect(page).to have_selector(scoped_search_link('test')) - expect(page).to have_selector(scoped_search_link('test', group_id: subgroup.id)) expect(page).not_to have_selector(scoped_search_link('test', project_id: project.id)) end end @@ -268,10 +270,10 @@ RSpec.describe 'User uses header search field', :js, :disable_rate_limiter, feat href.concat("&search_code=true") if search_code href.concat("&repository_ref=#{repository_ref}") if repository_ref - "[data-testid='header-search-dropdown-menu'] a[href='#{href}']" + ".global-search-results a[href='#{href}']" end - def dashboard_search_options_popup_menu - "[data-testid='header-search-dropdown-menu'] .header-search-dropdown-content" + def search_modal_results + ".global-search-results" end end diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb index ac0682d9e06..03f46ea0122 100644 --- a/spec/features/snippets/show_spec.rb +++ b/spec/features/snippets/show_spec.rb @@ -3,45 +3,63 @@ require 'spec_helper' RSpec.describe 'Snippet', :js, feature_category: :source_code_management do - let_it_be(:user) { create(:user) } - let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: user) } + let_it_be(:owner) { create(:user) } + let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: owner) } + let(:anchor) { nil } + let(:file_path) { 'files/ruby/popen.rb' } - it_behaves_like 'show and render proper snippet blob' do - let(:anchor) { nil } + before do + # rubocop: disable RSpec/AnyInstanceOf -- TODO: The usage of let_it_be forces us + allow_any_instance_of(Snippet).to receive(:blobs) + .and_return([snippet.repository.blob_at('master', file_path)]) + # rubocop: enable RSpec/AnyInstanceOf + end - subject do - visit snippet_path(snippet, anchor: anchor) + def visit_page + visit snippet_path(snippet, anchor: anchor) + end - wait_for_requests + context 'when signed in' do + before do + sign_in(user) + visit_page end - end - # it_behaves_like 'showing user status' do - # This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394 + context 'as the snippet owner' do + let(:user) { owner } - it_behaves_like 'does not show New Snippet button' do - let(:file_path) { 'files/ruby/popen.rb' } + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does show New Snippet button' + it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets + end - subject { visit snippet_path(snippet) } - end + context 'as external user' do + let_it_be(:user) { create(:user, :external) } - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does not show New Snippet button' + it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets + end - context 'when unauthenticated' do - it 'shows the "Explore" sidebar' do - visit snippet_path(snippet) + context 'as another user' do + let_it_be(:user) { create(:user) } - expect(page).to have_css('#super-sidebar-context-header', text: 'Explore') + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does show New Snippet button' + it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets end end - context 'when authenticated as a different user' do - let_it_be(:different_user) { create(:user, :no_super_sidebar) } - + context 'when unauthenticated' do before do - sign_in(different_user) + visit_page end - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets + it_behaves_like 'show and render proper snippet blob' + it_behaves_like 'does not show New Snippet button' + + it 'shows the "Explore" sidebar' do + expect(page).to have_css('#super-sidebar-context-header', text: 'Explore') + end end end diff --git a/spec/frontend/environments/graphql/resolvers/flux_spec.js b/spec/frontend/environments/graphql/resolvers/flux_spec.js index aa6f9e120f0..ea733c6e0e8 100644 --- a/spec/frontend/environments/graphql/resolvers/flux_spec.js +++ b/spec/frontend/environments/graphql/resolvers/flux_spec.js @@ -1,4 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; +import { WatchApi } from '@gitlab/cluster-client'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK, HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status'; import { resolvers } from '~/environments/graphql/resolvers'; @@ -27,114 +28,306 @@ describe('~/frontend/environments/graphql/resolvers', () => { }); describe('fluxKustomizationStatus', () => { + const client = { writeQuery: jest.fn() }; const endpoint = `${configuration.basePath}/apis/kustomize.toolkit.fluxcd.io/v1beta1/namespaces/${namespace}/kustomizations/${environmentName}`; const fluxResourcePath = 'kustomize.toolkit.fluxcd.io/v1beta1/namespaces/my-namespace/kustomizations/app'; const endpointWithFluxResourcePath = `${configuration.basePath}/apis/${fluxResourcePath}`; - it('should request Flux Kustomizations for the provided namespace via the Kubernetes API if the fluxResourcePath is not specified', async () => { - mock - .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) - .reply(HTTP_STATUS_OK, { - status: { conditions: fluxKustomizationsMock }, - }); + describe('when k8sWatchApi feature is disabled', () => { + it('should request Flux Kustomizations for the provided namespace via the Kubernetes API if the fluxResourcePath is not specified', async () => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, { + status: { conditions: fluxKustomizationsMock }, + }); + + const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); - const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus(null, { - configuration, - namespace, - environmentName, + expect(fluxKustomizationStatus).toEqual(fluxKustomizationsMock); }); + it('should request Flux Kustomization for the provided fluxResourcePath via the Kubernetes API', async () => { + mock + .onGet(endpointWithFluxResourcePath, { + withCredentials: true, + headers: configuration.baseOptions.headers, + }) + .reply(HTTP_STATUS_OK, { + status: { conditions: fluxKustomizationsMock }, + }); - expect(fluxKustomizationStatus).toEqual(fluxKustomizationsMock); - }); - it('should request Flux Kustomization for the provided fluxResourcePath via the Kubernetes API', async () => { - mock - .onGet(endpointWithFluxResourcePath, { - withCredentials: true, - headers: configuration.baseOptions.headers, - }) - .reply(HTTP_STATUS_OK, { - status: { conditions: fluxKustomizationsMock }, - }); + const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + fluxResourcePath, + }, + { client }, + ); - const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus(null, { - configuration, - namespace, - environmentName, - fluxResourcePath, + expect(fluxKustomizationStatus).toEqual(fluxKustomizationsMock); }); + it('should throw an error if the API call fails', async () => { + const apiError = 'Invalid credentials'; + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.base }) + .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError }); + + const fluxKustomizationsError = mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); - expect(fluxKustomizationStatus).toEqual(fluxKustomizationsMock); + await expect(fluxKustomizationsError).rejects.toThrow(apiError); + }); }); - it('should throw an error if the API call fails', async () => { - const apiError = 'Invalid credentials'; - mock - .onGet(endpoint, { withCredentials: true, headers: configuration.base }) - .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError }); - const fluxKustomizationsError = mockResolvers.Query.fluxKustomizationStatus(null, { - configuration, - namespace, - environmentName, + describe('when k8sWatchApi feature is enabled', () => { + const mockWatcher = WatchApi.prototype; + const mockKustomizationStatusFn = jest.fn().mockImplementation(() => { + return Promise.resolve(mockWatcher); + }); + const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => { + if (eventName === 'data') { + callback(fluxKustomizationsMock); + } + }); + const resourceName = 'custom-resource'; + + beforeEach(() => { + gon.features = { k8sWatchApi: true }; + jest.spyOn(mockWatcher, 'subscribeToStream').mockImplementation(mockKustomizationStatusFn); + jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn); + }); + + describe('when the Kustomization data is present', () => { + beforeEach(() => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, { + metadata: { name: resourceName }, + status: { conditions: fluxKustomizationsMock }, + }); + }); + it('should watch Kustomization by the metadata name from the cluster_client library when the data is present', async () => { + await mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(mockKustomizationStatusFn).toHaveBeenCalledWith( + `/apis/kustomize.toolkit.fluxcd.io/v1beta1/namespaces/${namespace}/kustomizations`, + { + watch: true, + fieldSelector: `metadata.name=${decodeURIComponent(resourceName)}`, + }, + ); + }); + + it('should return data when received from the library', async () => { + const kustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(kustomizationStatus).toEqual(fluxKustomizationsMock); + }); }); - await expect(fluxKustomizationsError).rejects.toThrow(apiError); + it('should not watch Kustomization by the metadata name from the cluster_client library when the data is not present', async () => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, {}); + + await mockResolvers.Query.fluxKustomizationStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(mockKustomizationStatusFn).not.toHaveBeenCalled(); + }); }); }); describe('fluxHelmReleaseStatus', () => { + const client = { writeQuery: jest.fn() }; const endpoint = `${configuration.basePath}/apis/helm.toolkit.fluxcd.io/v2beta1/namespaces/${namespace}/helmreleases/${environmentName}`; const fluxResourcePath = 'helm.toolkit.fluxcd.io/v2beta1/namespaces/my-namespace/helmreleases/app'; const endpointWithFluxResourcePath = `${configuration.basePath}/apis/${fluxResourcePath}`; - it('should request Flux Helm Releases via the Kubernetes API', async () => { - mock - .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) - .reply(HTTP_STATUS_OK, { - status: { conditions: fluxKustomizationsMock }, - }); + describe('when k8sWatchApi feature is disabled', () => { + it('should request Flux Helm Releases via the Kubernetes API', async () => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, { + status: { conditions: fluxKustomizationsMock }, + }); + + const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); - const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus(null, { - configuration, - namespace, - environmentName, + expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock); }); + it('should request Flux HelmRelease for the provided fluxResourcePath via the Kubernetes API', async () => { + mock + .onGet(endpointWithFluxResourcePath, { + withCredentials: true, + headers: configuration.baseOptions.headers, + }) + .reply(HTTP_STATUS_OK, { + status: { conditions: fluxKustomizationsMock }, + }); - expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock); - }); - it('should request Flux HelmRelease for the provided fluxResourcePath via the Kubernetes API', async () => { - mock - .onGet(endpointWithFluxResourcePath, { - withCredentials: true, - headers: configuration.baseOptions.headers, - }) - .reply(HTTP_STATUS_OK, { - status: { conditions: fluxKustomizationsMock }, - }); + const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + fluxResourcePath, + }, + { client }, + ); - const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus(null, { - configuration, - namespace, - environmentName, - fluxResourcePath, + expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock); }); + it('should throw an error if the API call fails', async () => { + const apiError = 'Invalid credentials'; + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.base }) + .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError }); + + const fluxHelmReleasesError = mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); - expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock); + await expect(fluxHelmReleasesError).rejects.toThrow(apiError); + }); }); - it('should throw an error if the API call fails', async () => { - const apiError = 'Invalid credentials'; - mock - .onGet(endpoint, { withCredentials: true, headers: configuration.base }) - .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError }); - const fluxHelmReleasesError = mockResolvers.Query.fluxHelmReleaseStatus(null, { - configuration, - namespace, - environmentName, + describe('when k8sWatchApi feature is enabled', () => { + const mockWatcher = WatchApi.prototype; + const mockHelmReleaseStatusFn = jest.fn().mockImplementation(() => { + return Promise.resolve(mockWatcher); + }); + const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => { + if (eventName === 'data') { + callback(fluxKustomizationsMock); + } + }); + const resourceName = 'custom-resource'; + + beforeEach(() => { + gon.features = { k8sWatchApi: true }; + jest.spyOn(mockWatcher, 'subscribeToStream').mockImplementation(mockHelmReleaseStatusFn); + jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn); + }); + + describe('when the HelmRelease data is present', () => { + beforeEach(() => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, { + metadata: { name: resourceName }, + status: { conditions: fluxKustomizationsMock }, + }); + }); + it('should watch HelmRelease by the metadata name from the cluster_client library when the data is present', async () => { + await mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(mockHelmReleaseStatusFn).toHaveBeenCalledWith( + `/apis/helm.toolkit.fluxcd.io/v2beta1/namespaces/${namespace}/helmreleases`, + { + watch: true, + fieldSelector: `metadata.name=${decodeURIComponent(resourceName)}`, + }, + ); + }); + + it('should return data when received from the library', async () => { + const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock); + }); }); - await expect(fluxHelmReleasesError).rejects.toThrow(apiError); + it('should not watch Kustomization by the metadata name from the cluster_client library when the data is not present', async () => { + mock + .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers }) + .reply(HTTP_STATUS_OK, {}); + + await mockResolvers.Query.fluxHelmReleaseStatus( + null, + { + configuration, + namespace, + environmentName, + }, + { client }, + ); + + expect(mockHelmReleaseStatusFn).not.toHaveBeenCalled(); + }); }); }); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js index da86d5443bf..ee7164515f6 100644 --- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js @@ -133,6 +133,8 @@ describe('Source Viewer component', () => { expect(blameDataQueryHandlerSuccess).toHaveBeenCalledWith( expect.objectContaining({ fromLine: 1, toLine: 70 }), ); + + expect(findChunks().at(0).props('isHighlighted')).toBe(true); }); it('does not render a Blame component when `showBlame: false`', async () => { diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 3e95cb25b12..bbbb2dd4d71 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -755,14 +755,6 @@ RSpec.describe ApplicationHelper do it { is_expected.not_to include('with-top-bar') } end end - - describe 'logged-out-marketing-header' do - before do - allow(helper).to receive(:current_user).and_return(nil) - end - - it { is_expected.not_to include('logged-out-marketing-header') } - end end describe '#dispensable_render' do diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb index 18a978f6921..3e0fc1ffcb7 100644 --- a/spec/helpers/nav_helper_spec.rb +++ b/spec/helpers/nav_helper_spec.rb @@ -160,24 +160,6 @@ RSpec.describe NavHelper, feature_category: :navigation do end end - shared_examples 'anonymous show_super_sidebar is supposed to' do - before do - stub_feature_flags(super_sidebar_logged_out: feature_flag) - end - - context 'when super_sidebar_logged_out feature flag is disabled' do - let(:feature_flag) { false } - - specify { expect(subject).to eq false } - end - - context 'when super_sidebar_logged_out feature flag is enabled' do - let(:feature_flag) { true } - - specify { expect(subject).to eq true } - end - end - context 'without a user' do context 'with current_user (nil) as a default' do before do @@ -186,13 +168,13 @@ RSpec.describe NavHelper, feature_category: :navigation do subject { helper.show_super_sidebar? } - it_behaves_like 'anonymous show_super_sidebar is supposed to' + specify { expect(subject).to eq true } end context 'with nil provided as an argument' do subject { helper.show_super_sidebar?(nil) } - it_behaves_like 'anonymous show_super_sidebar is supposed to' + specify { expect(subject).to eq true } end end diff --git a/spec/lib/sidebars/projects/menus/scope_menu_spec.rb b/spec/lib/sidebars/projects/menus/scope_menu_spec.rb index 1c2d159950a..108a98e28a4 100644 --- a/spec/lib/sidebars/projects/menus/scope_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/scope_menu_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Sidebars::Projects::Menus::ScopeMenu, feature_category: :navigati describe '#container_html_options' do subject { described_class.new(context).container_html_options } - specify { is_expected.to match(hash_including(class: 'shortcuts-project rspec-project-link')) } + specify { is_expected.to match(hash_including(class: 'shortcuts-project')) } end describe '#extra_nav_link_html_options' do diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb index 66da0ab2eec..dd5ce63876e 100644 --- a/spec/support/helpers/search_helpers.rb +++ b/spec/support/helpers/search_helpers.rb @@ -2,10 +2,10 @@ module SearchHelpers def fill_in_search(text) - page.within('.header-search') do - find('#search').click - fill_in 'search', with: text + within_testid('super-sidebar') do + click_button "Search or go to…" end + fill_in 'search', with: text wait_for_all_requests end diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index b36f67be55e..6f34bee3bea 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -4011,7 +4011,6 @@ - './spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb' - './spec/features/projects/show/user_uploads_files_spec.rb' - './spec/features/projects/snippets/create_snippet_spec.rb' -- './spec/features/projects/snippets/show_spec.rb' - './spec/features/projects/snippets/user_comments_on_snippet_spec.rb' - './spec/features/projects/snippets/user_deletes_snippet_spec.rb' - './spec/features/projects/snippets/user_updates_snippet_spec.rb' @@ -4082,7 +4081,6 @@ - './spec/features/snippets/private_snippets_spec.rb' - './spec/features/snippets/public_snippets_spec.rb' - './spec/features/snippets/search_snippets_spec.rb' -- './spec/features/snippets/show_spec.rb' - './spec/features/snippets/spam_snippets_spec.rb' - './spec/features/snippets_spec.rb' - './spec/features/snippets/user_creates_snippet_spec.rb' diff --git a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb index 58bf461c733..d410653ca43 100644 --- a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb +++ b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb @@ -4,8 +4,8 @@ RSpec.shared_examples 'project features apply to issuables' do |klass| let(:described_class) { klass } let(:group) { create(:group) } - let(:user_in_group) { create(:group_member, :developer, user: create(:user, :no_super_sidebar), group: group ).user } - let(:user_outside_group) { create(:user, :no_super_sidebar) } + let(:user_in_group) { create(:group_member, :developer, user: create(:user), group: group ).user } + let(:user_outside_group) { create(:user) } let(:project) { create(:project, :public, project_args) } diff --git a/spec/support/shared_examples/features/snippets_shared_examples.rb b/spec/support/shared_examples/features/snippets_shared_examples.rb index 383f81d048f..0f830fa125a 100644 --- a/spec/support/shared_examples/features/snippets_shared_examples.rb +++ b/spec/support/shared_examples/features/snippets_shared_examples.rb @@ -52,30 +52,24 @@ RSpec.shared_examples 'tabs with counts' do end RSpec.shared_examples 'does not show New Snippet button' do - let(:user) { create(:user, :external, :no_super_sidebar) } - specify do - sign_in(user) - - subject - - wait_for_requests - + expect(page).to have_link(text: "$#{snippet.id}") expect(page).not_to have_link('New snippet') end end -RSpec.shared_examples 'show and render proper snippet blob' do - before do - allow_any_instance_of(Snippet).to receive(:blobs).and_return([snippet.repository.blob_at('master', file_path)]) +RSpec.shared_examples 'does show New Snippet button' do + specify do + expect(page).to have_link(text: "$#{snippet.id}") + expect(page).to have_link('New snippet') end +end +RSpec.shared_examples 'show and render proper snippet blob' do context 'Ruby file' do let(:file_path) { 'files/ruby/popen.rb' } it 'displays the blob' do - subject - aggregate_failures do # shows highlighted Ruby code expect(page).to have_content("require 'fileutils'") @@ -99,10 +93,6 @@ RSpec.shared_examples 'show and render proper snippet blob' do let(:file_path) { 'files/markdown/ruby-style-guide.md' } context 'visiting directly' do - before do - subject - end - it 'displays the blob using the rich viewer' do aggregate_failures do # hides the simple viewer @@ -171,8 +161,6 @@ RSpec.shared_examples 'show and render proper snippet blob' do let(:anchor) { 'LC1' } it 'displays the blob using the simple viewer' do - subject - aggregate_failures do # hides the rich viewer expect(page).to have_selector('.blob-viewer[data-type="simple"]') 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 94db1c55a90..01674e941d5 100644 --- a/spec/support/shared_examples/features/work_items_shared_examples.rb +++ b/spec/support/shared_examples/features/work_items_shared_examples.rb @@ -416,7 +416,7 @@ RSpec.shared_examples 'work items todos' do expect(page).to have_button s_('WorkItem|Mark as done') - page.within ".header-content span[aria-label='#{_('Todos count')}']" do + within_testid('todos-shortcut-button') do expect(page).to have_content '1' end end @@ -426,7 +426,9 @@ RSpec.shared_examples 'work items todos' do click_button s_('WorkItem|Mark as done') expect(page).to have_button s_('WorkItem|Add a to do') - expect(page).to have_selector(".header-content span[aria-label='#{_('Todos count')}']", visible: :hidden) + within_testid('todos-shortcut-button') do + expect(page).to have_content("") + end end end diff --git a/spec/views/layouts/application.html.haml_spec.rb b/spec/views/layouts/application.html.haml_spec.rb index 825e295b73d..20bef2a3685 100644 --- a/spec/views/layouts/application.html.haml_spec.rb +++ b/spec/views/layouts/application.html.haml_spec.rb @@ -80,7 +80,6 @@ RSpec.describe 'layouts/application' do before do allow(view).to receive(:current_user).and_return(nil) allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(nil)) - Feature.enable(:super_sidebar_logged_out) end it 'renders the new marketing header for logged-out users' do diff --git a/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb b/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb index f81e8c5badf..7f49f96de03 100644 --- a/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb +++ b/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb @@ -5,7 +5,6 @@ require 'spec_helper' RSpec.describe 'layouts/header/_super_sidebar_logged_out', feature_category: :navigation do before do allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(nil)) - Feature.enable(:super_sidebar_logged_out) end context 'on gitlab.com' do diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index 3ec731c8eb7..005283f66da 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'layouts/nav/sidebar/_project', feature_category: :navigation do it 'has a link to the project path' do render - expect(rendered).to have_link(project.name, href: project_path(project), class: %w(shortcuts-project rspec-project-link)) + expect(rendered).to have_link(project.name, href: project_path(project), class: 'shortcuts-project') expect(rendered).to have_selector("[aria-label=\"#{project.name}\"]") end end -- cgit v1.2.3