Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
cd353f0da2
commit
04b866f03b
|
@ -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"
|
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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`;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,12 +4,6 @@ html {
|
|||
&.touch .tooltip {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
&.logged-out-marketing-header {
|
||||
--header-height: 72px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,3 +28,5 @@ module Ci
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Ci::DestroyPipelineService.prepend_mod
|
||||
|
|
|
@ -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 = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.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: '</a>'.html_safe }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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: '<strong>'.html_safe, strong_end: '</strong>'.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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 = '<a href="%{url}" target="_blank">'.html_safe % { url: registration_features_docs_path }
|
||||
|
||||
%div
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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') }
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 = {}) => {
|
|||
'<rootDir>/spec/frontend/__helpers__/html_string_serializer.js',
|
||||
'<rootDir>/spec/frontend/__helpers__/clean_html_element_serializer.js',
|
||||
],
|
||||
roots: [
|
||||
'<rootDir>/app/assets/javascripts/',
|
||||
...extRoots,
|
||||
...(IS_EE ? ['<rootDir>/ee/app/assets/javascripts/', ...extRootsEE] : []),
|
||||
...(IS_JH ? ['<rootDir>/jh/app/assets/javascripts/', ...extRootsJH] : []),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,6 +23,10 @@ module.exports = {
|
|||
moduleNameMapperJH: {
|
||||
'^jh_else_ce_test_helpers(/.*)$': '<rootDir>/jh/spec/frontend_integration/test_helpers$1',
|
||||
},
|
||||
// We need to include spec/frontend in `roots` for the __mocks__ to be found
|
||||
roots: ['<rootDir>/spec/frontend_integration/', '<rootDir>/spec/frontend/'],
|
||||
rootsEE: ['<rootDir>/ee/spec/frontend_integration/'],
|
||||
rootsJH: ['<rootDir>/jh/spec/frontend_integration/'],
|
||||
}),
|
||||
fakeTimers: {
|
||||
enableGlobally: false,
|
||||
|
|
|
@ -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: ['<rootDir>/spec/frontend'],
|
||||
rootsEE: ['<rootDir>/ee/spec/frontend'],
|
||||
rootsJH: ['<rootDir>/jh/spec/frontend'],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
const baseConfig = require('./jest.config.base');
|
||||
|
||||
module.exports = {
|
||||
...baseConfig('spec/frontend', {
|
||||
roots: ['<rootDir>/scripts/lib', '<rootDir>/spec/frontend'],
|
||||
}),
|
||||
testMatch: [],
|
||||
};
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 " \
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
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_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)
|
||||
|
||||
within_testid('super-sidebar') do
|
||||
expect(page).to have_button("Merge requests #{count}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# it_behaves_like 'showing user status' do
|
||||
# This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394
|
||||
context 'when unauthenticated' do
|
||||
before do
|
||||
visit_page
|
||||
end
|
||||
|
||||
it_behaves_like 'does not show New Snippet button' do
|
||||
let(:file_path) { 'files/ruby/popen.rb' }
|
||||
|
||||
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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
context 'as the snippet owner' do
|
||||
let(:user) { owner }
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets
|
||||
end
|
||||
|
||||
context 'as another user' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
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
|
||||
|
||||
# it_behaves_like 'showing user status' do
|
||||
# This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394
|
||||
|
||||
it_behaves_like 'does not show New Snippet button' do
|
||||
let(:file_path) { 'files/ruby/popen.rb' }
|
||||
|
||||
subject { visit snippet_path(snippet) }
|
||||
end
|
||||
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets
|
||||
|
||||
context 'when unauthenticated' do
|
||||
it 'shows the "Explore" sidebar' do
|
||||
visit snippet_path(snippet)
|
||||
before do
|
||||
visit_page
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
context 'when authenticated as a different user' do
|
||||
let_it_be(:different_user) { create(:user, :no_super_sidebar) }
|
||||
|
||||
before do
|
||||
sign_in(different_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_snippets_path, :snippets
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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 },
|
||||
);
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
await expect(fluxKustomizationsError).rejects.toThrow(apiError);
|
||||
});
|
||||
});
|
||||
|
||||
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)}`,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus(null, {
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
});
|
||||
it('should return data when received from the library', async () => {
|
||||
const kustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus(
|
||||
null,
|
||||
{
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
},
|
||||
{ client },
|
||||
);
|
||||
|
||||
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(kustomizationStatus).toEqual(fluxKustomizationsMock);
|
||||
});
|
||||
|
||||
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 });
|
||||
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, {});
|
||||
|
||||
const fluxKustomizationsError = mockResolvers.Query.fluxKustomizationStatus(null, {
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
await mockResolvers.Query.fluxKustomizationStatus(
|
||||
null,
|
||||
{
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
},
|
||||
{ client },
|
||||
);
|
||||
|
||||
expect(mockKustomizationStatusFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await expect(fluxKustomizationsError).rejects.toThrow(apiError);
|
||||
});
|
||||
});
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
await expect(fluxHelmReleasesError).rejects.toThrow(apiError);
|
||||
});
|
||||
});
|
||||
|
||||
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)}`,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus(null, {
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
});
|
||||
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);
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
||||
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 });
|
||||
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, {});
|
||||
|
||||
const fluxHelmReleasesError = mockResolvers.Query.fluxHelmReleaseStatus(null, {
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
await mockResolvers.Query.fluxHelmReleaseStatus(
|
||||
null,
|
||||
{
|
||||
configuration,
|
||||
namespace,
|
||||
environmentName,
|
||||
},
|
||||
{ client },
|
||||
);
|
||||
|
||||
expect(mockHelmReleaseStatusFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await expect(fluxHelmReleasesError).rejects.toThrow(apiError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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"]')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue