Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b0107e8756
commit
840d5ecdbb
|
@ -120,10 +120,10 @@ export default {
|
|||
]),
|
||||
...mapGetters('details', ['stacktrace']),
|
||||
firstReleaseLink() {
|
||||
return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseShortVersion}`;
|
||||
return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseVersion}`;
|
||||
},
|
||||
lastReleaseLink() {
|
||||
return `${this.error.externalBaseUrl}/releases/${this.error.lastReleaseShortVersion}`;
|
||||
return `${this.error.externalBaseUrl}/releases/${this.error.lastReleaseVersion}`;
|
||||
},
|
||||
showStacktrace() {
|
||||
return Boolean(this.stacktrace?.length);
|
||||
|
@ -400,18 +400,18 @@ export default {
|
|||
<icon name="external-link" class="ml-1 flex-shrink-0" />
|
||||
</gl-link>
|
||||
</li>
|
||||
<li v-if="error.firstReleaseShortVersion">
|
||||
<li v-if="error.firstReleaseVersion">
|
||||
<strong class="bold">{{ __('First seen') }}:</strong>
|
||||
<time-ago-tooltip :time="error.firstSeen" />
|
||||
<gl-link :href="firstReleaseLink" target="_blank">
|
||||
<span>{{ __('Release') }}: {{ error.firstReleaseShortVersion.substr(0, 10) }}</span>
|
||||
<span>{{ __('Release') }}: {{ error.firstReleaseVersion }}</span>
|
||||
</gl-link>
|
||||
</li>
|
||||
<li v-if="error.lastReleaseShortVersion">
|
||||
<li v-if="error.lastReleaseVersion">
|
||||
<strong class="bold">{{ __('Last seen') }}:</strong>
|
||||
<time-ago-tooltip :time="error.lastSeen" />
|
||||
<gl-link :href="lastReleaseLink" target="_blank">
|
||||
<span>{{ __('Release') }}: {{ error.lastReleaseShortVersion.substr(0, 10) }}</span>
|
||||
<span>{{ __('Release') }}: {{ error.lastReleaseVersion }}</span>
|
||||
</gl-link>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -18,8 +18,8 @@ query errorDetails($fullPath: ID!, $errorId: ID!) {
|
|||
}
|
||||
externalUrl
|
||||
externalBaseUrl
|
||||
firstReleaseShortVersion
|
||||
lastReleaseShortVersion
|
||||
firstReleaseVersion
|
||||
lastReleaseVersion
|
||||
gitlabCommit
|
||||
gitlabCommitPath
|
||||
gitlabIssuePath
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
<script>
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { GlLink, GlSprintf, GlButton } from '@gitlab/ui';
|
||||
import MrWidgetIcon from './mr_widget_icon.vue';
|
||||
import PipelineTourState from './states/mr_widget_pipeline_tour.vue';
|
||||
import Tracking from '~/tracking';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
const TRACK_LABEL = 'no_pipeline_noticed';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetSuggestPipeline',
|
||||
iconName: 'status_notfound',
|
||||
popoverTarget: 'suggest-popover',
|
||||
popoverContainer: 'suggest-pipeline',
|
||||
trackLabel: 'no_pipeline_noticed',
|
||||
trackLabel: TRACK_LABEL,
|
||||
linkTrackValue: 30,
|
||||
linkTrackEvent: 'click_link',
|
||||
showTrackValue: 10,
|
||||
showTrackEvent: 'click_button',
|
||||
helpContent: s__(
|
||||
`mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd} by simply adding a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust.`,
|
||||
),
|
||||
helpURL: 'https://about.gitlab.com/blog/2019/07/12/guide-to-ci-cd-pipelines/',
|
||||
components: {
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlButton,
|
||||
MrWidgetIcon,
|
||||
PipelineTourState,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
props: {
|
||||
pipelinePath: {
|
||||
type: String,
|
||||
|
@ -31,45 +40,89 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
tracking() {
|
||||
return {
|
||||
label: TRACK_LABEL,
|
||||
property: this.humanAccess,
|
||||
};
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.track();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest gl-mb-3">
|
||||
<mr-widget-icon :name="$options.iconName" />
|
||||
<div :id="$options.popoverTarget">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd}
|
||||
<div class="mr-widget-body mr-pipeline-suggest gl-mb-3">
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<mr-widget-icon :name="$options.iconName" />
|
||||
<div>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd}
|
||||
%{addPipelineLinkStart}Add the .gitlab-ci.yml file%{addPipelineLinkEnd}
|
||||
to create one.`)
|
||||
"
|
||||
>
|
||||
<template #prefixToLink="{content}">
|
||||
"
|
||||
>
|
||||
<template #prefixToLink="{content}">
|
||||
<strong>
|
||||
{{ content }}
|
||||
</strong>
|
||||
</template>
|
||||
<template #addPipelineLink="{content}">
|
||||
<gl-link
|
||||
:href="pipelinePath"
|
||||
class="gl-ml-1"
|
||||
data-testid="add-pipeline-link"
|
||||
:data-track-property="humanAccess"
|
||||
:data-track-value="$options.linkTrackValue"
|
||||
:data-track-event="$options.linkTrackEvent"
|
||||
:data-track-label="$options.trackLabel"
|
||||
>
|
||||
{{ content }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-5 order-md-last col-12 gl-mt-5 mt-md-n3 svg-content svg-225">
|
||||
<img data-testid="pipeline-image" :src="pipelineSvgPath" />
|
||||
</div>
|
||||
<div class="col-md-7 order-md-first col-12">
|
||||
<div class="ml-6 gl-pt-5">
|
||||
<strong>
|
||||
{{ content }}
|
||||
{{ s__('mrWidget|Are you adding technical debt or code vulnerabilities?') }}
|
||||
</strong>
|
||||
</template>
|
||||
<template #addPipelineLink="{content}">
|
||||
<gl-link
|
||||
<p class="gl-mt-2">
|
||||
<gl-sprintf :message="$options.helpContent">
|
||||
<template #link="{ content }">
|
||||
<gl-link
|
||||
data-testid="help"
|
||||
:href="$options.helpURL"
|
||||
target="_blank"
|
||||
class="font-size-inherit"
|
||||
>{{ content }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<gl-button
|
||||
data-testid="ok"
|
||||
category="primary"
|
||||
class="gl-mt-2"
|
||||
variant="info"
|
||||
:href="pipelinePath"
|
||||
class="ml-2 js-add-pipeline-path"
|
||||
:data-track-property="humanAccess"
|
||||
:data-track-value="$options.linkTrackValue"
|
||||
:data-track-event="$options.linkTrackEvent"
|
||||
:data-track-value="$options.showTrackValue"
|
||||
:data-track-event="$options.showTrackEvent"
|
||||
:data-track-label="$options.trackLabel"
|
||||
>
|
||||
{{ content }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<pipeline-tour-state
|
||||
:pipeline-path="pipelinePath"
|
||||
:pipeline-svg-path="pipelineSvgPath"
|
||||
:human-access="humanAccess"
|
||||
:popover-target="$options.popoverTarget"
|
||||
:popover-container="$options.popoverContainer"
|
||||
:track-label="$options.trackLabel"
|
||||
/>
|
||||
{{ __('Show me how to add a pipeline') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
<script>
|
||||
import { GlPopover, GlDeprecatedButton, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import Cookies from 'js-cookie';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import Tracking from '~/tracking';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
||||
const cookieKey = 'suggest_pipeline_dismissed';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetPipelineTour',
|
||||
dismissTrackValue: 20,
|
||||
showTrackValue: 10,
|
||||
trackEvent: 'click_button',
|
||||
helpContent: s__(
|
||||
`mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd}, simply add a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust.`,
|
||||
),
|
||||
helpURL: 'https://about.gitlab.com/blog/2019/07/12/guide-to-ci-cd-pipelines/',
|
||||
components: {
|
||||
GlPopover,
|
||||
GlDeprecatedButton,
|
||||
Icon,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
props: {
|
||||
pipelinePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
pipelineSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
humanAccess: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
popoverTarget: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
popoverContainer: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
trackLabel: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
popoverDismissed: parseBoolean(Cookies.get(cookieKey)),
|
||||
tracking: {
|
||||
label: this.trackLabel,
|
||||
property: this.humanAccess,
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.trackOnShow();
|
||||
},
|
||||
methods: {
|
||||
trackOnShow() {
|
||||
if (!this.popoverDismissed) {
|
||||
this.track();
|
||||
}
|
||||
},
|
||||
dismissPopover() {
|
||||
this.popoverDismissed = true;
|
||||
Cookies.set(cookieKey, this.popoverDismissed, { expires: 365 });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-popover
|
||||
v-if="!popoverDismissed"
|
||||
show
|
||||
:target="popoverTarget"
|
||||
:container="popoverContainer"
|
||||
placement="rightbottom"
|
||||
>
|
||||
<template #title>
|
||||
<button
|
||||
class="btn-blank float-right mt-1"
|
||||
type="button"
|
||||
:aria-label="__('Close')"
|
||||
:data-track-property="humanAccess"
|
||||
:data-track-value="$options.dismissTrackValue"
|
||||
:data-track-event="$options.trackEvent"
|
||||
:data-track-label="trackLabel"
|
||||
@click="dismissPopover"
|
||||
>
|
||||
<icon name="close" aria-hidden="true" />
|
||||
</button>
|
||||
{{ s__('mrWidget|Are you adding technical debt or code vulnerabilities?') }}
|
||||
</template>
|
||||
<div class="svg-content svg-150 pt-1">
|
||||
<img :src="pipelineSvgPath" />
|
||||
</div>
|
||||
<gl-sprintf :message="$options.helpContent">
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="$options.helpURL" target="_blank" class="font-size-inherit">{{
|
||||
content
|
||||
}}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<gl-deprecated-button
|
||||
ref="ok"
|
||||
category="primary"
|
||||
class="mt-2 mb-0"
|
||||
variant="info"
|
||||
block
|
||||
:href="pipelinePath"
|
||||
:data-track-property="humanAccess"
|
||||
:data-track-value="$options.showTrackValue"
|
||||
:data-track-event="$options.trackEvent"
|
||||
:data-track-label="trackLabel"
|
||||
>
|
||||
{{ __('Show me how to add a pipeline') }}
|
||||
</gl-deprecated-button>
|
||||
<gl-deprecated-button
|
||||
ref="no-thanks"
|
||||
category="secondary"
|
||||
class="mt-2 mb-0"
|
||||
variant="info"
|
||||
block
|
||||
:data-track-property="humanAccess"
|
||||
:data-track-value="$options.dismissTrackValue"
|
||||
:data-track-event="$options.trackEvent"
|
||||
:data-track-label="trackLabel"
|
||||
@click="dismissPopover"
|
||||
>
|
||||
{{ __('No thanks') }}
|
||||
</gl-deprecated-button>
|
||||
</gl-popover>
|
||||
</template>
|
|
@ -88,6 +88,7 @@ export default {
|
|||
type="search"
|
||||
class="mb-3"
|
||||
autofocus
|
||||
data-qa-selector="project_search_field"
|
||||
@input="onInput"
|
||||
/>
|
||||
<div class="d-flex flex-column">
|
||||
|
@ -107,6 +108,7 @@ export default {
|
|||
:project="project"
|
||||
:matcher="searchQuery"
|
||||
class="js-project-list-item"
|
||||
data-qa-selector="project_list_item"
|
||||
@click="projectClicked(project)"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import renderKramdownList from './renderers/render_kramdown_list';
|
||||
import renderKramdownText from './renderers/render_kramdown_text';
|
||||
import renderIdentifierText from './renderers/render_identifier_text';
|
||||
import renderEmbeddedRubyText from './renderers/render_embedded_ruby_text';
|
||||
|
||||
const listRenderers = [renderKramdownList];
|
||||
const textRenderers = [renderKramdownText, renderIdentifierText];
|
||||
const textRenderers = [renderKramdownText, renderIdentifierText, renderEmbeddedRubyText];
|
||||
|
||||
const executeRenderer = (renderers, node, context) => {
|
||||
const availableRenderer = renderers.find(renderer => renderer.canRender(node, context));
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { buildUneditableTokens } from './build_uneditable_token';
|
||||
|
||||
const embeddedRubyRegex = /(^<%.+%>$)/;
|
||||
|
||||
const canRender = ({ literal }) => {
|
||||
return embeddedRubyRegex.test(literal);
|
||||
};
|
||||
|
||||
const render = (_, { origin }) => {
|
||||
return buildUneditableTokens(origin());
|
||||
};
|
||||
|
||||
export default { canRender, render };
|
|
@ -33,7 +33,7 @@ const hasExitingPotential = literal => literal.includes(']: ');
|
|||
const hasAdjacentExit = node => {
|
||||
let currentNode = node;
|
||||
|
||||
while (currentNode.next && currentNode.literal !== null) {
|
||||
while (currentNode && currentNode.literal !== null) {
|
||||
if (hasExitingPotential(currentNode.literal)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
$image-widths: 80 130 150 250 306 394 430;
|
||||
$image-widths: 80 130 150 225 250 306 394 430;
|
||||
@each $width in $image-widths {
|
||||
&.svg-#{$width} {
|
||||
img,
|
||||
|
|
|
@ -397,6 +397,16 @@ $mr-widget-min-height: 69px;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mr-pipeline-suggest {
|
||||
border-radius: $border-radius-default;
|
||||
line-height: 20px;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
.circle-icon-container {
|
||||
color: $gl-text-color-quaternary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-help {
|
||||
|
@ -596,26 +606,6 @@ $mr-widget-min-height: 69px;
|
|||
}
|
||||
}
|
||||
|
||||
.mr-pipeline-suggest {
|
||||
flex-wrap: wrap;
|
||||
border-radius: $border-radius-default;
|
||||
padding: $gl-padding;
|
||||
border: 1px solid $border-color;
|
||||
min-height: $mr-widget-min-height;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.circle-icon-container {
|
||||
color: $gl-text-color-quaternary;
|
||||
}
|
||||
|
||||
.popover {
|
||||
z-index: 240;
|
||||
}
|
||||
}
|
||||
|
||||
.card-new-merge-request {
|
||||
.card-header {
|
||||
padding: 5px 10px;
|
||||
|
|
|
@ -6,7 +6,7 @@ class Admin::ServicesController < Admin::ApplicationController
|
|||
before_action :service, only: [:edit, :update]
|
||||
before_action :whitelist_query_limiting, only: [:index]
|
||||
before_action only: :edit do
|
||||
push_frontend_feature_flag(:integration_form_refactor)
|
||||
push_frontend_feature_flag(:integration_form_refactor, default_enabled: true)
|
||||
end
|
||||
|
||||
def index
|
||||
|
|
|
@ -9,7 +9,7 @@ module IntegrationsActions
|
|||
before_action :not_found, unless: :integrations_enabled?
|
||||
before_action :integration, only: [:edit, :update, :test]
|
||||
before_action only: :edit do
|
||||
push_frontend_feature_flag(:integration_form_refactor)
|
||||
push_frontend_feature_flag(:integration_form_refactor, default_enabled: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WikiActions
|
||||
include DiffHelper
|
||||
include SendsBlob
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -11,8 +12,9 @@ module WikiActions
|
|||
before_action :authorize_admin_wiki!, only: :destroy
|
||||
|
||||
before_action :wiki
|
||||
before_action :page, only: [:show, :edit, :update, :history, :destroy]
|
||||
before_action :page, only: [:show, :edit, :update, :history, :destroy, :diff]
|
||||
before_action :load_sidebar, except: [:pages]
|
||||
before_action :set_content_class
|
||||
|
||||
before_action only: [:show, :edit, :update] do
|
||||
@valid_encoding = valid_encoding?
|
||||
|
@ -25,6 +27,8 @@ module WikiActions
|
|||
redirect_to wiki_path(wiki)
|
||||
end
|
||||
end
|
||||
|
||||
helper_method :view_file_button, :diff_file_html_data
|
||||
end
|
||||
|
||||
def new
|
||||
|
@ -136,6 +140,19 @@ module WikiActions
|
|||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def diff
|
||||
return render_404 unless page
|
||||
|
||||
apply_diff_view_cookie!
|
||||
|
||||
@diffs = page.diffs(diff_options)
|
||||
@diff_notes_disabled = true
|
||||
|
||||
render 'shared/wikis/diff'
|
||||
end
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def destroy
|
||||
WikiPages::DestroyService.new(container: container, current_user: current_user).execute(page)
|
||||
|
@ -207,7 +224,7 @@ module WikiActions
|
|||
|
||||
def page_params
|
||||
keys = [:id]
|
||||
keys << :version_id if params[:action] == 'show'
|
||||
keys << :version_id if %w[show diff].include?(params[:action])
|
||||
|
||||
params.values_at(*keys)
|
||||
end
|
||||
|
@ -233,4 +250,25 @@ module WikiActions
|
|||
wiki.repository.blob_at(commit.id, params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_class
|
||||
@content_class = 'limit-container-width' unless fluid_layout # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
end
|
||||
|
||||
# Override CommitsHelper#view_file_button
|
||||
def view_file_button(commit_sha, *args)
|
||||
path = wiki_page_path(wiki, page, version_id: page.version.id)
|
||||
|
||||
helpers.link_to(path, class: 'btn') do
|
||||
helpers.raw(_('View page @ ')) + helpers.content_tag(:span, Commit.truncate_sha(commit_sha), class: 'commit-sha')
|
||||
end
|
||||
end
|
||||
|
||||
# Override DiffHelper#diff_file_html_data
|
||||
def diff_file_html_data(_project, _diff_file_path, diff_commit_id)
|
||||
{
|
||||
blob_diff_path: wiki_page_path(wiki, page, action: :diff, version_id: diff_commit_id),
|
||||
view: diff_view
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update]
|
||||
before_action :redirect_deprecated_prometheus_service, only: [:update]
|
||||
before_action only: :edit do
|
||||
push_frontend_feature_flag(:integration_form_refactor)
|
||||
push_frontend_feature_flag(:integration_form_refactor, default_enabled: true)
|
||||
end
|
||||
|
||||
respond_to :html
|
||||
|
|
|
@ -14,6 +14,8 @@ module Types
|
|||
description: 'Number of lines deleted'
|
||||
field :changes, GraphQL::INT_TYPE, null: false,
|
||||
description: 'Number of lines changed'
|
||||
field :file_count, GraphQL::INT_TYPE, null: false,
|
||||
description: 'Number of files changed'
|
||||
|
||||
def changes
|
||||
object[:additions] + object[:deletions]
|
||||
|
|
|
@ -76,8 +76,14 @@ module Types
|
|||
description: 'Commit the error was last seen'
|
||||
field :first_release_short_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: 'Release version the error was first seen'
|
||||
description: 'Release short version the error was first seen'
|
||||
field :last_release_short_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: 'Release short version the error was last seen'
|
||||
field :first_release_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: 'Release version the error was first seen'
|
||||
field :last_release_version, GraphQL::STRING_TYPE,
|
||||
null: true,
|
||||
description: 'Release version the error was last seen'
|
||||
field :gitlab_commit, GraphQL::STRING_TYPE,
|
||||
|
|
|
@ -153,11 +153,11 @@ module Types
|
|||
end
|
||||
|
||||
def diff_stats_summary
|
||||
nil_stats = { additions: 0, deletions: 0 }
|
||||
nil_stats = { additions: 0, deletions: 0, file_count: 0 }
|
||||
return nil_stats unless object.diff_stats.present?
|
||||
|
||||
object.diff_stats.each_with_object(nil_stats) do |status, hash|
|
||||
hash.merge!(additions: status.additions, deletions: status.deletions) { |_, x, y| x + y }
|
||||
hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -181,15 +181,11 @@ module CommitsHelper
|
|||
end
|
||||
|
||||
def view_file_button(commit_sha, diff_new_path, project, replaced: false)
|
||||
path = project_blob_path(project, tree_join(commit_sha, diff_new_path))
|
||||
title = replaced ? _('View replaced file @ ') : _('View file @ ')
|
||||
|
||||
link_to(
|
||||
project_blob_path(project,
|
||||
tree_join(commit_sha, diff_new_path)),
|
||||
class: 'btn view-file js-view-file'
|
||||
) do
|
||||
raw(title) + content_tag(:span, Commit.truncate_sha(commit_sha),
|
||||
class: 'commit-sha')
|
||||
link_to(path, class: 'btn') do
|
||||
raw(title) + content_tag(:span, truncate_sha(commit_sha), class: 'commit-sha')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -135,8 +135,7 @@ module DiffHelper
|
|||
|
||||
def diff_file_html_data(project, diff_file_path, diff_commit_id)
|
||||
{
|
||||
blob_diff_path: project_blob_diff_path(project,
|
||||
tree_join(diff_commit_id, diff_file_path)),
|
||||
blob_diff_path: project_blob_diff_path(project, tree_join(diff_commit_id, diff_file_path)),
|
||||
view: diff_view
|
||||
}
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module NavHelper
|
|||
end
|
||||
elsif current_path?('jobs#show')
|
||||
%w[page-gutter build-sidebar right-sidebar-expanded]
|
||||
elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access', 'destroy')
|
||||
elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access', 'destroy', 'diff')
|
||||
%w[page-gutter wiki-sidebar right-sidebar-expanded]
|
||||
else
|
||||
[]
|
||||
|
|
|
@ -96,7 +96,7 @@ module ServicesHelper
|
|||
end
|
||||
|
||||
def integration_form_refactor?
|
||||
Feature.enabled?(:integration_form_refactor, @project)
|
||||
Feature.enabled?(:integration_form_refactor, @project, default_enabled: true)
|
||||
end
|
||||
|
||||
def integration_form_data(integration)
|
||||
|
|
|
@ -3,6 +3,30 @@
|
|||
module WikiHelper
|
||||
include API::Helpers::RelatedResourcesHelpers
|
||||
|
||||
def wiki_page_title(page, action = nil)
|
||||
titles = [_('Wiki')]
|
||||
|
||||
if page.persisted?
|
||||
titles << page.human_title
|
||||
breadcrumb_title(page.human_title)
|
||||
wiki_breadcrumb_dropdown_links(page.slug)
|
||||
end
|
||||
|
||||
titles << action if action
|
||||
page_title(*titles.reverse)
|
||||
add_to_breadcrumbs(_('Wiki'), wiki_path(page.wiki))
|
||||
end
|
||||
|
||||
def link_to_wiki_page(page, **options)
|
||||
link_to page.human_title, wiki_page_path(page.wiki, page), **options
|
||||
end
|
||||
|
||||
def wiki_sidebar_toggle_button
|
||||
content_tag :button, class: 'btn btn-default sidebar-toggle js-sidebar-wiki-toggle', role: 'button', type: 'button' do
|
||||
sprite_icon('chevron-double-lg-left')
|
||||
end
|
||||
end
|
||||
|
||||
# Produces a pure text breadcrumb for a given page.
|
||||
#
|
||||
# page_slug - The slug of a WikiPage object.
|
||||
|
|
|
@ -31,7 +31,7 @@ module Ci
|
|||
merge_request_event: 10,
|
||||
external_pull_request_event: 11,
|
||||
parent_pipeline: 12,
|
||||
ondemand_scan: 13
|
||||
ondemand_dast_scan: 13
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -301,6 +301,10 @@ class WikiPage
|
|||
version&.commit&.committed_date
|
||||
end
|
||||
|
||||
def diffs(diff_options = {})
|
||||
Gitlab::Diff::FileCollection::WikiPage.new(self, diff_options: diff_options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialize_front_matter(hash)
|
||||
|
|
|
@ -27,6 +27,7 @@ module MergeRequests
|
|||
success
|
||||
end
|
||||
end
|
||||
|
||||
log_info("Merge process finished on JID #{merge_jid} with state #{state}")
|
||||
rescue MergeError => e
|
||||
handle_merge_error(log_message: e.message, save_message_on_model: true)
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
an outdated change in
|
||||
commit
|
||||
|
||||
%span.commit-sha= Commit.truncate_sha(discussion.commit_id)
|
||||
%span.commit-sha= truncate_sha(discussion.commit_id)
|
||||
- else
|
||||
- unless discussion.active?
|
||||
an old version of
|
||||
|
|
|
@ -26,16 +26,16 @@
|
|||
%ul
|
||||
- if dashboard_nav_link?(:groups)
|
||||
%li.d-md-none
|
||||
= link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups' do
|
||||
= link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', data: { qa_selector: 'groups_link' } do
|
||||
= _('Groups')
|
||||
- if dashboard_nav_link?(:activity)
|
||||
= nav_link(path: 'dashboard#activity') do
|
||||
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity' do
|
||||
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', data: { qa_selector: 'activity_link' } do
|
||||
= _('Activity')
|
||||
|
||||
- if dashboard_nav_link?(:milestones)
|
||||
= nav_link(controller: 'dashboard/milestones') do
|
||||
= link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones' do
|
||||
= link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', data: { qa_selector: 'milestones_link' } do
|
||||
= _('Milestones')
|
||||
|
||||
- if dashboard_nav_link?(:snippets)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
= diff_merge_request_whitespace_link(diffs.project, @merge_request, class: 'd-none d-sm-inline-block')
|
||||
- elsif current_controller?(:compare)
|
||||
= diff_compare_whitespace_link(diffs.project, params[:from], params[:to], class: 'd-none d-sm-inline-block')
|
||||
- elsif current_controller?(:wikis)
|
||||
= toggle_whitespace_link(url_for(params_with_whitespace), class: 'd-none d-sm-inline-block')
|
||||
.btn-group
|
||||
= inline_diff_btn
|
||||
= parallel_diff_btn
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
- page_title s_("WikiClone|Git Access"), _("Wiki")
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.py-3.flex-column.flex-lg-row
|
||||
%button.btn.btn-default.d-block.d-sm-block.d-md-none.float-right.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
|
||||
= sprite_icon('chevron-double-lg-left')
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.git-access-header.w-100.d-flex.flex-column.justify-content-center
|
||||
%span
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
- wiki_page_title @page, _('Changes')
|
||||
- commit = @diffs.diffable
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _('Changes')
|
||||
|
||||
.nav-controls.pb-md-3.pb-lg-0
|
||||
= link_to wiki_page_path(@wiki, @page, action: :history), class: 'btn', role: 'button', data: { qa_selector: 'page_history_button' } do
|
||||
= s_('Wiki|Page history')
|
||||
|
||||
.page-content-header
|
||||
.header-main-content
|
||||
%strong= markdown_field(commit, :title)
|
||||
%span.d-none.d-sm-inline= _('authored')
|
||||
#{time_ago_with_tooltip(commit.authored_date)}
|
||||
%span= s_('ByAuthor|by')
|
||||
= author_avatar(commit, size: 24, has_tooltip: false)
|
||||
%strong
|
||||
= commit_author_link(commit, avatar: true, size: 24)
|
||||
- if commit.description.present?
|
||||
%pre.commit-description<
|
||||
= preserve(markdown_field(commit, :description))
|
||||
|
||||
= render 'projects/diffs/diffs', diffs: @diffs
|
||||
= render 'shared/wikis/sidebar'
|
|
@ -1,18 +1,14 @@
|
|||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- add_to_breadcrumbs _("Wiki"), wiki_page_path(@wiki, @page)
|
||||
- breadcrumb_title @page.persisted? ? _("Edit") : _("New")
|
||||
- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki")
|
||||
- wiki_page_title @page, @page.persisted? ? _('Edit') : _('New')
|
||||
|
||||
= wiki_page_errors(@error)
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
|
||||
= sprite_icon('chevron-double-lg-left')
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
- if @page.persisted?
|
||||
= link_to @page.human_title, wiki_page_path(@wiki, @page)
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= s_("Wiki|Edit Page")
|
||||
|
|
|
@ -1,41 +1,38 @@
|
|||
- page_title _("History"), @page.human_title, _("Wiki")
|
||||
- wiki_page_title @page, _('History')
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
|
||||
= sprite_icon('chevron-double-lg-left')
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
= link_to @page.human_title, wiki_page_path(@wiki, @page)
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _("History")
|
||||
= _('History')
|
||||
|
||||
.table-holder
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th= s_("Wiki|Page version")
|
||||
%th= _("Author")
|
||||
%th= _("Commit Message")
|
||||
%th= _("Last updated")
|
||||
%th= _("Format")
|
||||
%tbody
|
||||
- @page_versions.each_with_index do |version, index|
|
||||
- commit = version
|
||||
.prepend-top-default.gl-mb-3
|
||||
.table-holder
|
||||
%table.table.wiki-history
|
||||
%thead
|
||||
%tr
|
||||
%td
|
||||
= link_to wiki_page_path(@wiki, @page, version_id: index == 0 ? nil : commit.id) do
|
||||
= truncate_sha(commit.id)
|
||||
%td
|
||||
= commit.author_name
|
||||
%td
|
||||
= commit.message
|
||||
%td
|
||||
#{time_ago_with_tooltip(version.authored_date)}
|
||||
%td
|
||||
%strong
|
||||
= version.format
|
||||
= paginate @page_versions, theme: 'gitlab'
|
||||
%th= s_('Wiki|Page version')
|
||||
%th= _('Author')
|
||||
%th= _('Changes')
|
||||
%th= _('Last updated')
|
||||
%tbody
|
||||
- @page_versions.each do |commit|
|
||||
%tr
|
||||
%td
|
||||
= link_to wiki_page_path(@wiki, @page, version_id: commit.id) do
|
||||
= truncate_sha(commit.id)
|
||||
%td
|
||||
= commit.author_name
|
||||
%td
|
||||
%span.str-truncated-60
|
||||
= link_to wiki_page_path(@wiki, @page, action: :diff, version_id: commit.id), { title: commit.message } do
|
||||
= commit.message
|
||||
%td
|
||||
= time_ago_with_tooltip(commit.authored_date)
|
||||
= paginate @page_versions, theme: 'gitlab'
|
||||
|
||||
= render 'shared/wikis/sidebar'
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- breadcrumb_title @page.human_title
|
||||
- wiki_breadcrumb_dropdown_links(@page.slug)
|
||||
- page_title @page.human_title, _("Wiki")
|
||||
- add_to_breadcrumbs _("Wiki"), wiki_path(@wiki)
|
||||
- wiki_page_title @page
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
|
||||
= sprite_icon('chevron-double-lg-left')
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text.flex-fill
|
||||
%h2.wiki-page-title{ data: { qa_selector: 'wiki_page_title' } }= @page.human_title
|
||||
%span.wiki-last-edit-by
|
||||
- if @page.last_version
|
||||
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
|
||||
#{time_ago_with_tooltip(@page.last_version.authored_date)}
|
||||
= time_ago_with_tooltip(@page.last_version.authored_date)
|
||||
|
||||
.nav-controls.pb-md-3.pb-lg-0
|
||||
= render 'shared/wikis/main_links'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow diffing changes in wiki history
|
||||
merge_request: 35330
|
||||
author: gwhyte, Steve Mokris
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use full version instead of short version for Sentry Error Release links.
|
||||
merge_request: 35623
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add a custom HTML renderer to the Static Site Editor for embedded ruby (ERB) syntax
|
||||
merge_request: 35261
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix unique case where static site editor's custom renderer for identifier syntax didn't robustly handle inline code
|
||||
merge_request: 35775
|
||||
author: Derek Knox
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add MergeRequest.diffStatsSummary.fileCount to graphql API
|
||||
merge_request: 35685
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update integration form to use GitLab UI components
|
||||
merge_request: 35582
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Propagate error on FF pre-receive failure
|
||||
merge_request: 35633
|
||||
author:
|
||||
type: fixed
|
|
@ -10,6 +10,7 @@ scope(controller: :wikis) do
|
|||
scope(path: 'wikis/*id', as: :wiki, format: false) do
|
||||
get :edit
|
||||
get :history
|
||||
get :diff
|
||||
post :preview_markdown
|
||||
get '/', action: :show
|
||||
put '/', action: :update
|
||||
|
|
|
@ -3164,6 +3164,11 @@ type DiffStatsSummary {
|
|||
Number of lines deleted
|
||||
"""
|
||||
deletions: Int!
|
||||
|
||||
"""
|
||||
Number of files changed
|
||||
"""
|
||||
fileCount: Int!
|
||||
}
|
||||
|
||||
type Discussion implements ResolvableInterface {
|
||||
|
@ -5393,6 +5398,31 @@ type Group {
|
|||
startDate: ISO8601Date!
|
||||
): VulnerabilitiesCountByDayAndSeverityConnection
|
||||
|
||||
"""
|
||||
Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups
|
||||
"""
|
||||
vulnerabilityScanners(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): VulnerabilityScannerConnection
|
||||
|
||||
"""
|
||||
Web URL of the group
|
||||
"""
|
||||
|
@ -5520,6 +5550,31 @@ type InstanceSecurityDashboard {
|
|||
"""
|
||||
last: Int
|
||||
): ProjectConnection!
|
||||
|
||||
"""
|
||||
Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard
|
||||
"""
|
||||
vulnerabilityScanners(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): VulnerabilityScannerConnection
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -9771,6 +9826,31 @@ type Project {
|
|||
state: [VulnerabilityState!]
|
||||
): VulnerabilityConnection
|
||||
|
||||
"""
|
||||
Vulnerability scanners reported on the project vulnerabilties
|
||||
"""
|
||||
vulnerabilityScanners(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): VulnerabilityScannerConnection
|
||||
|
||||
"""
|
||||
Counts for each severity of vulnerability of the project
|
||||
"""
|
||||
|
@ -11465,10 +11545,15 @@ type SentryDetailedError {
|
|||
firstReleaseLastCommit: String
|
||||
|
||||
"""
|
||||
Release version the error was first seen
|
||||
Release short version the error was first seen
|
||||
"""
|
||||
firstReleaseShortVersion: String
|
||||
|
||||
"""
|
||||
Release version the error was first seen
|
||||
"""
|
||||
firstReleaseVersion: String
|
||||
|
||||
"""
|
||||
Timestamp when the error was first seen
|
||||
"""
|
||||
|
@ -11505,10 +11590,15 @@ type SentryDetailedError {
|
|||
lastReleaseLastCommit: String
|
||||
|
||||
"""
|
||||
Release version the error was last seen
|
||||
Release short version the error was last seen
|
||||
"""
|
||||
lastReleaseShortVersion: String
|
||||
|
||||
"""
|
||||
Release version the error was last seen
|
||||
"""
|
||||
lastReleaseVersion: String
|
||||
|
||||
"""
|
||||
Timestamp when the error was last seen
|
||||
"""
|
||||
|
@ -14443,6 +14533,51 @@ type VulnerabilityScanner {
|
|||
Name of the vulnerability scanner
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
Type of the vulnerability report
|
||||
"""
|
||||
reportType: VulnerabilityReportType
|
||||
|
||||
"""
|
||||
Vendor of the vulnerability scanner
|
||||
"""
|
||||
vendor: String
|
||||
}
|
||||
|
||||
"""
|
||||
The connection type for VulnerabilityScanner.
|
||||
"""
|
||||
type VulnerabilityScannerConnection {
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
edges: [VulnerabilityScannerEdge]
|
||||
|
||||
"""
|
||||
A list of nodes.
|
||||
"""
|
||||
nodes: [VulnerabilityScanner]
|
||||
|
||||
"""
|
||||
Information to aid in pagination.
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
type VulnerabilityScannerEdge {
|
||||
"""
|
||||
A cursor for use in pagination.
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
The item at the end of the edge.
|
||||
"""
|
||||
node: VulnerabilityScanner
|
||||
}
|
||||
|
||||
"""
|
||||
|
|
|
@ -8778,6 +8778,24 @@
|
|||
"description": "Number of lines deleted",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "fileCount",
|
||||
"description": "Number of files changed",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
|
@ -14808,6 +14826,59 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilityScanners",
|
||||
"description": "Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webUrl",
|
||||
"description": "Web URL of the group",
|
||||
|
@ -15206,6 +15277,59 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilityScanners",
|
||||
"description": "Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
@ -28685,6 +28809,59 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilityScanners",
|
||||
"description": "Vulnerability scanners reported on the project vulnerabilties",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilitySeveritiesCount",
|
||||
"description": "Counts for each severity of vulnerability of the project",
|
||||
|
@ -33654,6 +33831,20 @@
|
|||
},
|
||||
{
|
||||
"name": "firstReleaseShortVersion",
|
||||
"description": "Release short version the error was first seen",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "firstReleaseVersion",
|
||||
"description": "Release version the error was first seen",
|
||||
"args": [
|
||||
|
||||
|
@ -33786,6 +33977,20 @@
|
|||
},
|
||||
{
|
||||
"name": "lastReleaseShortVersion",
|
||||
"description": "Release short version the error was last seen",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lastReleaseVersion",
|
||||
"description": "Release version the error was last seen",
|
||||
"args": [
|
||||
|
||||
|
@ -42503,6 +42708,146 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "reportType",
|
||||
"description": "Type of the vulnerability report",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "VulnerabilityReportType",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vendor",
|
||||
"description": "Vendor of the vulnerability scanner",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerConnection",
|
||||
"description": "The connection type for VulnerabilityScanner.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerEdge",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "nodes",
|
||||
"description": "A list of nodes.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScanner",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "pageInfo",
|
||||
"description": "Information to aid in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "PageInfo",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScannerEdge",
|
||||
"description": "An edge in a connection.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "cursor",
|
||||
"description": "A cursor for use in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "node",
|
||||
"description": "The item at the end of the edge.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilityScanner",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
|
|
@ -545,6 +545,7 @@ Aggregated summary of changes
|
|||
| `additions` | Int! | Number of lines added |
|
||||
| `changes` | Int! | Number of lines changed |
|
||||
| `deletions` | Int! | Number of lines deleted |
|
||||
| `fileCount` | Int! | Number of files changed |
|
||||
|
||||
## Discussion
|
||||
|
||||
|
@ -1679,7 +1680,8 @@ A Sentry error.
|
|||
| `externalBaseUrl` | String! | External Base URL of the Sentry Instance |
|
||||
| `externalUrl` | String! | External URL of the error |
|
||||
| `firstReleaseLastCommit` | String | Commit the error was first seen |
|
||||
| `firstReleaseShortVersion` | String | Release version the error was first seen |
|
||||
| `firstReleaseShortVersion` | String | Release short version the error was first seen |
|
||||
| `firstReleaseVersion` | String | Release version the error was first seen |
|
||||
| `firstSeen` | Time! | Timestamp when the error was first seen |
|
||||
| `frequency` | SentryErrorFrequency! => Array | Last 24hr stats of the error |
|
||||
| `gitlabCommit` | String | GitLab commit SHA attributed to the Error based on the release version |
|
||||
|
@ -1687,7 +1689,8 @@ A Sentry error.
|
|||
| `gitlabIssuePath` | String | URL of GitLab Issue |
|
||||
| `id` | ID! | ID (global ID) of the error |
|
||||
| `lastReleaseLastCommit` | String | Commit the error was last seen |
|
||||
| `lastReleaseShortVersion` | String | Release version the error was last seen |
|
||||
| `lastReleaseShortVersion` | String | Release short version the error was last seen |
|
||||
| `lastReleaseVersion` | String | Release version the error was last seen |
|
||||
| `lastSeen` | Time! | Timestamp when the error was last seen |
|
||||
| `message` | String | Sentry metadata message of the error |
|
||||
| `sentryId` | String! | ID (Sentry ID) of the error |
|
||||
|
@ -2219,6 +2222,8 @@ Represents a vulnerability scanner.
|
|||
| --- | ---- | ---------- |
|
||||
| `externalId` | String | External ID of the vulnerability scanner |
|
||||
| `name` | String | Name of the vulnerability scanner |
|
||||
| `reportType` | VulnerabilityReportType | Type of the vulnerability report |
|
||||
| `vendor` | String | Vendor of the vulnerability scanner |
|
||||
|
||||
## VulnerabilitySeveritiesCount
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ Example response:
|
|||
|
||||
### Set override flag for a member of a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 12.10.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 13.0.
|
||||
|
||||
By default, the access level of LDAP group members is set to the value specified
|
||||
by LDAP through Group Sync. You can allow access level overrides by calling this endpoint.
|
||||
|
@ -326,7 +326,7 @@ Example response:
|
|||
|
||||
### Remove override for a member of a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 12.10.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 13.0.
|
||||
|
||||
Sets the override flag to false and allows LDAP Group Sync to reset the access
|
||||
level to the LDAP-prescribed value.
|
||||
|
|
|
@ -17,7 +17,9 @@ Working with our frontend assets requires Node (v10.13.0 or greater) and Yarn
|
|||
|
||||
For our currently-supported browsers, see our [requirements](../../install/requirements.md#supported-web-browsers).
|
||||
|
||||
Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers. Login to BrowserStack with the credentials saved in GitLab's [shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).
|
||||
Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers.
|
||||
Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's
|
||||
[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide).
|
||||
|
||||
## Initiatives
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ Take into consideration that such action can make the feature available on
|
|||
GitLab.com shortly after the change to the feature flag is merged.
|
||||
|
||||
Changing the default state or removing the feature flag has to be done before
|
||||
the 22nd of the month, _at least_ 2 working days before, in order for the change
|
||||
the 22nd of the month, _at least_ 3-4 working days before, in order for the change
|
||||
to be included in the final self-managed release.
|
||||
|
||||
In addition to this, the feature behind feature flag should:
|
||||
|
|
|
@ -856,7 +856,8 @@ Some regressions only affect a specific browser version. We can install and test
|
|||
|
||||
[BrowserStack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
|
||||
You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
|
||||
You can find the credentials on 1Password, under `frontendteam@gitlab.com`.
|
||||
Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's
|
||||
[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide).
|
||||
|
||||
### Firefox
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
|
@ -748,6 +748,11 @@ Note the following properties:
|
|||
|
||||
![heatmap panel type](img/heatmap_panel_type.png)
|
||||
|
||||
CAUTION: **Warning:**
|
||||
When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
|
||||
|
||||
![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png)
|
||||
|
||||
### Templating variables for metrics dashboards
|
||||
|
||||
Templating variables can be used to make your metrics dashboard more versatile.
|
||||
|
|
|
@ -73,16 +73,16 @@ module Gitlab
|
|||
# @return [Hash<String,Array<String>>]
|
||||
def changes_by_category
|
||||
all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
|
||||
hash[category_for_file(file)] << file
|
||||
categories_for_file(file).each { |category| hash[category] << file }
|
||||
end
|
||||
end
|
||||
|
||||
# Determines the category a file is in, e.g., `:frontend` or `:backend`
|
||||
# @return[Symbol]
|
||||
def category_for_file(file)
|
||||
_, category = CATEGORIES.find { |regexp, _| regexp.match?(file) }
|
||||
# Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`.
|
||||
# @return Array<Symbol>
|
||||
def categories_for_file(file)
|
||||
_, categories = CATEGORIES.find { |regexp, _| regexp.match?(file) }
|
||||
|
||||
category || :unknown
|
||||
Array(categories || :unknown)
|
||||
end
|
||||
|
||||
# Returns the GFM for a category label, making its best guess if it's not
|
||||
|
@ -125,10 +125,13 @@ module Gitlab
|
|||
jest\.config\.js |
|
||||
package\.json |
|
||||
yarn\.lock |
|
||||
config/.+\.js |
|
||||
\.gitlab/ci/frontend\.gitlab-ci\.yml
|
||||
config/.+\.js
|
||||
)\z}x => :frontend,
|
||||
|
||||
%r{(\A|/)(
|
||||
\.gitlab/ci/frontend\.gitlab-ci\.yml
|
||||
)\z}x => %i[frontend engineering_productivity],
|
||||
|
||||
%r{\A(ee/)?db/(?!fixtures)[^/]+} => :database,
|
||||
%r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database,
|
||||
%r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Diff
|
||||
module FileCollection
|
||||
class WikiPage < Base
|
||||
def initialize(page, diff_options:)
|
||||
commit = page.wiki.commit(page.version.commit)
|
||||
diff_options = diff_options.merge(
|
||||
expanded: true,
|
||||
paths: [page.path]
|
||||
)
|
||||
|
||||
super(commit,
|
||||
# TODO: Uncouple diffing from projects
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/217752
|
||||
project: page.wiki,
|
||||
diff_options: diff_options,
|
||||
diff_refs: commit.diff_refs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,7 @@ module Gitlab
|
|||
:id,
|
||||
:last_release_last_commit,
|
||||
:last_release_short_version,
|
||||
:last_release_version,
|
||||
:last_seen,
|
||||
:message,
|
||||
:project_id,
|
||||
|
|
|
@ -101,6 +101,10 @@ module Gitlab
|
|||
wrapped_gitaly_errors do
|
||||
gitaly_find_page(title: title, version: version, dir: dir)
|
||||
end
|
||||
rescue Gitlab::Git::CommandError
|
||||
# Return nil for invalid versions.
|
||||
# This can be removed with https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2323 in place.
|
||||
nil
|
||||
end
|
||||
|
||||
def file(name, version)
|
||||
|
|
|
@ -178,6 +178,10 @@ module Gitlab
|
|||
timeout: GitalyClient.long_timeout
|
||||
)
|
||||
|
||||
if response.pre_receive_error.present?
|
||||
raise Gitlab::Git::PreReceiveError.new("GL-HOOK-ERR: pre-receive hook failed.")
|
||||
end
|
||||
|
||||
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
||||
rescue GRPC::FailedPrecondition => e
|
||||
raise Gitlab::Git::CommitError, e
|
||||
|
|
|
@ -168,7 +168,8 @@ module Sentry
|
|||
first_release_short_version: issue.dig('firstRelease', 'shortVersion'),
|
||||
first_release_version: issue.dig('firstRelease', 'version'),
|
||||
last_release_last_commit: issue.dig('lastRelease', 'lastCommit'),
|
||||
last_release_short_version: issue.dig('lastRelease', 'shortVersion')
|
||||
last_release_short_version: issue.dig('lastRelease', 'shortVersion'),
|
||||
last_release_version: issue.dig('lastRelease', 'version')
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -10187,9 +10187,6 @@ msgstr ""
|
|||
msgid "Forks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Format: %{dateFormat}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10280,6 +10277,9 @@ msgstr ""
|
|||
msgid "Generate new export"
|
||||
msgstr ""
|
||||
|
||||
msgid "GenericReports|Report"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11234,16 +11234,16 @@ msgstr ""
|
|||
msgid "Group: %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupActivityMetrics|New Members created"
|
||||
msgid "GroupActivityMetrics|Issues opened"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupActivyMetrics|Issues created"
|
||||
msgid "GroupActivityMetrics|Members added"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupActivyMetrics|Merge Requests created"
|
||||
msgid "GroupActivityMetrics|Merge Requests opened"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupActivyMetrics|Recent activity (last 90 days)"
|
||||
msgid "GroupActivityMetrics|Recent activity (last 90 days)"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupImport|Failed to import group."
|
||||
|
@ -15311,9 +15311,6 @@ msgstr ""
|
|||
msgid "No test coverage"
|
||||
msgstr ""
|
||||
|
||||
msgid "No thanks"
|
||||
msgstr ""
|
||||
|
||||
msgid "No vulnerabilities present"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25306,6 +25303,9 @@ msgstr ""
|
|||
msgid "View open merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "View page @ "
|
||||
msgstr ""
|
||||
|
||||
msgid "View performance dashboard."
|
||||
msgstr ""
|
||||
|
||||
|
@ -27551,7 +27551,7 @@ msgstr ""
|
|||
msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd}, simply add a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust."
|
||||
msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd} by simply adding a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust."
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
|
||||
|
|
1
qa/qa.rb
1
qa/qa.rb
|
@ -448,6 +448,7 @@ module QA
|
|||
autoload :ConfirmModal, 'qa/page/component/confirm_modal'
|
||||
autoload :CustomMetric, 'qa/page/component/custom_metric'
|
||||
autoload :DesignManagement, 'qa/page/component/design_management'
|
||||
autoload :ProjectSelector, 'qa/page/component/project_selector'
|
||||
|
||||
module Issuable
|
||||
autoload :Common, 'qa/page/component/issuable/common'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module ProjectSelector
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
super
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue' do
|
||||
element :project_search_field
|
||||
element :project_list_item
|
||||
end
|
||||
end
|
||||
|
||||
def fill_project_search_input(project_name)
|
||||
fill_element :project_search_field, project_name
|
||||
end
|
||||
|
||||
def select_project
|
||||
click_element :project_list_item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,9 @@ module QA
|
|||
element :groups_dropdown, required: true
|
||||
element :more_dropdown
|
||||
element :snippets_link
|
||||
element :groups_link
|
||||
element :activity_link
|
||||
element :milestones_link
|
||||
end
|
||||
|
||||
view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
|
||||
|
@ -53,10 +56,10 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def go_to_snippets
|
||||
def go_to_more_dropdown_option(option_name)
|
||||
within_top_menu do
|
||||
click_element :more_dropdown
|
||||
click_element :snippets_link
|
||||
click_element option_name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -148,3 +151,5 @@ module QA
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
QA::Page::Main::Menu.prepend_if_ee('QA::EE::Page::Main::Menu')
|
||||
|
|
|
@ -6,7 +6,9 @@ module QA
|
|||
it 'User creates a personal snippet' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
Page::Main::Menu.perform(&:go_to_snippets)
|
||||
Page::Main::Menu.perform do |menu|
|
||||
menu.go_to_more_dropdown_option(:snippets_link)
|
||||
end
|
||||
|
||||
Resource::Snippet.fabricate_via_browser_ui! do |snippet|
|
||||
snippet.title = 'Snippet title'
|
||||
|
|
|
@ -8,29 +8,29 @@ module RuboCop
|
|||
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#type-authorization'
|
||||
|
||||
# We want to exclude our own basetypes and scalars
|
||||
WHITELISTED_TYPES = %w[BaseEnum BaseScalar BasePermissionType MutationType
|
||||
QueryType GraphQL::Schema BaseUnion].freeze
|
||||
ALLOWED_TYPES = %w[BaseEnum BaseScalar BasePermissionType MutationType
|
||||
QueryType GraphQL::Schema BaseUnion].freeze
|
||||
|
||||
def_node_search :authorize?, <<~PATTERN
|
||||
(send nil? :authorize ...)
|
||||
PATTERN
|
||||
|
||||
def on_class(node)
|
||||
return if whitelisted?(class_constant(node))
|
||||
return if whitelisted?(superclass_constant(node))
|
||||
return if allowed?(class_constant(node))
|
||||
return if allowed?(superclass_constant(node))
|
||||
|
||||
add_offense(node, location: :expression) unless authorize?(node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def whitelisted?(class_node)
|
||||
def allowed?(class_node)
|
||||
class_const = class_node&.const_name
|
||||
|
||||
return false unless class_const
|
||||
return true if class_const.end_with?('Enum')
|
||||
|
||||
WHITELISTED_TYPES.any? { |whitelisted| class_node.const_name.include?(whitelisted) }
|
||||
ALLOWED_TYPES.any? { |allowed| class_node.const_name.include?(allowed) }
|
||||
end
|
||||
|
||||
def class_constant(node)
|
||||
|
|
|
@ -8,9 +8,10 @@ RSpec.describe 'User views a wiki page' do
|
|||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||
let(:path) { 'image.png' }
|
||||
let(:wiki) { project.wiki }
|
||||
let(:wiki_page) do
|
||||
create(:wiki_page,
|
||||
wiki: project.wiki,
|
||||
wiki: wiki,
|
||||
title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})")
|
||||
end
|
||||
|
||||
|
@ -70,11 +71,13 @@ RSpec.describe 'User views a wiki page' do
|
|||
|
||||
click_on('Page history')
|
||||
|
||||
page.within(:css, '.nav-text') do
|
||||
within('.nav-text') do
|
||||
expect(page).to have_content('History')
|
||||
end
|
||||
|
||||
find('a[href*="?version_id"]')
|
||||
within('.wiki-history') do
|
||||
expect(page).to have_css('a[href*="?version_id"]', count: 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -92,8 +95,8 @@ RSpec.describe 'User views a wiki page' do
|
|||
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
|
||||
|
||||
it do
|
||||
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
|
||||
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
|
||||
expect(page).to have_xpath("//img[@data-src='#{wiki.wiki_base_path}/#{path}']")
|
||||
expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
|
||||
|
||||
click_on('image')
|
||||
|
||||
|
@ -103,7 +106,7 @@ RSpec.describe 'User views a wiki page' do
|
|||
end
|
||||
|
||||
it 'shows the creation page if file does not exist' do
|
||||
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
|
||||
expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
|
||||
|
||||
click_on('image')
|
||||
|
||||
|
@ -114,7 +117,7 @@ RSpec.describe 'User views a wiki page' do
|
|||
|
||||
context 'when a page has history' do
|
||||
before do
|
||||
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
|
||||
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') # rubocop:disable Rails/SaveBang
|
||||
end
|
||||
|
||||
it 'shows the page history' do
|
||||
|
@ -134,13 +137,74 @@ RSpec.describe 'User views a wiki page' do
|
|||
|
||||
expect(page).not_to have_selector('a.btn', text: 'Edit')
|
||||
end
|
||||
|
||||
context 'show the diff' do
|
||||
def expect_diff_links(commit)
|
||||
diff_path = wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
|
||||
|
||||
expect(page).to have_link('Hide whitespace changes', href: "#{diff_path}&w=1")
|
||||
expect(page).to have_link('Inline', href: "#{diff_path}&view=inline")
|
||||
expect(page).to have_link('Side-by-side', href: "#{diff_path}&view=parallel")
|
||||
expect(page).to have_link("View page @ #{commit.short_id}", href: wiki_page_path(wiki, wiki_page, version_id: commit))
|
||||
expect(page).to have_css('.diff-file[data-blob-diff-path="%s"]' % diff_path)
|
||||
end
|
||||
|
||||
it 'links to the correct diffs' do
|
||||
visit project_wiki_history_path(project, wiki_page)
|
||||
|
||||
commit1 = wiki.commit('HEAD^')
|
||||
commit2 = wiki.commit
|
||||
|
||||
expect(page).to have_link('created page: home', href: wiki_page_path(wiki, wiki_page, version_id: commit1, action: :diff))
|
||||
expect(page).to have_link('updated home', href: wiki_page_path(wiki, wiki_page, version_id: commit2, action: :diff))
|
||||
end
|
||||
|
||||
it 'between the current and the previous version of a page' do
|
||||
commit = wiki.commit
|
||||
visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
|
||||
|
||||
expect(page).to have_content('by John Doe')
|
||||
expect(page).to have_content('updated home')
|
||||
expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
|
||||
expect(page).to have_content('some link')
|
||||
|
||||
expect_diff_links(commit)
|
||||
end
|
||||
|
||||
it 'between two old versions of a page' do
|
||||
wiki_page.update(message: 'latest home change', content: 'updated [another link](other-page)') # rubocop:disable Rails/SaveBang:
|
||||
commit = wiki.commit('HEAD^')
|
||||
visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
|
||||
|
||||
expect(page).to have_content('by John Doe')
|
||||
expect(page).to have_content('updated home')
|
||||
expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
|
||||
expect(page).to have_content('some link')
|
||||
expect(page).not_to have_content('latest home change')
|
||||
expect(page).not_to have_content('another link')
|
||||
|
||||
expect_diff_links(commit)
|
||||
end
|
||||
|
||||
it 'for the oldest version of a page' do
|
||||
commit = wiki.commit('HEAD^')
|
||||
visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
|
||||
|
||||
expect(page).to have_content('by John Doe')
|
||||
expect(page).to have_content('created page: home')
|
||||
expect(page).to have_content('Showing 1 changed file with 4 additions and 0 deletions')
|
||||
expect(page).to have_content('Look at this')
|
||||
|
||||
expect_diff_links(commit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a page has special characters in its title' do
|
||||
let(:title) { '<foo> !@#$%^&*()[]{}=_+\'"\\|<>? <bar>' }
|
||||
|
||||
before do
|
||||
wiki_page.update(title: title )
|
||||
wiki_page.update(title: title ) # rubocop:disable Rails/SaveBang
|
||||
end
|
||||
|
||||
it 'preserves the special characters' do
|
||||
|
@ -155,7 +219,7 @@ RSpec.describe 'User views a wiki page' do
|
|||
let(:title) { '<script>alert("title")<script>' }
|
||||
|
||||
before do
|
||||
wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar')
|
||||
wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar') # rubocop:disable Rails/SaveBang
|
||||
end
|
||||
|
||||
it 'safely displays the page' do
|
||||
|
@ -168,7 +232,7 @@ RSpec.describe 'User views a wiki page' do
|
|||
|
||||
context 'when a page has XSS in its message' do
|
||||
before do
|
||||
wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update')
|
||||
wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update') # rubocop:disable Rails/SaveBang
|
||||
end
|
||||
|
||||
it 'safely displays the message' do
|
||||
|
|
|
@ -42,7 +42,30 @@
|
|||
"isBookmarked": false,
|
||||
"isPublic": false,
|
||||
"isSubscribed": true,
|
||||
"lastRelease": null,
|
||||
"lastRelease": {
|
||||
"dateReleased": null,
|
||||
"commitCount": 1,
|
||||
"url": null,
|
||||
"data": {},
|
||||
"lastDeploy": {},
|
||||
"deployCount": 3,
|
||||
"dateCreated": "2020-06-29T08:10:45.909Z",
|
||||
"lastEvent": "2020-06-30T09:47:19.651Z",
|
||||
"version": "17642328ead24b51867165985996d04b29321448",
|
||||
"firstEvent": "2020-06-29T09:13:36.696Z",
|
||||
"lastCommit": {},
|
||||
"shortVersion": "27de6b42eb4",
|
||||
"authors": [],
|
||||
"owner": null,
|
||||
"newGroups": 208,
|
||||
"ref": null,
|
||||
"projects": [
|
||||
{
|
||||
"name": "Pump Station",
|
||||
"slug": "pump-station"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastSeen": "2018-11-06T21:19:55Z",
|
||||
"level": "error",
|
||||
"logger": null,
|
||||
|
|
|
@ -5,23 +5,6 @@ import { TEST_HOST } from '../helpers/test_constants';
|
|||
export const mockProjectDir = '/frontend-fixtures/environments-project';
|
||||
export const mockApiEndpoint = `${TEST_HOST}/monitoring/mock`;
|
||||
|
||||
export const propsData = {
|
||||
hasMetrics: false,
|
||||
documentationPath: '/path/to/docs',
|
||||
settingsPath: '/path/to/settings',
|
||||
clustersPath: '/path/to/clusters',
|
||||
tagsPath: '/path/to/tags',
|
||||
defaultBranch: 'master',
|
||||
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
|
||||
emptyLoadingSvgPath: '/path/to/loading.svg',
|
||||
emptyNoDataSvgPath: '/path/to/no-data.svg',
|
||||
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
|
||||
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
|
||||
customMetricsAvailable: false,
|
||||
customMetricsPath: '',
|
||||
validateQueryPath: '',
|
||||
};
|
||||
|
||||
export const customDashboardBasePath = '.gitlab/dashboards';
|
||||
|
||||
const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import DashboardPage from '~/monitoring/pages/dashboard_page.vue';
|
||||
import Dashboard from '~/monitoring/components/dashboard.vue';
|
||||
import { propsData } from '../mock_data';
|
||||
import { dashboardProps } from '../fixture_data';
|
||||
|
||||
describe('monitoring/pages/dashboard_page', () => {
|
||||
let wrapper;
|
||||
|
@ -28,9 +28,18 @@ describe('monitoring/pages/dashboard_page', () => {
|
|||
});
|
||||
|
||||
it('renders the dashboard page with dashboard component', () => {
|
||||
buildWrapper({ dashboardProps: propsData });
|
||||
buildWrapper({ dashboardProps });
|
||||
|
||||
const allProps = {
|
||||
...dashboardProps,
|
||||
// default props values
|
||||
rearrangePanelsAvailable: false,
|
||||
showHeader: true,
|
||||
showPanels: true,
|
||||
smallEmptyState: false,
|
||||
};
|
||||
|
||||
expect(findDashboardComponent().props()).toMatchObject(propsData);
|
||||
expect(findDashboardComponent()).toExist();
|
||||
expect(allProps).toMatchObject(findDashboardComponent().props());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import suggestPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue';
|
||||
import stubChildren from 'helpers/stub_children';
|
||||
import PipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
|
||||
import MrWidgetIcon from '~/vue_merge_request_widget/components/mr_widget_icon.vue';
|
||||
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
|
||||
import { popoverProps, iconName } from './pipeline_tour_mock_data';
|
||||
|
||||
describe('MRWidgetHeader', () => {
|
||||
describe('MRWidgetSuggestPipeline', () => {
|
||||
let wrapper;
|
||||
const pipelinePath = '/foo/bar/add/pipeline/path';
|
||||
const pipelineSvgPath = '/foo/bar/pipeline/svg/path';
|
||||
const humanAccess = 'maintainer';
|
||||
const iconName = 'status_notfound';
|
||||
let trackingSpy;
|
||||
|
||||
const mockTrackingOnWrapper = () => {
|
||||
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.dataset.page = 'projects:merge_requests:show';
|
||||
trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
|
||||
|
||||
wrapper = mount(suggestPipelineComponent, {
|
||||
propsData: { pipelinePath, pipelineSvgPath, humanAccess },
|
||||
propsData: popoverProps,
|
||||
stubs: {
|
||||
...stubChildren(PipelineTourState),
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
|
@ -25,14 +27,17 @@ describe('MRWidgetHeader', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
unmockTracking();
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
const findOkBtn = () => wrapper.find('[data-testid="ok"]');
|
||||
|
||||
it('renders add pipeline file link', () => {
|
||||
const link = wrapper.find(GlLink);
|
||||
|
||||
expect(link.exists()).toBe(true);
|
||||
expect(link.attributes().href).toBe(pipelinePath);
|
||||
expect(link.attributes().href).toBe(popoverProps.pipelinePath);
|
||||
});
|
||||
|
||||
it('renders the expected text', () => {
|
||||
|
@ -52,25 +57,60 @@ describe('MRWidgetHeader', () => {
|
|||
);
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
let spy;
|
||||
it('renders the show me how button', () => {
|
||||
const button = findOkBtn();
|
||||
|
||||
beforeEach(() => {
|
||||
spy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.classes('btn-info')).toEqual(true);
|
||||
expect(button.attributes('href')).toBe(popoverProps.pipelinePath);
|
||||
});
|
||||
|
||||
it('renders the help link', () => {
|
||||
const link = wrapper.find('[data-testid="help"]');
|
||||
|
||||
expect(link.exists()).toBe(true);
|
||||
expect(link.attributes('href')).toBe(wrapper.vm.$options.helpURL);
|
||||
});
|
||||
|
||||
it('renders the empty pipelines image', () => {
|
||||
const image = wrapper.find('[data-testid="pipeline-image"]');
|
||||
|
||||
expect(image.exists()).toBe(true);
|
||||
expect(image.attributes().src).toBe(popoverProps.pipelineSvgPath);
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
it('send event for basic view of the suggest pipeline widget', () => {
|
||||
const expectedCategory = undefined;
|
||||
const expectedAction = undefined;
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(expectedCategory, expectedAction, {
|
||||
label: wrapper.vm.$options.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmockTracking();
|
||||
it('send an event when add pipeline link is clicked', () => {
|
||||
mockTrackingOnWrapper();
|
||||
const link = wrapper.find('[data-testid="add-pipeline-link"]');
|
||||
triggerEvent(link.element);
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_link', {
|
||||
label: wrapper.vm.$options.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
value: '30',
|
||||
});
|
||||
});
|
||||
|
||||
it('send an event when ok button is clicked', () => {
|
||||
const link = wrapper.find(GlLink);
|
||||
triggerEvent(link.element);
|
||||
mockTrackingOnWrapper();
|
||||
const okBtn = findOkBtn();
|
||||
triggerEvent(okBtn.element);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('_category_', 'click_link', {
|
||||
label: 'no_pipeline_noticed',
|
||||
property: humanAccess,
|
||||
value: '30',
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_button', {
|
||||
label: wrapper.vm.$options.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
value: '10',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export const popoverProps = {
|
||||
pipelinePath: '/foo/bar/add/pipeline/path',
|
||||
pipelineSvgPath: 'assets/illustrations/something.svg',
|
||||
humanAccess: 'maintainer',
|
||||
};
|
||||
|
||||
export const iconName = 'status_notfound';
|
|
@ -1,153 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlPopover, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import Cookies from 'js-cookie';
|
||||
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
|
||||
import pipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
|
||||
import { popoverProps, cookieKey } from './pipeline_tour_mock_data';
|
||||
|
||||
describe('MRWidgetPipelineTour', () => {
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
describe(`when ${cookieKey} cookie is set`, () => {
|
||||
beforeEach(() => {
|
||||
Cookies.set(cookieKey, true);
|
||||
wrapper = shallowMount(pipelineTourState, {
|
||||
propsData: popoverProps,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render the popover', () => {
|
||||
const popover = wrapper.find(GlPopover);
|
||||
|
||||
expect(popover.exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmockTracking();
|
||||
});
|
||||
it('does not call tracking', () => {
|
||||
expect(trackingSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when ${cookieKey} cookie is not set`, () => {
|
||||
const findOkBtn = () => wrapper.find({ ref: 'ok' });
|
||||
const findDismissBtn = () => wrapper.find({ ref: 'no-thanks' });
|
||||
|
||||
beforeEach(() => {
|
||||
Cookies.remove(cookieKey);
|
||||
wrapper = shallowMount(pipelineTourState, {
|
||||
propsData: popoverProps,
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the popover', () => {
|
||||
const popover = wrapper.find(GlPopover);
|
||||
|
||||
expect(popover.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the help link', () => {
|
||||
const link = wrapper.find(GlLink);
|
||||
|
||||
expect(link.exists()).toBe(true);
|
||||
expect(link.attributes('href')).toBe(wrapper.vm.$options.helpURL);
|
||||
});
|
||||
|
||||
it('renders the show me how button', () => {
|
||||
const button = findOkBtn();
|
||||
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.attributes().category).toBe('primary');
|
||||
});
|
||||
|
||||
it('renders the dismiss button', () => {
|
||||
const button = findDismissBtn();
|
||||
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.attributes().category).toBe('secondary');
|
||||
});
|
||||
|
||||
it('renders the empty pipelines image', () => {
|
||||
const image = wrapper.find('img');
|
||||
|
||||
expect(image.exists()).toBe(true);
|
||||
expect(image.attributes().src).toBe(popoverProps.pipelineSvgPath);
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmockTracking();
|
||||
});
|
||||
|
||||
it('send event for basic view of popover', () => {
|
||||
document.body.dataset.page = 'projects:merge_requests:show';
|
||||
|
||||
wrapper.vm.trackOnShow();
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, undefined, {
|
||||
label: popoverProps.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
});
|
||||
});
|
||||
|
||||
it('send an event when ok button is clicked', () => {
|
||||
const okBtn = findOkBtn();
|
||||
triggerEvent(okBtn.element);
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_button', {
|
||||
label: popoverProps.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
value: '10',
|
||||
});
|
||||
});
|
||||
|
||||
it('send an event when dismiss button is clicked', () => {
|
||||
const dismissBtn = findDismissBtn();
|
||||
triggerEvent(dismissBtn.element);
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_button', {
|
||||
label: popoverProps.trackLabel,
|
||||
property: popoverProps.humanAccess,
|
||||
value: '20',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dismissPopover', () => {
|
||||
it('updates popoverDismissed', () => {
|
||||
const button = findDismissBtn();
|
||||
const popover = wrapper.find(GlPopover);
|
||||
button.vm.$emit('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(Cookies.get(cookieKey)).toBe('true');
|
||||
expect(popover.exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
export const popoverProps = {
|
||||
pipelinePath: '/foo/bar/add/pipeline/path',
|
||||
pipelineSvgPath: 'assets/illustrations/something.svg',
|
||||
humanAccess: 'maintainer',
|
||||
popoverTarget: 'suggest-popover',
|
||||
popoverContainer: 'suggest-pipeline',
|
||||
trackLabel: 'some_tracking_label',
|
||||
};
|
||||
|
||||
export const cookieKey = 'suggest_pipeline_dismissed';
|
|
@ -19,9 +19,7 @@ const buildMockTextNodeWithAdjacentInlineCode = isForward => {
|
|||
type: 'code',
|
||||
[direction]: {
|
||||
literal: isForward ? literalClose : literalOpen,
|
||||
[direction]: {
|
||||
literal: null,
|
||||
},
|
||||
[direction]: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -47,6 +45,7 @@ export const kramdownTextNode = buildMockTextNode('{:toc}');
|
|||
export const identifierTextNode = buildMockTextNode('[Some text]: https://link.com');
|
||||
export const identifierInlineCodeTextEnteringNode = buildMockTextNodeWithAdjacentInlineCode(true);
|
||||
export const identifierInlineCodeTextExitingNode = buildMockTextNodeWithAdjacentInlineCode(false);
|
||||
export const embeddedRubyTextNode = buildMockTextNode('<%= partial("some/path") %>');
|
||||
export const normalTextNode = buildMockTextNode('This is just normal text.');
|
||||
|
||||
const uneditableOpenToken = {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_embedded_ruby_text';
|
||||
import { buildUneditableTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
|
||||
import { embeddedRubyTextNode, normalTextNode } from '../../mock_data';
|
||||
|
||||
describe('Render Embedded Ruby Text renderer', () => {
|
||||
describe('canRender', () => {
|
||||
it('should return true when the argument `literal` has embedded ruby syntax', () => {
|
||||
expect(renderer.canRender(embeddedRubyTextNode)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when the argument `literal` lacks embedded ruby syntax', () => {
|
||||
expect(renderer.canRender(normalTextNode)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
const origin = jest.fn();
|
||||
|
||||
it('should return uneditable tokens', () => {
|
||||
const context = { origin };
|
||||
|
||||
expect(renderer.render(embeddedRubyTextNode, context)).toStrictEqual(
|
||||
buildUneditableTokens(origin()),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -3,7 +3,7 @@ import {
|
|||
buildUneditableOpenTokens,
|
||||
buildUneditableCloseTokens,
|
||||
buildUneditableTokens,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers//build_uneditable_token';
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
|
||||
import {
|
||||
identifierTextNode,
|
||||
|
|
|
@ -31,6 +31,8 @@ RSpec.describe GitlabSchema.types['SentryDetailedError'] do
|
|||
lastReleaseLastCommit
|
||||
firstReleaseShortVersion
|
||||
lastReleaseShortVersion
|
||||
firstReleaseVersion
|
||||
lastReleaseVersion
|
||||
gitlabIssuePath
|
||||
gitlabCommit
|
||||
gitlabCommitPath
|
||||
|
|
|
@ -51,6 +51,20 @@ RSpec.describe CommitsHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#view_file_button' do
|
||||
let(:project) { build(:project) }
|
||||
let(:path) { 'path/to/file' }
|
||||
let(:sha) { '1234567890' }
|
||||
|
||||
subject do
|
||||
helper.view_file_button(sha, path, project)
|
||||
end
|
||||
|
||||
it 'links to project files' do
|
||||
expect(subject).to have_link('1234567', href: helper.project_blob_path(project, "#{sha}/#{path}"))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#view_on_environment_button' do
|
||||
let(:project) { create(:project) }
|
||||
let(:environment) { create(:environment, external_url: 'http://example.com') }
|
||||
|
|
|
@ -303,6 +303,20 @@ RSpec.describe DiffHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#diff_file_html_data' do
|
||||
let(:project) { build(:project) }
|
||||
let(:path) { 'path/to/file' }
|
||||
let(:sha) { '1234567890' }
|
||||
|
||||
subject do
|
||||
helper.diff_file_html_data(project, path, sha)
|
||||
end
|
||||
|
||||
it 'returns data for project files' do
|
||||
expect(subject).to include(blob_diff_path: helper.project_blob_diff_path(project, "#{sha}/#{path}"))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diff_file_path_text' do
|
||||
it 'returns full path by default' do
|
||||
expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path)
|
||||
|
|
|
@ -3,6 +3,38 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WikiHelper do
|
||||
describe '#wiki_page_title' do
|
||||
let_it_be(:page) { create(:wiki_page) }
|
||||
|
||||
it 'sets the title for the show action' do
|
||||
expect(helper).to receive(:breadcrumb_title).with(page.human_title)
|
||||
expect(helper).to receive(:wiki_breadcrumb_dropdown_links).with(page.slug)
|
||||
expect(helper).to receive(:page_title).with(page.human_title, 'Wiki')
|
||||
expect(helper).to receive(:add_to_breadcrumbs).with('Wiki', helper.wiki_path(page.wiki))
|
||||
|
||||
helper.wiki_page_title(page)
|
||||
end
|
||||
|
||||
it 'sets the title for a custom action' do
|
||||
expect(helper).to receive(:breadcrumb_title).with(page.human_title)
|
||||
expect(helper).to receive(:wiki_breadcrumb_dropdown_links).with(page.slug)
|
||||
expect(helper).to receive(:page_title).with('Edit', page.human_title, 'Wiki')
|
||||
expect(helper).to receive(:add_to_breadcrumbs).with('Wiki', helper.wiki_path(page.wiki))
|
||||
|
||||
helper.wiki_page_title(page, 'Edit')
|
||||
end
|
||||
|
||||
it 'sets the title for an unsaved page' do
|
||||
expect(page).to receive(:persisted?).and_return(false)
|
||||
expect(helper).not_to receive(:breadcrumb_title)
|
||||
expect(helper).not_to receive(:wiki_breadcrumb_dropdown_links)
|
||||
expect(helper).to receive(:page_title).with('Wiki')
|
||||
expect(helper).to receive(:add_to_breadcrumbs).with('Wiki', helper.wiki_path(page.wiki))
|
||||
|
||||
helper.wiki_page_title(page)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#breadcrumb' do
|
||||
context 'when the page is at the root level' do
|
||||
it 'returns the capitalized page name' do
|
||||
|
|
|
@ -165,125 +165,127 @@ RSpec.describe Gitlab::Danger::Helper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#category_for_file' do
|
||||
where(:path, :expected_category) do
|
||||
'doc/foo' | :none
|
||||
'CONTRIBUTING.md' | :none
|
||||
'LICENSE' | :none
|
||||
'MAINTENANCE.md' | :none
|
||||
'PHILOSOPHY.md' | :none
|
||||
'PROCESS.md' | :none
|
||||
'README.md' | :none
|
||||
describe '#categories_for_file' do
|
||||
where(:path, :expected_categories) do
|
||||
'doc/foo' | [:none]
|
||||
'CONTRIBUTING.md' | [:none]
|
||||
'LICENSE' | [:none]
|
||||
'MAINTENANCE.md' | [:none]
|
||||
'PHILOSOPHY.md' | [:none]
|
||||
'PROCESS.md' | [:none]
|
||||
'README.md' | [:none]
|
||||
|
||||
'ee/doc/foo' | :unknown
|
||||
'ee/README' | :unknown
|
||||
'ee/doc/foo' | [:unknown]
|
||||
'ee/README' | [:unknown]
|
||||
|
||||
'app/assets/foo' | :frontend
|
||||
'app/views/foo' | :frontend
|
||||
'public/foo' | :frontend
|
||||
'scripts/frontend/foo' | :frontend
|
||||
'spec/javascripts/foo' | :frontend
|
||||
'spec/frontend/bar' | :frontend
|
||||
'vendor/assets/foo' | :frontend
|
||||
'babel.config.js' | :frontend
|
||||
'jest.config.js' | :frontend
|
||||
'package.json' | :frontend
|
||||
'yarn.lock' | :frontend
|
||||
'config/foo.js' | :frontend
|
||||
'config/deep/foo.js' | :frontend
|
||||
'app/assets/foo' | [:frontend]
|
||||
'app/views/foo' | [:frontend]
|
||||
'public/foo' | [:frontend]
|
||||
'scripts/frontend/foo' | [:frontend]
|
||||
'spec/javascripts/foo' | [:frontend]
|
||||
'spec/frontend/bar' | [:frontend]
|
||||
'vendor/assets/foo' | [:frontend]
|
||||
'babel.config.js' | [:frontend]
|
||||
'jest.config.js' | [:frontend]
|
||||
'package.json' | [:frontend]
|
||||
'yarn.lock' | [:frontend]
|
||||
'config/foo.js' | [:frontend]
|
||||
'config/deep/foo.js' | [:frontend]
|
||||
|
||||
'ee/app/assets/foo' | :frontend
|
||||
'ee/app/views/foo' | :frontend
|
||||
'ee/spec/javascripts/foo' | :frontend
|
||||
'ee/spec/frontend/bar' | :frontend
|
||||
'ee/app/assets/foo' | [:frontend]
|
||||
'ee/app/views/foo' | [:frontend]
|
||||
'ee/spec/javascripts/foo' | [:frontend]
|
||||
'ee/spec/frontend/bar' | [:frontend]
|
||||
|
||||
'app/models/foo' | :backend
|
||||
'bin/foo' | :backend
|
||||
'config/foo' | :backend
|
||||
'lib/foo' | :backend
|
||||
'rubocop/foo' | :backend
|
||||
'spec/foo' | :backend
|
||||
'spec/foo/bar' | :backend
|
||||
'.gitlab/ci/frontend.gitlab-ci.yml' | %i[frontend engineering_productivity]
|
||||
|
||||
'ee/app/foo' | :backend
|
||||
'ee/bin/foo' | :backend
|
||||
'ee/spec/foo' | :backend
|
||||
'ee/spec/foo/bar' | :backend
|
||||
'app/models/foo' | [:backend]
|
||||
'bin/foo' | [:backend]
|
||||
'config/foo' | [:backend]
|
||||
'lib/foo' | [:backend]
|
||||
'rubocop/foo' | [:backend]
|
||||
'spec/foo' | [:backend]
|
||||
'spec/foo/bar' | [:backend]
|
||||
|
||||
'generator_templates/foo' | :backend
|
||||
'vendor/languages.yml' | :backend
|
||||
'vendor/licenses.csv' | :backend
|
||||
'file_hooks/examples/' | :backend
|
||||
'ee/app/foo' | [:backend]
|
||||
'ee/bin/foo' | [:backend]
|
||||
'ee/spec/foo' | [:backend]
|
||||
'ee/spec/foo/bar' | [:backend]
|
||||
|
||||
'Gemfile' | :backend
|
||||
'Gemfile.lock' | :backend
|
||||
'Rakefile' | :backend
|
||||
'FOO_VERSION' | :backend
|
||||
'generator_templates/foo' | [:backend]
|
||||
'vendor/languages.yml' | [:backend]
|
||||
'vendor/licenses.csv' | [:backend]
|
||||
'file_hooks/examples/' | [:backend]
|
||||
|
||||
'Dangerfile' | :engineering_productivity
|
||||
'danger/commit_messages/Dangerfile' | :engineering_productivity
|
||||
'ee/danger/commit_messages/Dangerfile' | :engineering_productivity
|
||||
'danger/commit_messages/' | :engineering_productivity
|
||||
'ee/danger/commit_messages/' | :engineering_productivity
|
||||
'.gitlab-ci.yml' | :engineering_productivity
|
||||
'.gitlab/ci/cng.gitlab-ci.yml' | :engineering_productivity
|
||||
'.gitlab/ci/ee-specific-checks.gitlab-ci.yml' | :engineering_productivity
|
||||
'scripts/foo' | :engineering_productivity
|
||||
'lib/gitlab/danger/foo' | :engineering_productivity
|
||||
'ee/lib/gitlab/danger/foo' | :engineering_productivity
|
||||
'.overcommit.yml.example' | :engineering_productivity
|
||||
'.editorconfig' | :engineering_productivity
|
||||
'tooling/overcommit/foo' | :engineering_productivity
|
||||
'.codeclimate.yml' | :engineering_productivity
|
||||
'Gemfile' | [:backend]
|
||||
'Gemfile.lock' | [:backend]
|
||||
'Rakefile' | [:backend]
|
||||
'FOO_VERSION' | [:backend]
|
||||
|
||||
'lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml' | :backend
|
||||
'Dangerfile' | [:engineering_productivity]
|
||||
'danger/commit_messages/Dangerfile' | [:engineering_productivity]
|
||||
'ee/danger/commit_messages/Dangerfile' | [:engineering_productivity]
|
||||
'danger/commit_messages/' | [:engineering_productivity]
|
||||
'ee/danger/commit_messages/' | [:engineering_productivity]
|
||||
'.gitlab-ci.yml' | [:engineering_productivity]
|
||||
'.gitlab/ci/cng.gitlab-ci.yml' | [:engineering_productivity]
|
||||
'.gitlab/ci/ee-specific-checks.gitlab-ci.yml' | [:engineering_productivity]
|
||||
'scripts/foo' | [:engineering_productivity]
|
||||
'lib/gitlab/danger/foo' | [:engineering_productivity]
|
||||
'ee/lib/gitlab/danger/foo' | [:engineering_productivity]
|
||||
'.overcommit.yml.example' | [:engineering_productivity]
|
||||
'.editorconfig' | [:engineering_productivity]
|
||||
'tooling/overcommit/foo' | [:engineering_productivity]
|
||||
'.codeclimate.yml' | [:engineering_productivity]
|
||||
|
||||
'ee/FOO_VERSION' | :unknown
|
||||
'lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml' | [:backend]
|
||||
|
||||
'db/schema.rb' | :database
|
||||
'db/structure.sql' | :database
|
||||
'db/migrate/foo' | :database
|
||||
'db/post_migrate/foo' | :database
|
||||
'ee/db/migrate/foo' | :database
|
||||
'ee/db/post_migrate/foo' | :database
|
||||
'ee/db/geo/migrate/foo' | :database
|
||||
'ee/db/geo/post_migrate/foo' | :database
|
||||
'app/models/project_authorization.rb' | :database
|
||||
'app/services/users/refresh_authorized_projects_service.rb' | :database
|
||||
'lib/gitlab/background_migration.rb' | :database
|
||||
'lib/gitlab/background_migration/foo' | :database
|
||||
'ee/lib/gitlab/background_migration/foo' | :database
|
||||
'lib/gitlab/database.rb' | :database
|
||||
'lib/gitlab/database/foo' | :database
|
||||
'ee/lib/gitlab/database/foo' | :database
|
||||
'lib/gitlab/github_import.rb' | :database
|
||||
'lib/gitlab/github_import/foo' | :database
|
||||
'lib/gitlab/sql/foo' | :database
|
||||
'rubocop/cop/migration/foo' | :database
|
||||
'ee/FOO_VERSION' | [:unknown]
|
||||
|
||||
'db/fixtures/foo.rb' | :backend
|
||||
'ee/db/fixtures/foo.rb' | :backend
|
||||
'db/schema.rb' | [:database]
|
||||
'db/structure.sql' | [:database]
|
||||
'db/migrate/foo' | [:database]
|
||||
'db/post_migrate/foo' | [:database]
|
||||
'ee/db/migrate/foo' | [:database]
|
||||
'ee/db/post_migrate/foo' | [:database]
|
||||
'ee/db/geo/migrate/foo' | [:database]
|
||||
'ee/db/geo/post_migrate/foo' | [:database]
|
||||
'app/models/project_authorization.rb' | [:database]
|
||||
'app/services/users/refresh_authorized_projects_service.rb' | [:database]
|
||||
'lib/gitlab/background_migration.rb' | [:database]
|
||||
'lib/gitlab/background_migration/foo' | [:database]
|
||||
'ee/lib/gitlab/background_migration/foo' | [:database]
|
||||
'lib/gitlab/database.rb' | [:database]
|
||||
'lib/gitlab/database/foo' | [:database]
|
||||
'ee/lib/gitlab/database/foo' | [:database]
|
||||
'lib/gitlab/github_import.rb' | [:database]
|
||||
'lib/gitlab/github_import/foo' | [:database]
|
||||
'lib/gitlab/sql/foo' | [:database]
|
||||
'rubocop/cop/migration/foo' | [:database]
|
||||
|
||||
'qa/foo' | :qa
|
||||
'ee/qa/foo' | :qa
|
||||
'db/fixtures/foo.rb' | [:backend]
|
||||
'ee/db/fixtures/foo.rb' | [:backend]
|
||||
|
||||
'changelogs/foo' | :none
|
||||
'ee/changelogs/foo' | :none
|
||||
'locale/gitlab.pot' | :none
|
||||
'qa/foo' | [:qa]
|
||||
'ee/qa/foo' | [:qa]
|
||||
|
||||
'FOO' | :unknown
|
||||
'foo' | :unknown
|
||||
'changelogs/foo' | [:none]
|
||||
'ee/changelogs/foo' | [:none]
|
||||
'locale/gitlab.pot' | [:none]
|
||||
|
||||
'foo/bar.rb' | :backend
|
||||
'foo/bar.js' | :frontend
|
||||
'foo/bar.txt' | :none
|
||||
'foo/bar.md' | :none
|
||||
'FOO' | [:unknown]
|
||||
'foo' | [:unknown]
|
||||
|
||||
'foo/bar.rb' | [:backend]
|
||||
'foo/bar.js' | [:frontend]
|
||||
'foo/bar.txt' | [:none]
|
||||
'foo/bar.md' | [:none]
|
||||
end
|
||||
|
||||
with_them do
|
||||
subject { helper.category_for_file(path) }
|
||||
subject { helper.categories_for_file(path) }
|
||||
|
||||
it { is_expected.to eq(expected_category) }
|
||||
it { is_expected.to eq(expected_categories) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -296,6 +298,7 @@ RSpec.describe Gitlab::Danger::Helper do
|
|||
:frontend | '~frontend'
|
||||
:none | ''
|
||||
:qa | '~QA'
|
||||
:engineering_productivity | '~"Engineering Productivity" for CI, Danger'
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
@ -191,6 +191,20 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
|
|||
|
||||
it { expect(subject).to be_nil }
|
||||
end
|
||||
|
||||
context "when the pre-receive hook fails" do
|
||||
let(:response) do
|
||||
Gitaly::UserFFBranchResponse.new(
|
||||
branch_update: nil,
|
||||
pre_receive_error: "pre-receive hook error message\n"
|
||||
)
|
||||
end
|
||||
|
||||
it "raises the error" do
|
||||
# the PreReceiveError class strips the GL-HOOK-ERR prefix from this error
|
||||
expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "pre-receive hook failed.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'cherry pick and revert errors' do
|
||||
|
|
|
@ -220,7 +220,7 @@ ci_pipelines:
|
|||
- parent_pipeline
|
||||
- downstream_bridges
|
||||
- job_artifacts
|
||||
- vulnerabilities_occurrence_pipelines
|
||||
- vulnerabilities_finding_pipelines
|
||||
- vulnerability_findings
|
||||
- pipeline_config
|
||||
- security_scans
|
||||
|
|
|
@ -234,6 +234,7 @@ RSpec.describe Sentry::Client::Issue do
|
|||
:first_release_short_version | [:firstRelease, :shortVersion]
|
||||
:last_release_short_version | [:lastRelease, :shortVersion]
|
||||
:first_release_version | [:firstRelease, :version]
|
||||
:last_release_version | [:lastRelease, :version]
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
@ -864,6 +864,24 @@ RSpec.describe WikiPage do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#diffs' do
|
||||
subject { existing_page }
|
||||
|
||||
it 'returns a diff instance' do
|
||||
diffs = subject.diffs(foo: 'bar')
|
||||
|
||||
expect(diffs).to be_a(Gitlab::Diff::FileCollection::WikiPage)
|
||||
expect(diffs.diffable).to be_a(Commit)
|
||||
expect(diffs.diffable.id).to eq(subject.version.id)
|
||||
expect(diffs.project).to be(subject.wiki)
|
||||
expect(diffs.diff_options).to include(
|
||||
expanded: true,
|
||||
paths: [subject.path],
|
||||
foo: 'bar'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_slugs(page_or_dir)
|
||||
|
|
|
@ -55,7 +55,12 @@ RSpec.describe 'getting merge request information nested in a project' do
|
|||
|
||||
expect(merge_request_graphql_data).to include(
|
||||
'diffStats' => all(a_hash_including('path' => String, 'additions' => be_natural, 'deletions' => be_natural)),
|
||||
'diffStatsSummary' => a_hash_including('additions' => be_natural, 'deletions' => be_natural, 'changes' => be_natural)
|
||||
'diffStatsSummary' => a_hash_including(
|
||||
'fileCount' => merge_request.diff_stats.count,
|
||||
'additions' => be_natural,
|
||||
'deletions' => be_natural,
|
||||
'changes' => be_natural
|
||||
)
|
||||
)
|
||||
|
||||
# diff_stats is consistent with summary
|
||||
|
|
|
@ -104,6 +104,35 @@ RSpec.shared_examples 'wiki controller actions' do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'GET #diff' do
|
||||
context 'when commit exists' do
|
||||
it 'renders the diff' do
|
||||
get :diff, params: routing_params.merge(id: wiki_title, version_id: wiki.repository.commit.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template('shared/wikis/diff')
|
||||
expect(assigns(:diffs)).to be_a(Gitlab::Diff::FileCollection::Base)
|
||||
expect(assigns(:diff_notes_disabled)).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when commit does not exist' do
|
||||
it 'returns a 404 error' do
|
||||
get :diff, params: routing_params.merge(id: wiki_title, version_id: 'invalid')
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when page does not exist' do
|
||||
it 'returns a 404 error' do
|
||||
get :diff, params: routing_params.merge(id: 'invalid')
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
render_views
|
||||
|
||||
|
|
Loading…
Reference in New Issue