Commit 8f0f5070 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1b8334b3 e50f7270
<script>
import { GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
import { GlBadge, GlModal } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
import CodeBlock from '~/vue_shared/components/code_block.vue';
export default {
name: 'TestCaseDetails',
components: {
CodeBlock,
GlBadge,
GlModal,
},
props: {
......@@ -21,9 +22,35 @@ export default {
Boolean(classname) && Boolean(formattedTime) && Boolean(name),
},
},
computed: {
failureHistoryMessage() {
if (!this.hasRecentFailures) {
return null;
}
return sprintf(
n__(
'Reports|Failed %{count} time in %{baseBranch} in the last 14 days',
'Reports|Failed %{count} times in %{baseBranch} in the last 14 days',
this.recentFailures.count,
),
{
count: this.recentFailures.count,
baseBranch: this.recentFailures.base_branch,
},
);
},
hasRecentFailures() {
return Boolean(this.recentFailures);
},
recentFailures() {
return this.testCase.recent_failures;
},
},
text: {
name: __('Name'),
duration: __('Execution time'),
history: __('History'),
trace: __('System output'),
},
modalCloseButton: {
......@@ -53,6 +80,13 @@ export default {
</div>
</div>
<div v-if="testCase.recent_failures" class="gl-display-flex gl-flex-wrap gl-mx-n4 gl-my-3">
<strong class="gl-text-right col-sm-3">{{ $options.text.history }}</strong>
<div class="col-sm-9" data-testid="test-case-recent-failures">
<gl-badge variant="warning">{{ failureHistoryMessage }}</gl-badge>
</div>
</div>
<div
v-if="testCase.system_output"
class="gl-display-flex gl-flex-wrap gl-mx-n4 gl-my-3"
......
......@@ -17,15 +17,11 @@ module SpammableActions
private
def ensure_spam_config_loaded!
Gitlab::Recaptcha.load_configurations!
end
def recaptcha_check_with_fallback(should_redirect = true, &fallback)
if should_redirect && spammable.valid?
redirect_to spammable_path
elsif render_recaptcha?
ensure_spam_config_loaded!
elsif spammable.render_recaptcha?
Gitlab::Recaptcha.load_configurations!
respond_to do |format|
format.html do
......@@ -82,11 +78,4 @@ module SpammableActions
def authorize_submit_spammable!
access_denied! unless current_user.admin?
end
def render_recaptcha?
return false if spammable.errors.count > 1 # re-render "new" template in case there are other errors
return false unless Gitlab::Recaptcha.enabled?
spammable.needs_recaptcha?
end
end
# frozen_string_literal: true
module Mutations
# This concern can be mixed into a mutation to provide support for spam checking,
# and optionally support the workflow to allow clients to display and solve recaptchas.
module CanMutateSpammable
extend ActiveSupport::Concern
included do
field :spam,
GraphQL::BOOLEAN_TYPE,
null: true,
description: 'Indicates whether the operation returns a record detected as spam.'
end
# additional_spam_params -> hash
#
# Used from a spammable mutation's #resolve method to generate
# the required additional spam/recaptcha params which must be merged into the params
# passed to the constructor of a service, where they can then be used in the service
# to perform spam checking via SpamActionService.
#
# Also accesses the #context of the mutation's Resolver superclass to obtain the request.
#
# Example:
#
# existing_args.merge!(additional_spam_params)
def additional_spam_params
{
api: true,
request: context[:request]
}
end
# with_spam_action_fields(spammable) { {other_fields: true} } -> hash
#
# Takes a Spammable and a block as arguments.
#
# The block passed should be a hash, which the spam action fields will be merged into.
def with_spam_action_fields(spammable)
spam_action_fields = {
spam: spammable.spam?,
# NOTE: These fields are intentionally named with 'captcha' instead of 'recaptcha', so
# that they can be applied to future alternative captcha implementations other than
# reCAPTCHA (such as FriendlyCaptcha) without having to change the response field name
# in the API.
needs_captcha_response: spammable.render_recaptcha?,
spam_log_id: spammable.spam_log&.id,
captcha_site_key: Gitlab::CurrentSettings.recaptcha_site_key
}
yield.merge(spam_action_fields)
end
end
end
# frozen_string_literal: true
module Mutations
module SpammableMutationFields
extend ActiveSupport::Concern
included do
field :spam,
GraphQL::BOOLEAN_TYPE,
null: true,
description: 'Indicates whether the operation returns a record detected as spam.'
end
def with_spam_params(&block)
request = Feature.enabled?(:snippet_spam) ? context[:request] : nil
yield.merge({ api: true, request: request })
end
def with_spam_fields(spammable, &block)
{ spam: spammable.spam? }.merge!(yield)
end
end
end
......@@ -3,7 +3,8 @@
module Mutations
module Snippets
class Create < BaseMutation
include SpammableMutationFields
include ServiceCompatibility
include CanMutateSpammable
authorize :create_snippet
......@@ -45,18 +46,17 @@ module Mutations
authorize!(:global)
end
service_response = ::Snippets::CreateService.new(project,
current_user,
create_params(args)).execute
process_args_for_params!(args)
snippet = service_response.payload[:snippet]
service_response = ::Snippets::CreateService.new(project, current_user, args).execute
# Only when the user is not an api user and the operation was successful
if !api_user? && service_response.success?
::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user)
end
with_spam_fields(snippet) do
snippet = service_response.payload[:snippet]
with_spam_action_fields(snippet) do
{
snippet: service_response.success? ? snippet : nil,
errors: errors_on_object(snippet)
......@@ -70,18 +70,25 @@ module Mutations
Project.find_by_full_path(full_path)
end
def create_params(args)
with_spam_params do
args.tap do |create_args|
# We need to rename `blob_actions` into `snippet_actions` because
# it's the expected key param
create_args[:snippet_actions] = create_args.delete(:blob_actions)&.map(&:to_h)
# We need to rename `uploaded_files` into `files` because
# it's the expected key param
create_args[:files] = create_args.delete(:uploaded_files)
end
# process_args_for_params!(args) -> nil
#
# Modifies/adds/deletes mutation resolve args as necessary to be passed as params to service layer.
def process_args_for_params!(args)
convert_blob_actions_to_snippet_actions!(args)
# We need to rename `uploaded_files` into `files` because
# it's the expected key param
args[:files] = args.delete(:uploaded_files)
if Feature.enabled?(:snippet_spam)
args.merge!(additional_spam_params)
else
args[:disable_spam_action_service] = true
end
# Return nil to make it explicit that this method is mutating the args parameter, and that
# the return value is not relevant and is not to be used.
nil
end
end
end
......
# frozen_string_literal: true
module Mutations
module Snippets
# Translates graphql mutation field params to be compatible with those expected by the service layer
module ServiceCompatibility
extend ActiveSupport::Concern
# convert_blob_actions_to_snippet_actions!(args) -> nil
#
# Converts the blob_actions mutation argument into the
# snippet_actions hash which the service layer expects
def convert_blob_actions_to_snippet_actions!(args)
# We need to rename `blob_actions` into `snippet_actions` because
# it's the expected key param
args[:snippet_actions] = args.delete(:blob_actions)&.map(&:to_h)
# Return nil to make it explicit that this method is mutating the args parameter
nil
end
end
end
end
......@@ -3,7 +3,8 @@
module Mutations
module Snippets
class Update < Base
include SpammableMutationFields
include ServiceCompatibility
include CanMutateSpammable
graphql_name 'UpdateSnippet'
......@@ -30,19 +31,23 @@ module Mutations
def resolve(id:, **args)
snippet = authorized_find!(id: id)
result = ::Snippets::UpdateService.new(snippet.project,
current_user,
update_params(args)).execute(snippet)
snippet = result.payload[:snippet]
process_args_for_params!(args)
service_response = ::Snippets::UpdateService.new(snippet.project, current_user, args).execute(snippet)
# TODO: DRY this up - From here down, this is all duplicated with Mutations::Snippets::Create#resolve, except for
# `snippet.reset`, which is required in order to return the object in its non-dirty, unmodified, database state
# See issue here: https://gitlab.com/gitlab-org/gitlab/-/issues/300250
# Only when the user is not an api user and the operation was successful
if !api_user? && result.success?
if !api_user? && service_response.success?
::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user)
end
with_spam_fields(snippet) do
snippet = service_response.payload[:snippet]
with_spam_action_fields(snippet) do
{
snippet: result.success? ? snippet : snippet.reset,
snippet: service_response.success? ? snippet : snippet.reset,
errors: errors_on_object(snippet)
}
end
......@@ -50,18 +55,25 @@ module Mutations
private
def ability_name
'update'
end
# process_args_for_params!(args) -> nil
#
# Modifies/adds/deletes mutation resolve args as necessary to be passed as params to service layer.
def process_args_for_params!(args)
convert_blob_actions_to_snippet_actions!(args)
def update_params(args)
with_spam_params do
args.tap do |update_args|
# We need to rename `blob_actions` into `snippet_actions` because
# it's the expected key param
update_args[:snippet_actions] = update_args.delete(:blob_actions)&.map(&:to_h)
end
if Feature.enabled?(:snippet_spam)
args.merge!(additional_spam_params)
else
args[:disable_spam_action_service] = true
end
# Return nil to make it explicit that this method is mutating the args parameter, and that
# the return value is not relevant and is not to be used.
nil
end
def ability_name
'update'
end
end
end
......
......@@ -45,6 +45,17 @@ module Spammable
self.needs_recaptcha = true
end
##
# Indicates if a recaptcha should be rendered before allowing this model to be saved.
#
def render_recaptcha?
return false unless Gitlab::Recaptcha.enabled?
return false if self.errors.count > 1 # captcha should not be rendered if are still other errors
self.needs_recaptcha?
end
def spam!
self.spam = true
end
......
......@@ -3,6 +3,8 @@
module Snippets
class CreateService < Snippets::BaseService
def execute
# NOTE: disable_spam_action_service can be removed when the ':snippet_spam' feature flag is removed.
disable_spam_action_service = params.delete(:disable_spam_action_service) == true
@request = params.delete(:request)
@spam_params = Spam::SpamActionService.filter_spam_params!(params)
......@@ -16,12 +18,14 @@ module Snippets
@snippet.author = current_user
Spam::SpamActionService.new(
spammable: @snippet,
request: request,
user: current_user,
action: :create
).execute(spam_params: spam_params)
unless disable_spam_action_service
Spam::SpamActionService.new(
spammable: @snippet,
request: request,
user: current_user,
action: :create
).execute(spam_params: spam_params)
end
if save_and_commit
UserAgentDetailService.new(@snippet, request).create
......
......@@ -7,6 +7,8 @@ module Snippets
UpdateError = Class.new(StandardError)
def execute(snippet)
# NOTE: disable_spam_action_service can be removed when the ':snippet_spam' feature flag is removed.
disable_spam_action_service = params.delete(:disable_spam_action_service) == true
@request = params.delete(:request)
@spam_params = Spam::SpamActionService.filter_spam_params!(params)
......@@ -17,17 +19,20 @@ module Snippets
end
update_snippet_attributes(snippet)
Spam::SpamActionService.new(
spammable: snippet,
request: request,
user: current_user,
action: :update
).execute(spam_params: spam_params)
unless disable_spam_action_service
Spam::SpamActionService.new(
spammable: snippet,
request: request,
user: current_user,
action: :update
).execute(spam_params: spam_params)
end
if save_and_commit(snippet)
Gitlab::UsageDataCounters::SnippetCounter.count(:update)
ServiceResponse.success(payload: { snippet: snippet } )
ServiceResponse.success(payload: { snippet: snippet })
else
snippet_error_response(snippet, 400)
end
......
---
title: Show recent test case failures in the pipeline test report
merge_request: 52606
author:
type: added
......@@ -26,4 +26,4 @@ relevant compliance standards.
|**[Audit events](audit_events.md)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit events system, so you can control, analyze, and track every change.|Premium+|✓|Instance, Group, Project|
|**[Auditor users](auditor_users.md)**<br>Auditor users are users who are given read-only access to all projects, groups, and other resources on the GitLab instance.|Premium+||Instance|
|**[Credentials inventory](../user/admin_area/credentials_inventory.md)**<br>With a credentials inventory, GitLab administrators can keep track of the credentials used by all of the users in their GitLab instance. |Ultimate||Instance|
|**Separation of Duties using [Protected branches](../user/project/protected_branches.md#protected-branches-approval-by-code-owners) and [custom CI Configuration Paths](../ci/pipelines/settings.md#custom-cicd-configuration-path)**<br> GitLab Silver and Premium users can leverage the GitLab cross-project YAML configurations to define deployers of code and developers of code. View the [Separation of Duties Deploy Project](https://gitlab.com/guided-explorations/separation-of-duties-deploy/blob/master/README.md) and [Separation of Duties Project](https://gitlab.com/guided-explorations/separation-of-duties/blob/master/README.md) to see how to use this set up to define these roles.|Premium+|✓|Project|
|**Separation of Duties using [Protected branches](../user/project/protected_branches.md#protected-branches-approval-by-code-owners) and [custom CI Configuration Paths](../ci/pipelines/settings.md#custom-cicd-configuration-path)**<br> GitLab Premium users can leverage the GitLab cross-project YAML configurations to define deployers of code and developers of code. View the [Separation of Duties Deploy Project](https://gitlab.com/guided-explorations/separation-of-duties-deploy/blob/master/README.md) and [Separation of Duties Project](https://gitlab.com/guided-explorations/separation-of-duties/blob/master/README.md) to see how to use this set up to define these roles.|Premium+|✓|Project|
......@@ -15,7 +15,7 @@ Every API call to `epic_links` must be authenticated.
If a user is not a member of a private group, a `GET` request on that
group results in a `404` status code.
Multi-level Epics are available only in GitLab [Ultimate/Gold](https://about.gitlab.com/pricing/).
Multi-level Epics are available only in GitLab [Ultimate](https://about.gitlab.com/pricing/).
If the Multi-level Epics feature is not available, a `403` status code is returned.
## List epics related to a given epic
......
......@@ -196,7 +196,7 @@ Example responses:
}
```
Users on GitLab [Ultimate or Gold](https://about.gitlab.com/pricing/) also see the `new_epic`
Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) also see the `new_epic`
parameter:
```json
......
......@@ -53,4 +53,4 @@ You can add up to 150 projects for GitLab to display on this dashboard.
GitLab.com users can add public projects to the Environments
Dashboard for free. If your project is private, the group it belongs
to must have a [GitLab Silver](https://about.gitlab.com/pricing/) plan.
to must have a [GitLab Premium](https://about.gitlab.com/pricing/) plan.
......@@ -67,8 +67,9 @@ execution time and the error output.
### Number of recent failures
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241759) in GitLab 13.7.
> - [Introduced in Merge Requests](https://gitlab.com/gitlab-org/gitlab/-/issues/241759) in GitLab 13.7.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/268249) in GitLab 13.8.
> - [Introduced in Test Reports](https://gitlab.com/gitlab-org/gitlab/-/issues/235525) in GitLab 13.9.
If a test failed in the project's default branch in the last 14 days, a message like
`Failed {n} time(s) in {default_branch} in the last 14 days` is displayed for that test.
......
......@@ -19,7 +19,7 @@ integration as well as linking to more detailed resources for how to do so.
## Integration Tiers
The security offerings in GitLab are designed for GitLab Gold and GitLab Ultimate users, and the
The security offerings in GitLab are designed for GitLab Ultimate users, and the
[DevSecOps](https://about.gitlab.com/handbook/use-cases/#4-devsecops-shift-left-security)
use case. All the features are in those tiers. This includes the APIs and standard reporting
framework needed to provide a consistent experience for users to easily bring their preferred
......@@ -75,7 +75,7 @@ and complete an integration with the Secure stage.
1. [Create an issue](https://gitlab.com/gitlab-com/alliances/alliances/-/issues/new?issuable_template=new_partner)
using our new partner issue template to begin the discussion.
1. Get a test account to begin developing your integration. You can
request a [GitLab.com Gold Subscription Sandbox](https://about.gitlab.com/partners/integrate/#gitlabcom-gold-subscription-sandbox-request)
request a [GitLab.com Subscription Sandbox](https://about.gitlab.com/partners/integrate/#gitlabcom-subscription-sandbox-request)
or an [EE Developer License](https://about.gitlab.com/partners/integrate/#requesting-ee-dev-license-for-rd).
1. Provide a [pipeline job](../../development/pipelines.md)
template that users could integrate into their own GitLab pipelines.
......
......@@ -70,6 +70,7 @@ the tiers are no longer mentioned in GitLab documentation:
- [Code Owners as eligible approvers](../user/project/merge_requests/merge_request_approvals.md#code-owners-as-eligible-approvers)
- All [Approval rules](../user/project/merge_requests/merge_request_approvals.md#approval-rules) features
- [Restricting push and merge access to certain users](../user/project/protected_branches.md#restricting-push-and-merge-access-to-certain-users)
- [Visual Reviews](../ci/review_apps/index.md#visual-reviews)
- Metrics and analytics:
- [Contribution Analytics](../user/group/contribution_analytics/index.md)
- [Merge Request Analytics](../user/analytics/merge_request_analytics.md)
......@@ -132,5 +133,6 @@ Bronze-level subscribers:
- Fields in the [Merge requests API](../api/merge_requests.md) for [merge request approvals](../user/project/merge_requests/merge_request_approvals.md)
- Fields in the [Protected branches API](../api/protected_branches.md) that specify users or groups allowed to merge
- [Merge request approvals API](../api/merge_request_approvals.md)
- [Visual review discussions API](../api/visual_review_discussions.md)
- Development information:
- [Run Jenkins in a macOS development environment](../development/integrations/jenkins.md)
......@@ -46,11 +46,11 @@ at any given time doesn't exceed the subscription count.
Every occupied seat is counted in the subscription, with the following exception:
- Members with Guest permissions on a Gold subscription.
- Members with Guest permissions on an Ultimate subscription.
NOTE:
To support the open source community and encourage the development of open
source projects, GitLab grants access to **Gold** features for all GitLab.com
source projects, GitLab grants access to **Ultimate** features for all GitLab.com
**public** projects, regardless of the subscription.
## Obtain a GitLab.com subscription
......@@ -62,7 +62,7 @@ To subscribe to GitLab.com:
[sign up page](https://gitlab.com/users/sign_up).
1. Visit the [billing page](https://gitlab.com/profile/billings)
under your profile.
1. Select the **Bronze**, **Silver**, or **Gold** GitLab.com plan through the
1. Select the **Bronze**, **Premium**, or **Ultimate** GitLab.com plan through the
[Customers Portal](https://customers.gitlab.com/).
1. Link your GitLab.com account with your Customers Portal account.
Once a plan has been selected, if your account is not
......@@ -79,7 +79,7 @@ To subscribe to GitLab.com:
namespace.
1. Create additional users and
[add them to the group](../../user/group/index.md#add-users-to-a-group).
1. Select the **Bronze**, **Silver**, or **Gold** GitLab.com plan through the
1. Select the **Bronze**, **Premium**, or **Ultimate** GitLab.com plan through the
[Customers Portal](https://customers.gitlab.com/).
1. Link your GitLab.com account with your Customers Portal account.
Once a plan has been selected, if your account is not
......@@ -271,8 +271,8 @@ of CI pipeline minutes:
- Free: 400 minutes
- Bronze: 2,000 minutes
- Silver: 10,000 minutes
- Gold: 50,000 minutes
- Premium: 10,000 minutes
- Ultimate: 50,000 minutes
Quotas apply to:
......@@ -290,7 +290,7 @@ The available quota is reset on the first of each calendar month at midnight UTC
When the CI minutes are depleted, an email is sent automatically to notify the owner(s)
of the namespace. You can [purchase additional CI minutes](#purchase-additional-ci-minutes),
or upgrade your account to [Silver or Gold](https://about.gitlab.com/pricing/).
or upgrade your account to [Premium or Ultimate](https://about.gitlab.com/pricing/).
Your own runners can still be used even if you reach your limits.
### Purchase additional CI minutes
......
......@@ -51,7 +51,7 @@ those projects provide a bare-bones application built on some well-known framewo
1. Give your project a name, optionally a description, and make it public so that
you can take advantage of the features available in the
[GitLab Gold plan](https://about.gitlab.com/pricing/#gitlab-com).
[GitLab Ultimate plan](https://about.gitlab.com/pricing/).
![Create project](img/guide_create_project_v12_3.png)
......
......@@ -39,6 +39,8 @@ To learn how to create templates for various file types in groups, visit
images guidelines, link to the related issue, reviewer name, and so on.
- You can also create issues and merge request templates for different
stages of your workflow, for example, feature proposal, feature improvement, or a bug report.
- You can use an [issue description template](#creating-issue-templates) as a
[Service Desk email template](service_desk.md#new-service-desk-issues).
## Creating issue templates
......
......@@ -34,7 +34,7 @@ It provides a unique email address for end users to create issues in a project.
Follow-up notes can be sent either through the GitLab interface or by email. End
users only see the thread through email.
For instance, let's assume you develop a game for iOS or Android.
For example, let's assume you develop a game for iOS or Android.
The codebase is hosted in your GitLab instance, built and deployed
with GitLab CI/CD.
......@@ -98,32 +98,52 @@ An email is sent to the author when:
- A user submits a new issue using Service Desk.
- A new note is created on a Service Desk issue.
The body of these email messages can be customized by using templates. To create a new customized template,
create a new Markdown (`.md`) file inside the `.gitlab/service_desk_templates/`
directory in your repository. Commit and push to your default branch.
You can customize the body of these email messages with templates.
Save your templates in the `.gitlab/service_desk_templates/`
directory in your repository.
With Service Desk, you can use templates for:
- [Thank you emails](#thank-you-email)
- [New note emails](#new-note-email)
- [New Service Desk issues](#new-service-desk-issues)
#### Thank you email
The **Thank you email** is the email sent to a user after they submit an issue.
The filename of the template has to be `thank_you.md`.
There are a few placeholders you can use which are automatically replaced in the email:
When a user submits an issue through Service Desk, GitLab sends a **thank you email**.
You must name the template file `thank_you.md`.
You can use these placeholders to be automatically replaced in each email:
- `%{ISSUE_ID}`: issue IID
- `%{ISSUE_PATH}`: project path appended with the issue IID
As the Service Desk issues are created as confidential (only project members can see them)
the response email does not provide the issue link.
Because Service Desk issues are created as [confidential](issues/confidential_issues.md) (only project members can see them),
the response email does not contain the issue link.
#### New note email
When a user-submitted issue receives a new comment, GitLab sends a **New note email**
to the user. The filename of this template must be `new_note.md`, and you can
use these placeholders in the email:
When a user-submitted issue receives a new comment, GitLab sends a **new note email**.
You must name the template file `new_note.md`.
You can use these placeholders to be automatically replaced in each email:
- `%{ISSUE_ID}`: issue IID
- `%{ISSUE_PATH}`: project path appended with the issue IID
- `%{NOTE_TEXT}`: note text
#### New Service Desk issues
You can select one [issue description template](description_templates.md#creating-issue-templates)
**per project** to be appended to every new Service Desk issue's description.
Issue description templates should reside in your repository's `.gitlab/issue_templates/` directory.
To use a custom issue template with Service Desk, in your project:
1. [Create a description template](description_templates.md#creating-issue-templates)
1. Go to **Settings > General > Service Desk**.
1. From the dropdown **Template to append to all Service Desk issues**, select your template.
### Using custom email display name
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7529) in GitLab 12.8.
......
......@@ -24059,6 +24059,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
msgid "Reports|Failed %{count} time in %{baseBranch} in the last 14 days"
msgid_plural "Reports|Failed %{count} times in %{baseBranch} in the last 14 days"
msgstr[0] ""
msgstr[1] ""
msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
msgstr[0] ""
......
......@@ -68,12 +68,9 @@ RSpec.describe SpammableActions do
allow(spammable).to receive(:valid?) { false }
end
# NOTE: Not adding coverage of details of render_recaptcha?, the plan is to refactor it out
# of this module anyway as part of adding support for the GraphQL reCAPTCHA flow.
context 'when render_recaptcha? is true' do
context 'when spammable.render_recaptcha? is true' do
before do
expect(controller).to receive(:render_recaptcha?) { true }
expect(spammable).to receive(:render_recaptcha?) { true }
end
context 'when format is :html' do
......
......@@ -11,12 +11,17 @@ describe('Test case details', () => {
classname: 'spec.test_spec',
name: 'Test#something cool',
formattedTime: '10.04ms',
recent_failures: {
count: 2,
base_branch: 'master',
},
system_output: 'Line 42 is broken',
};
const findModal = () => wrapper.find(GlModal);
const findName = () => wrapper.find('[data-testid="test-case-name"]');
const findDuration = () => wrapper.find('[data-testid="test-case-duration"]');
const findRecentFailures = () => wrapper.find('[data-testid="test-case-recent-failures"]');
const findSystemOutput = () => wrapper.find('[data-testid="test-case-trace"]');
const createComponent = (testCase = {}) => {
......@@ -56,6 +61,36 @@ describe('Test case details', () => {
});
});
describe('when test case has recent failures', () => {
describe('has only 1 recent failure', () => {
it('renders the recent failure', () => {
createComponent({ recent_failures: { ...defaultTestCase.recent_failures, count: 1 } });
expect(findRecentFailures().text()).toContain(
`Failed 1 time in ${defaultTestCase.recent_failures.base_branch} in the last 14 days`,
);
});
});
describe('has more than 1 recent failure', () => {
it('renders the recent failures', () => {
createComponent();
expect(findRecentFailures().text()).toContain(
`Failed ${defaultTestCase.recent_failures.count} times in ${defaultTestCase.recent_failures.base_branch} in the last 14 days`,
);
});
});
});
describe('when test case does not have recent failures', () => {
it('does not render the recent failures', () => {
createComponent({ recent_failures: null });
expect(findRecentFailures().exists()).toBe(false);
});
});
describe('when test case has system output', () => {
it('renders the test case system output', () => {
createComponent();
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::CanMutateSpammable do
let(:mutation_class) do
Class.new(Mutations::BaseMutation) do
include Mutations::CanMutateSpammable
end
end
let(:request) { double(:request) }
let(:query) { double(:query, schema: GitlabSchema) }
let(:context) { GraphQL::Query::Context.new(query: query, object: nil, values: { request: request }) }
subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
describe '#additional_spam_params' do
it 'returns additional spam-related params' do
expect(subject.additional_spam_params).to eq({ api: true, request: request })
end
end
describe '#with_spam_action_fields' do
let(:spam_log) { double(:spam_log, id: 1) }
let(:spammable) { double(:spammable, spam?: true, render_recaptcha?: true, spam_log: spam_log) }
before do
allow(Gitlab::CurrentSettings).to receive(:recaptcha_site_key) { 'abc123' }
end
it 'merges in spam action fields from spammable' do
result = subject.with_spam_action_fields(spammable) do
{ other_field: true }
end
expect(result)
.to eq({
spam: true,
needs_captcha_response: true,
spam_log_id: 1,
captcha_site_key: 'abc123',
other_field: true
})
end
end
end
......@@ -120,6 +120,61 @@ RSpec.describe Spammable do
end
end
describe '#render_recaptcha?' do
before do
allow(Gitlab::Recaptcha).to receive(:enabled?) { recaptcha_enabled }
end
context 'when recaptcha is not enabled' do
let(:recaptcha_enabled) { false }
it 'returns false' do
expect(issue.render_recaptcha?).to eq(false)
end
end
context 'when recaptcha is enabled' do
let(:recaptcha_enabled) { true }
context 'when there are two or more errors' do
before do
issue.errors.add(:base, 'a spam error')
issue.errors.add(:base, 'some other error')
end
it 'returns false' do
expect(issue.render_recaptcha?).to eq(false)
end
end
context 'when there are less than two errors' do
before do
issue.errors.add(:base, 'a spam error')
end
context 'when spammable does not need recaptcha' do
before do
issue.needs_recaptcha = false
end
it 'returns false' do
expect(issue.render_recaptcha?).to eq(false)
end
end
context 'when spammable needs recaptcha' do
before do
issue.needs_recaptcha!
end
it 'returns false' do
expect(issue.render_recaptcha?).to eq(true)
end
end
end
end
end
describe '#clear_spam_flags!' do
it 'clears spam and recaptcha flags' do
issue.spam = true
......
......@@ -78,6 +78,21 @@ RSpec.describe 'Creating a Snippet' do
end
it_behaves_like 'spam flag is present'
context 'when snippet_spam flag is disabled' do
before do
stub_feature_flags(snippet_spam: false)
end
it 'passes disable_spam_action_service param to service' do
expect(::Snippets::CreateService)
.to receive(:new)
.with(anything, anything, hash_including(disable_spam_action_service: true))
.and_call_original
subject
end
end
end
shared_examples 'creates snippet' do
......
......@@ -87,6 +87,21 @@ RSpec.describe 'Updating a Snippet' do
it_behaves_like 'spam flag is present'
context 'when snippet_spam flag is disabled' do
before do
stub_feature_flags(snippet_spam: false)
end
it 'passes disable_spam_action_service param to service' do
expect(::Snippets::UpdateService)
.to receive(:new)
.with(anything, anything, hash_including(disable_spam_action_service: true))
.and_call_original
subject
end
end
context 'when there are ActiveRecord validation errors' do
let(:updated_title) { '' }
......
......@@ -33,18 +33,4 @@ RSpec.shared_examples 'can raise spam flag' do
expect(mutation_response['errors']).to include("Your snippet has been recognized as spam and has been discarded.")
end
end
context 'when :snippet_spam flag is disabled' do
before do
stub_feature_flags(snippet_spam: false)
end
it 'request parameter is not passed to the service' do
expect(service).to receive(:new)
.with(anything, anything, hash_not_including(request: instance_of(ActionDispatch::Request)))
.and_call_original
subject
end
end
end
......@@ -5,13 +5,15 @@ RSpec.shared_examples 'checking spam' do
let(:api) { true }
let(:captcha_response) { 'abc123' }
let(:spam_log_id) { 1 }
let(:disable_spam_action_service) { false }
let(:extra_opts) do
{
request: request,
api: api,
captcha_response: captcha_response,
spam_log_id: spam_log_id
spam_log_id: spam_log_id,
disable_spam_action_service: disable_spam_action_service
}
end
......@@ -41,6 +43,16 @@ RSpec.shared_examples 'checking spam' do
subject
end
context 'when spam action service is disabled' do
let(:disable_spam_action_service) { true }
it 'request parameter is not passed to the service' do
expect(Spam::SpamActionService).not_to receive(:new)
subject
end
end
end
shared_examples 'invalid params error response' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment