Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0ab17699c8
commit
14cb5b3d79
|
|
@ -32,6 +32,7 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
inject: ['blobHash'],
|
||||
computed: {
|
||||
downloadUrl() {
|
||||
return `${this.rawPath}?inline=false`;
|
||||
|
|
@ -39,6 +40,9 @@ export default {
|
|||
copyDisabled() {
|
||||
return this.activeViewer === RICH_BLOB_VIEWER;
|
||||
},
|
||||
getBlobHashTarget() {
|
||||
return `[data-blob-hash="${this.blobHash}"]`;
|
||||
},
|
||||
},
|
||||
BTN_COPY_CONTENTS_TITLE,
|
||||
BTN_DOWNLOAD_TITLE,
|
||||
|
|
@ -53,7 +57,7 @@ export default {
|
|||
:aria-label="$options.BTN_COPY_CONTENTS_TITLE"
|
||||
:title="$options.BTN_COPY_CONTENTS_TITLE"
|
||||
:disabled="copyDisabled"
|
||||
data-clipboard-target="#blob-code-content"
|
||||
:data-clipboard-target="getBlobHashTarget"
|
||||
data-testid="copyContentsButton"
|
||||
icon="copy-to-clipboard"
|
||||
category="primary"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ issue: 'activeIssue' }),
|
||||
...mapGetters({ issue: 'activeIssue', projectPathForActiveIssue: 'projectPathForActiveIssue' }),
|
||||
hasDueDate() {
|
||||
return this.issue.dueDate != null;
|
||||
},
|
||||
|
|
@ -36,10 +36,6 @@ export default {
|
|||
|
||||
return dateInWords(this.parsedDueDate, true);
|
||||
},
|
||||
projectPath() {
|
||||
const referencePath = this.issue.referencePath || '';
|
||||
return referencePath.slice(0, referencePath.indexOf('#'));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveIssueDueDate']),
|
||||
|
|
@ -53,7 +49,7 @@ export default {
|
|||
|
||||
try {
|
||||
const dueDate = date ? formatDate(date, 'yyyy-mm-dd') : null;
|
||||
await this.setActiveIssueDueDate({ dueDate, projectPath: this.projectPath });
|
||||
await this.setActiveIssueDueDate({ dueDate, projectPath: this.projectPathForActiveIssue });
|
||||
} catch (e) {
|
||||
createFlash({ message: this.$options.i18n.updateDueDateError });
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ export default {
|
|||
},
|
||||
inject: ['labelsFetchPath', 'labelsManagePath', 'labelsFilterBasePath'],
|
||||
computed: {
|
||||
...mapGetters({ issue: 'activeIssue' }),
|
||||
...mapGetters(['activeIssue', 'projectPathForActiveIssue']),
|
||||
selectedLabels() {
|
||||
const { labels = [] } = this.issue;
|
||||
const { labels = [] } = this.activeIssue;
|
||||
|
||||
return labels.map(label => ({
|
||||
...label,
|
||||
|
|
@ -31,17 +31,13 @@ export default {
|
|||
}));
|
||||
},
|
||||
issueLabels() {
|
||||
const { labels = [] } = this.issue;
|
||||
const { labels = [] } = this.activeIssue;
|
||||
|
||||
return labels.map(label => ({
|
||||
...label,
|
||||
scoped: isScopedLabel(label),
|
||||
}));
|
||||
},
|
||||
projectPath() {
|
||||
const { referencePath = '' } = this.issue;
|
||||
return referencePath.slice(0, referencePath.indexOf('#'));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveIssueLabels']),
|
||||
|
|
@ -55,7 +51,7 @@ export default {
|
|||
.filter(label => !payload.find(selected => selected.id === label.id))
|
||||
.map(label => label.id);
|
||||
|
||||
const input = { addLabelIds, removeLabelIds, projectPath: this.projectPath };
|
||||
const input = { addLabelIds, removeLabelIds, projectPath: this.projectPathForActiveIssue };
|
||||
await this.setActiveIssueLabels(input);
|
||||
} catch (e) {
|
||||
createFlash({ message: __('An error occurred while updating labels.') });
|
||||
|
|
@ -68,7 +64,7 @@ export default {
|
|||
|
||||
try {
|
||||
const removeLabelIds = [getIdFromGraphQLId(id)];
|
||||
const input = { removeLabelIds, projectPath: this.projectPath };
|
||||
const input = { removeLabelIds, projectPath: this.projectPathForActiveIssue };
|
||||
await this.setActiveIssueLabels(input);
|
||||
} catch (e) {
|
||||
createFlash({ message: __('An error occurred when removing the label.') });
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { GlToggle } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
header: {
|
||||
title: __('Notifications'),
|
||||
/* Any change to subscribeDisabledDescription
|
||||
must be reflected in app/helpers/notifications_helper.rb */
|
||||
subscribeDisabledDescription: __(
|
||||
'Notifications have been disabled by the project or group owner',
|
||||
),
|
||||
},
|
||||
updateSubscribedErrorMessage: s__(
|
||||
'IssueBoards|An error occurred while setting notifications status.',
|
||||
),
|
||||
},
|
||||
components: {
|
||||
GlToggle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['activeIssue', 'projectPathForActiveIssue']),
|
||||
notificationText() {
|
||||
return this.activeIssue.emailsDisabled
|
||||
? this.$options.i18n.header.subscribeDisabledDescription
|
||||
: this.$options.i18n.header.title;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveIssueSubscribed']),
|
||||
async handleToggleSubscription() {
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
await this.setActiveIssueSubscribed({
|
||||
subscribed: !this.activeIssue.subscribed,
|
||||
projectPath: this.projectPathForActiveIssue,
|
||||
});
|
||||
} catch (error) {
|
||||
createFlash({ message: this.$options.i18n.updateSubscribedErrorMessage });
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="gl-display-flex gl-align-items-center gl-justify-content-space-between"
|
||||
data-testid="sidebar-notifications"
|
||||
>
|
||||
<span data-testid="notification-header-text"> {{ notificationText }} </span>
|
||||
<gl-toggle
|
||||
v-if="!activeIssue.emailsDisabled"
|
||||
:value="activeIssue.subscribed"
|
||||
:is-loading="loading"
|
||||
data-testid="notification-subscribe-toggle"
|
||||
@change="handleToggleSubscription"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
mutation issueSetSubscription($input: IssueSetSubscriptionInput!) {
|
||||
issueSetSubscription(input: $input) {
|
||||
issue {
|
||||
subscribed
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import destroyBoardListMutation from '../queries/board_list_destroy.mutation.gra
|
|||
import issueCreateMutation from '../queries/issue_create.mutation.graphql';
|
||||
import issueSetLabels from '../queries/issue_set_labels.mutation.graphql';
|
||||
import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql';
|
||||
import issueSetSubscriptionMutation from '../graphql/mutations/issue_set_subscription.mutation.graphql';
|
||||
|
||||
const notImplemented = () => {
|
||||
/* eslint-disable-next-line @gitlab/require-i18n-strings */
|
||||
|
|
@ -423,6 +424,29 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
setActiveIssueSubscribed: async ({ commit, getters }, input) => {
|
||||
const { data } = await gqlClient.mutate({
|
||||
mutation: issueSetSubscriptionMutation,
|
||||
variables: {
|
||||
input: {
|
||||
iid: String(getters.activeIssue.iid),
|
||||
projectPath: input.projectPath,
|
||||
subscribedState: input.subscribed,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (data.issueSetSubscription?.errors?.length > 0) {
|
||||
throw new Error(data.issueSetSubscription.errors);
|
||||
}
|
||||
|
||||
commit(types.UPDATE_ISSUE_BY_ID, {
|
||||
issueId: getters.activeIssue.id,
|
||||
prop: 'subscribed',
|
||||
value: data.issueSetSubscription.issue.subscribed,
|
||||
});
|
||||
},
|
||||
|
||||
fetchBacklog: () => {
|
||||
notImplemented();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ export default {
|
|||
return state.issues[state.activeId] || {};
|
||||
},
|
||||
|
||||
projectPathForActiveIssue: (_, getters) => {
|
||||
const referencePath = getters.activeIssue.referencePath || '';
|
||||
return referencePath.slice(0, referencePath.indexOf('#'));
|
||||
},
|
||||
|
||||
getListByLabelId: state => labelId => {
|
||||
return find(state.boardLists, l => l.label?.id === labelId);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const PERSISTENT_USER_CALLOUTS = [
|
|||
'.js-admin-licensed-user-count-threshold',
|
||||
'.js-buy-pipeline-minutes-notification-callout',
|
||||
'.js-token-expiry-callout',
|
||||
'.js-registration-enabled-callout',
|
||||
];
|
||||
|
||||
const initCallouts = () => {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
blobHash: Math.random()
|
||||
.toString()
|
||||
.split('.')[1],
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
blobContent: '',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export default {
|
|||
GlIcon,
|
||||
},
|
||||
mixins: [ViewerMixin],
|
||||
inject: ['blobHash'],
|
||||
data() {
|
||||
return {
|
||||
highlightedLine: null,
|
||||
|
|
@ -64,7 +65,7 @@ export default {
|
|||
</a>
|
||||
</div>
|
||||
<div class="blob-content">
|
||||
<pre class="code highlight"><code id="blob-code-content" v-html="content"></code></pre>
|
||||
<pre class="code highlight"><code :data-blob-hash="blobHash" v-html="content"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -394,6 +394,10 @@ module ApplicationSettingsHelper
|
|||
def show_documentation_base_url_field?
|
||||
Feature.enabled?(:help_page_documentation_redirect)
|
||||
end
|
||||
|
||||
def signup_enabled?
|
||||
!!Gitlab::CurrentSettings.signup_enabled
|
||||
end
|
||||
end
|
||||
|
||||
ApplicationSettingsHelper.prepend_if_ee('EE::ApplicationSettingsHelper')
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ module NotificationsHelper
|
|||
when :custom
|
||||
_('You will only receive notifications for the events you choose')
|
||||
when :owner_disabled
|
||||
# Any change must be reflected in board_sidebar_subscription.vue
|
||||
_('Notifications have been disabled by the project or group owner')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ module UserCalloutsHelper
|
|||
WEBHOOKS_MOVED = 'webhooks_moved'
|
||||
CUSTOMIZE_HOMEPAGE = 'customize_homepage'
|
||||
FEATURE_FLAGS_NEW_VERSION = 'feature_flags_new_version'
|
||||
REGISTRATION_ENABLED_CALLOUT = 'registration_enabled_callout'
|
||||
|
||||
def show_admin_integrations_moved?
|
||||
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
|
||||
|
|
@ -55,6 +56,10 @@ module UserCalloutsHelper
|
|||
!user_dismissed?(FEATURE_FLAGS_NEW_VERSION)
|
||||
end
|
||||
|
||||
def show_registration_enabled_user_callout?
|
||||
current_user&.admin? && signup_enabled? && !user_dismissed?(REGISTRATION_ENABLED_CALLOUT)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ module Ci
|
|||
raise ArgumentError, 'Offset is out of range' if offset < 0 || offset > size
|
||||
raise ArgumentError, 'Chunk size overflow' if CHUNK_SIZE < (offset + new_data.bytesize)
|
||||
|
||||
in_lock(*lock_params) { unsafe_append_data!(new_data, offset) }
|
||||
in_lock(lock_key, **lock_params) { unsafe_append_data!(new_data, offset) }
|
||||
|
||||
schedule_to_persist! if full?
|
||||
end
|
||||
|
|
@ -151,7 +151,7 @@ module Ci
|
|||
# acquired
|
||||
#
|
||||
def persist_data!
|
||||
in_lock(*lock_params) do # exclusive Redis lock is acquired first
|
||||
in_lock(lock_key, **lock_params) do # exclusive Redis lock is acquired first
|
||||
raise FailedToPersistDataError, 'Modifed build trace chunk detected' if has_changes_to_save?
|
||||
|
||||
self.reset.then do |chunk| # we ensure having latest lock_version
|
||||
|
|
@ -289,11 +289,16 @@ module Ci
|
|||
build.trace_chunks.maximum(:chunk_index).to_i
|
||||
end
|
||||
|
||||
def lock_key
|
||||
"trace_write:#{build_id}:chunks:#{chunk_index}"
|
||||
end
|
||||
|
||||
def lock_params
|
||||
["trace_write:#{build_id}:chunks:#{chunk_index}",
|
||||
{ ttl: WRITE_LOCK_TTL,
|
||||
retries: WRITE_LOCK_RETRY,
|
||||
sleep_sec: WRITE_LOCK_SLEEP }]
|
||||
{
|
||||
ttl: WRITE_LOCK_TTL,
|
||||
retries: WRITE_LOCK_RETRY,
|
||||
sleep_sec: WRITE_LOCK_SLEEP
|
||||
}
|
||||
end
|
||||
|
||||
def metrics
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class UserCallout < ApplicationRecord
|
|||
personal_access_token_expiry: 21, # EE-only
|
||||
suggest_pipeline: 22,
|
||||
customize_homepage: 23,
|
||||
feature_flags_new_version: 24
|
||||
feature_flags_new_version: 24,
|
||||
registration_enabled_callout: 25
|
||||
}
|
||||
|
||||
validates :user, presence: true
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ module MergeRequests
|
|||
return error('Failed to create keep around refs.') unless kept_around?
|
||||
return error('Failed to cache merge ref sha.') unless cache_merge_ref_sha
|
||||
|
||||
delete_refs
|
||||
delete_refs if repository.exists?
|
||||
|
||||
return error('Failed to update schedule.') unless update_schedule
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ module MergeRequests
|
|||
merge_request.update_project_counter_caches
|
||||
merge_request.cache_merge_request_closes_issues!(current_user)
|
||||
merge_request.cleanup_schedule&.destroy
|
||||
merge_request.update_column(:merge_ref_sha, nil)
|
||||
end
|
||||
|
||||
merge_request
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
- if @project.last_repository_check_failed?
|
||||
.row
|
||||
.col-md-12
|
||||
.gl-alert.gl-alert-danger.gl-mb-5
|
||||
.gl-alert.gl-alert-danger.gl-mb-5{ data: { testid: 'last-repository-check-failed-alert' } }
|
||||
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
.gl-alert-body
|
||||
- last_check_message = _("Last repository check (%{last_check_timestamp}) failed. See the 'repocheck.log' file for error messages.")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
= render_if_exists "layouts/header/token_expiry_notification"
|
||||
= render "layouts/broadcast"
|
||||
= render "layouts/header/read_only_banner"
|
||||
= render "layouts/header/registration_enabled_callout"
|
||||
= render "layouts/nav/classification_level_banner"
|
||||
= yield :flash_message
|
||||
= render "shared/ping_consent"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
- return unless show_registration_enabled_user_callout?
|
||||
|
||||
%div{ class: [container_class, @content_class, 'gl-pt-5!'] }
|
||||
.gl-alert.gl-alert-warning.js-registration-enabled-callout{ role: 'alert', data: { feature_id: UserCalloutsHelper::REGISTRATION_ENABLED_CALLOUT, dismiss_endpoint: user_callouts_path } }
|
||||
= sprite_icon('warning', size: 16, css_class: 'gl-alert-icon')
|
||||
%button.gl-alert-dismiss.js-close{ type: 'button', aria: { label: _('Close') }, data: { testid: 'close-registration-enabled-callout' } }
|
||||
= sprite_icon('close', size: 16)
|
||||
.gl-alert-title
|
||||
= _('Open registration is enabled on your instance.')
|
||||
.gl-alert-body
|
||||
= html_escape(_('%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance.')) % { anchorOpen: "<a href=\"#{help_page_path('user/admin_area/settings/sign_up_restrictions')}\">".html_safe, anchorClose: '</a>'.html_safe }
|
||||
.gl-alert-actions
|
||||
= link_to general_admin_application_settings_path(anchor: 'js-signup-settings'), class: 'btn gl-alert-action btn-info btn-md gl-button' do
|
||||
%span.gl-button-text
|
||||
= _('View setting')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add user callout to alert admins that registration is open by default
|
||||
merge_request: 47425
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Do not fail when cleaning up MR with no repository
|
||||
merge_request: 47744
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed copy contents functionality for snippets
|
||||
merge_request: 47646
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Clear cached merge_ref_sha on reopen
|
||||
merge_request: 47747
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -106,7 +106,8 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
|
|||
|
||||
- Write the raw SQL in the MR description. Preferably formatted
|
||||
nicely with [pgFormatter](https://sqlformat.darold.net) or
|
||||
[paste.depesz.com](https://paste.depesz.com).
|
||||
[paste.depesz.com](https://paste.depesz.com) and using regular quotes
|
||||
(e.g. `"projects"."id"`) and avoiding smart quotes (e.g. `“projects”.“id”`).
|
||||
- Include the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant
|
||||
queries in the description. If the output is too long, wrap it in
|
||||
`<details>` blocks, paste it in a GitLab Snippet, or provide the
|
||||
|
|
|
|||
|
|
@ -4,34 +4,34 @@ group: Ecosystem
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Setting up a development environment
|
||||
# Set up a development environment
|
||||
|
||||
The following are required to install and test the app:
|
||||
|
||||
1. A Jira Cloud instance
|
||||
- A Jira Cloud instance. Atlassian provides [free instances for development and testing](https://developer.atlassian.com/platform/marketplace/getting-started/#free-developer-instances-to-build-and-test-your-app).
|
||||
- A GitLab instance available over the internet. For the app to work, Jira Cloud should
|
||||
be able to connect to the GitLab instance through the internet. To easily expose your
|
||||
local development environment, you can use tools like:
|
||||
- [serveo](https://medium.com/automationmaster/how-to-forward-my-local-port-to-public-using-serveo-4979f352a3bf)
|
||||
- [ngrok](https://ngrok.com).
|
||||
|
||||
Atlassian provides free instances for development and testing. [Click here to sign up](https://developer.atlassian.com/platform/marketplace/getting-started/#free-developer-instances-to-build-and-test-your-app).
|
||||
These also take care of SSL for you because Jira requires all connections to the app
|
||||
host to be over SSL.
|
||||
|
||||
1. A GitLab instance available over the internet
|
||||
## Install the app in Jira
|
||||
|
||||
For the app to work, Jira Cloud should be able to connect to the GitLab instance through the internet.
|
||||
To install the app in Jira:
|
||||
|
||||
To easily expose your local development environment, you can use tools like
|
||||
[serveo](https://medium.com/automationmaster/how-to-forward-my-local-port-to-public-using-serveo-4979f352a3bf)
|
||||
or [ngrok](https://ngrok.com). These also take care of SSL for you because Jira
|
||||
requires all connections to the app host to be over SSL.
|
||||
1. Enable Jira development mode to install apps that are not from the Atlassian
|
||||
Marketplace:
|
||||
|
||||
## Installing the app in Jira
|
||||
|
||||
1. Enable Jira development mode to install apps that are not from the Atlassian Marketplace
|
||||
|
||||
1. Navigate to **Jira settings** (cog icon) > **Apps** > **Manage apps**.
|
||||
1. In Jira, navigate to **Jira settings > Apps > Manage apps**.
|
||||
1. Scroll to the bottom of the **Manage apps** page and click **Settings**.
|
||||
1. Select **Enable development mode** and click **Apply**.
|
||||
|
||||
1. Install the app
|
||||
1. Install the app:
|
||||
|
||||
1. Navigate to Jira, then choose **Jira settings** (cog icon) > **Apps** > **Manage apps**.
|
||||
1. In Jira, navigate to **Jira settings > Apps > Manage apps**.
|
||||
1. Click **Upload app**.
|
||||
1. In the **From this URL** field, provide a link to the app descriptor. The host and port must point to your GitLab instance.
|
||||
|
||||
|
|
|
|||
|
|
@ -356,6 +356,9 @@ msgstr ""
|
|||
msgid "%{address} is an invalid IP address range"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{author_link} wrote:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8410,9 +8413,6 @@ msgstr ""
|
|||
msgid "DastProfiles|Copy HTTP header to clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not create the scanner profile. Please try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8437,9 +8437,6 @@ msgstr ""
|
|||
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not update the scanner profile. Please try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8554,9 +8551,6 @@ msgstr ""
|
|||
msgid "DastProfiles|Site is not validated yet, please follow the steps."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Site must be validated to run an active scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Spider timeout"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8602,27 +8596,12 @@ msgstr ""
|
|||
msgid "DastProfiles|Validate"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validate target site"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validating..."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the chosen method."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation failed. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation is in progress..."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation must be turned off to change the target URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
|
||||
msgstr ""
|
||||
|
||||
msgid "Data is still calculating..."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -15098,6 +15077,9 @@ msgstr ""
|
|||
msgid "IssueAnalytics|Weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "IssueBoards|An error occurred while setting notifications status."
|
||||
msgstr ""
|
||||
|
||||
msgid "IssueBoards|Board"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19068,6 +19050,9 @@ msgstr ""
|
|||
msgid "Open raw"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open registration is enabled on your instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "Open sidebar"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -29895,6 +29880,9 @@ msgstr ""
|
|||
msgid "View replaced file @ "
|
||||
msgstr ""
|
||||
|
||||
msgid "View setting"
|
||||
msgstr ""
|
||||
|
||||
msgid "View supported languages and frameworks"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ module QA
|
|||
class File < Base
|
||||
attr_accessor :author_email,
|
||||
:author_name,
|
||||
:branch,
|
||||
:content,
|
||||
:commit_message,
|
||||
:name
|
||||
attr_writer :branch
|
||||
|
||||
attribute :project do
|
||||
Project.fabricate! do |resource|
|
||||
|
|
@ -29,6 +29,10 @@ module QA
|
|||
@commit_message = 'QA Test - Commit message'
|
||||
end
|
||||
|
||||
def branch
|
||||
@branch ||= "master"
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
project.visit!
|
||||
|
||||
|
|
@ -42,12 +46,6 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def resource_web_url(resource)
|
||||
super
|
||||
rescue ResourceURLMissingError
|
||||
# this particular resource does not expose a web_url property
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"/projects/#{CGI.escape(project.path_with_namespace)}/repository/files/#{CGI.escape(@name)}"
|
||||
end
|
||||
|
|
@ -58,13 +56,20 @@ module QA
|
|||
|
||||
def api_post_body
|
||||
{
|
||||
branch: @branch || "master",
|
||||
branch: branch,
|
||||
author_email: @author_email || Runtime::User.default_email,
|
||||
author_name: @author_name || Runtime::User.username,
|
||||
content: content,
|
||||
commit_message: commit_message
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def transform_api_resource(api_resource)
|
||||
api_resource[:web_url] = "#{Runtime::Scenario.gitlab_address}/#{project.full_path}/-/tree/#{branch}/#{api_resource[:file_path]}"
|
||||
api_resource
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Files management' do
|
||||
it 'user creates, edits and deletes a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/451' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
# Create
|
||||
file_name = 'QA Test - File name'
|
||||
file_content = 'QA Test - File content'
|
||||
commit_message_for_create = 'QA Test - Create new file'
|
||||
|
||||
Resource::File.fabricate_via_browser_ui! do |file|
|
||||
file.name = file_name
|
||||
file.content = file_content
|
||||
file.commit_message = commit_message_for_create
|
||||
end
|
||||
|
||||
Page::File::Show.perform do |file|
|
||||
aggregate_failures 'file details' do
|
||||
expect(file).to have_file(file_name)
|
||||
expect(file).to have_file_content(file_content)
|
||||
expect(file).to have_commit_message(commit_message_for_create)
|
||||
end
|
||||
end
|
||||
|
||||
# Edit
|
||||
updated_file_content = 'QA Test - Updated file content'
|
||||
commit_message_for_update = 'QA Test - Update file'
|
||||
|
||||
Page::File::Show.perform(&:click_edit)
|
||||
|
||||
Page::File::Form.perform do |file|
|
||||
file.remove_content
|
||||
file.add_content(updated_file_content)
|
||||
file.add_commit_message(commit_message_for_update)
|
||||
file.commit_changes
|
||||
end
|
||||
|
||||
Page::File::Show.perform do |file|
|
||||
aggregate_failures 'file details' do
|
||||
expect(file).to have_notice('Your changes have been successfully committed.')
|
||||
expect(file).to have_file_content(updated_file_content)
|
||||
expect(file).to have_commit_message(commit_message_for_update)
|
||||
end
|
||||
end
|
||||
|
||||
# Delete
|
||||
commit_message_for_delete = 'QA Test - Delete file'
|
||||
|
||||
Page::File::Show.perform do |file|
|
||||
file.click_delete
|
||||
file.add_commit_message(commit_message_for_delete)
|
||||
file.click_delete_file
|
||||
end
|
||||
|
||||
Page::Project::Show.perform do |project|
|
||||
aggregate_failures 'file details' do
|
||||
expect(project).to have_notice('The file has been successfully deleted.')
|
||||
expect(project).to have_commit_message(commit_message_for_delete)
|
||||
expect(project).not_to have_file(file_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
context 'File management' do
|
||||
file_name = 'QA Test - File name'
|
||||
file_content = 'QA Test - File content'
|
||||
commit_message_for_create = 'QA Test - Create new file'
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it 'user creates a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1093' do
|
||||
Resource::File.fabricate_via_browser_ui! do |file|
|
||||
file.name = file_name
|
||||
file.content = file_content
|
||||
file.commit_message = commit_message_for_create
|
||||
end
|
||||
|
||||
Page::File::Show.perform do |file|
|
||||
aggregate_failures 'file details' do
|
||||
expect(file).to have_file(file_name)
|
||||
expect(file).to have_file_content(file_content)
|
||||
expect(file).to have_commit_message(commit_message_for_create)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
context 'File management' do
|
||||
let(:file) { Resource::File.fabricate_via_api! }
|
||||
|
||||
commit_message_for_delete = 'QA Test - Delete file'
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
file.visit!
|
||||
end
|
||||
|
||||
it 'user deletes a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1095' do
|
||||
Page::File::Show.perform do |file|
|
||||
file.click_delete
|
||||
file.add_commit_message(commit_message_for_delete)
|
||||
file.click_delete_file
|
||||
end
|
||||
|
||||
Page::Project::Show.perform do |project|
|
||||
aggregate_failures 'file details' do
|
||||
expect(project).to have_notice('The file has been successfully deleted.')
|
||||
expect(project).to have_commit_message(commit_message_for_delete)
|
||||
expect(project).not_to have_file(file.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
context 'File management' do
|
||||
let(:file) { Resource::File.fabricate_via_api! }
|
||||
|
||||
updated_file_content = 'QA Test - Updated file content'
|
||||
commit_message_for_update = 'QA Test - Update file'
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
file.visit!
|
||||
end
|
||||
|
||||
it 'user edits a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1094' do
|
||||
Page::File::Show.perform(&:click_edit)
|
||||
|
||||
Page::File::Form.perform do |file|
|
||||
file.remove_content
|
||||
file.add_content(updated_file_content)
|
||||
file.add_commit_message(commit_message_for_update)
|
||||
file.commit_changes
|
||||
end
|
||||
|
||||
Page::File::Show.perform do |file|
|
||||
aggregate_failures 'file details' do
|
||||
expect(file).to have_notice('Your changes have been successfully committed.')
|
||||
expect(file).to have_file_content(updated_file_content)
|
||||
expect(file).to have_commit_message(commit_message_for_update)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -46,7 +46,7 @@ RSpec.describe 'Admin uses repository checks', :request_store, :clean_gitlab_red
|
|||
)
|
||||
visit_admin_project_page(project)
|
||||
|
||||
page.within('.gl-alert') do
|
||||
page.within('[data-testid="last-repository-check-failed-alert"]') do
|
||||
expect(page.text).to match(/Last repository check \(just now\) failed/)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Registration enabled callout' do
|
||||
let_it_be(:admin) { create(:admin) }
|
||||
let_it_be(:non_admin) { create(:user) }
|
||||
|
||||
context 'when "Sign-up enabled" setting is `true`' do
|
||||
before do
|
||||
stub_application_setting(signup_enabled: true)
|
||||
end
|
||||
|
||||
context 'when an admin is logged in' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
visit root_dashboard_path
|
||||
end
|
||||
|
||||
it 'displays callout' do
|
||||
expect(page).to have_content 'Open registration is enabled on your instance.'
|
||||
expect(page).to have_link 'View setting', href: general_admin_application_settings_path(anchor: 'js-signup-settings')
|
||||
end
|
||||
|
||||
context 'when callout is dismissed', :js do
|
||||
before do
|
||||
find('[data-testid="close-registration-enabled-callout"]').click
|
||||
|
||||
visit root_dashboard_path
|
||||
end
|
||||
|
||||
it 'does not display callout' do
|
||||
expect(page).not_to have_content 'Open registration is enabled on your instance.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non-admin is logged in' do
|
||||
before do
|
||||
sign_in(non_admin)
|
||||
visit root_dashboard_path
|
||||
end
|
||||
|
||||
it 'does not display callout' do
|
||||
expect(page).not_to have_content 'Open registration is enabled on your instance.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -14,8 +14,13 @@ describe('Blob Header Default Actions', () => {
|
|||
let btnGroup;
|
||||
let buttons;
|
||||
|
||||
const blobHash = 'foo-bar';
|
||||
|
||||
function createComponent(propsData = {}) {
|
||||
wrapper = mount(BlobHeaderActions, {
|
||||
provide: {
|
||||
blobHash,
|
||||
},
|
||||
propsData: {
|
||||
rawPath: Blob.rawPath,
|
||||
...propsData,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ describe('Blob Header Default Actions', () => {
|
|||
|
||||
function createComponent(blobProps = {}, options = {}, propsData = {}, shouldMount = false) {
|
||||
const method = shouldMount ? mount : shallowMount;
|
||||
const blobHash = 'foo-bar';
|
||||
wrapper = method.call(this, BlobHeader, {
|
||||
provide: {
|
||||
blobHash,
|
||||
},
|
||||
propsData: {
|
||||
blob: { ...Blob, ...blobProps },
|
||||
...propsData,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
import Vuex from 'vuex';
|
||||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import { GlToggle, GlLoadingIcon } from '@gitlab/ui';
|
||||
import BoardSidebarSubscription from '~/boards/components/sidebar/board_sidebar_subscription.vue';
|
||||
import * as types from '~/boards/stores/mutation_types';
|
||||
import { createStore } from '~/boards/stores';
|
||||
import { mockActiveIssue } from '../../mock_data';
|
||||
import createFlash from '~/flash';
|
||||
|
||||
jest.mock('~/flash.js');
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
describe('~/boards/components/sidebar/board_sidebar_subscription_spec.vue', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
|
||||
const findNotificationHeader = () => wrapper.find("[data-testid='notification-header-text']");
|
||||
const findToggle = () => wrapper.find(GlToggle);
|
||||
const findGlLoadingIcon = () => wrapper.find(GlLoadingIcon);
|
||||
|
||||
const createComponent = (activeIssue = { ...mockActiveIssue }) => {
|
||||
store = createStore();
|
||||
store.state.issues = { [activeIssue.id]: activeIssue };
|
||||
store.state.activeId = activeIssue.id;
|
||||
|
||||
wrapper = mount(BoardSidebarSubscription, {
|
||||
localVue,
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
store = null;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Board sidebar subscription component template', () => {
|
||||
it('displays "notifications" heading', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findNotificationHeader().text()).toBe('Notifications');
|
||||
});
|
||||
|
||||
it('renders toggle as "off" when currently not subscribed', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findToggle().exists()).toBe(true);
|
||||
expect(findToggle().props('value')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders toggle as "on" when currently subscribed', () => {
|
||||
createComponent({
|
||||
...mockActiveIssue,
|
||||
subscribed: true,
|
||||
});
|
||||
|
||||
expect(findToggle().exists()).toBe(true);
|
||||
expect(findToggle().props('value')).toBe(true);
|
||||
});
|
||||
|
||||
describe('when notification emails have been disabled', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
...mockActiveIssue,
|
||||
emailsDisabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('displays a message that notification have been disabled', () => {
|
||||
expect(findNotificationHeader().text()).toBe(
|
||||
'Notifications have been disabled by the project or group owner',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render the toggle button', () => {
|
||||
expect(findToggle().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Board sidebar subscription component `behavior`', () => {
|
||||
const mockSetActiveIssueSubscribed = subscribedState => {
|
||||
jest.spyOn(wrapper.vm, 'setActiveIssueSubscribed').mockImplementation(async () => {
|
||||
store.commit(types.UPDATE_ISSUE_BY_ID, {
|
||||
issueId: mockActiveIssue.id,
|
||||
prop: 'subscribed',
|
||||
value: subscribedState,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
it('subscribing to notification', async () => {
|
||||
createComponent();
|
||||
mockSetActiveIssueSubscribed(true);
|
||||
|
||||
expect(findGlLoadingIcon().exists()).toBe(false);
|
||||
|
||||
findToggle().trigger('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findGlLoadingIcon().exists()).toBe(true);
|
||||
expect(wrapper.vm.setActiveIssueSubscribed).toHaveBeenCalledWith({
|
||||
subscribed: true,
|
||||
projectPath: 'gitlab-org/test-subgroup/gitlab-test',
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findGlLoadingIcon().exists()).toBe(false);
|
||||
expect(findToggle().props('value')).toBe(true);
|
||||
});
|
||||
|
||||
it('unsubscribing from notification', async () => {
|
||||
createComponent({
|
||||
...mockActiveIssue,
|
||||
subscribed: true,
|
||||
});
|
||||
mockSetActiveIssueSubscribed(false);
|
||||
|
||||
expect(findGlLoadingIcon().exists()).toBe(false);
|
||||
|
||||
findToggle().trigger('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.vm.setActiveIssueSubscribed).toHaveBeenCalledWith({
|
||||
subscribed: false,
|
||||
projectPath: 'gitlab-org/test-subgroup/gitlab-test',
|
||||
});
|
||||
expect(findGlLoadingIcon().exists()).toBe(true);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findGlLoadingIcon().exists()).toBe(false);
|
||||
expect(findToggle().props('value')).toBe(false);
|
||||
});
|
||||
|
||||
it('flashes an error message when setting the subscribed state fails', async () => {
|
||||
createComponent();
|
||||
jest.spyOn(wrapper.vm, 'setActiveIssueSubscribed').mockImplementation(async () => {
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
findToggle().trigger('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(createFlash).toHaveBeenNthCalledWith(1, {
|
||||
message: wrapper.vm.$options.i18n.updateSubscribedErrorMessage,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -176,6 +176,14 @@ export const mockIssue = {
|
|||
},
|
||||
};
|
||||
|
||||
export const mockActiveIssue = {
|
||||
...mockIssue,
|
||||
id: 436,
|
||||
iid: '27',
|
||||
subscribed: false,
|
||||
emailsDisabled: false,
|
||||
};
|
||||
|
||||
export const mockIssueWithModel = new ListIssue(mockIssue);
|
||||
|
||||
export const mockIssue2 = {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
rawIssue,
|
||||
mockIssues,
|
||||
labels,
|
||||
mockActiveIssue,
|
||||
} from '../mock_data';
|
||||
import actions, { gqlClient } from '~/boards/stores/actions';
|
||||
import * as types from '~/boards/stores/mutation_types';
|
||||
|
|
@ -833,6 +834,57 @@ describe('setActiveIssueDueDate', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setActiveIssueSubscribed', () => {
|
||||
const state = { issues: { [mockActiveIssue.id]: mockActiveIssue } };
|
||||
const getters = { activeIssue: mockActiveIssue };
|
||||
const subscribedState = true;
|
||||
const input = {
|
||||
subscribedState,
|
||||
projectPath: 'gitlab-org/gitlab-test',
|
||||
};
|
||||
|
||||
it('should commit subscribed status', done => {
|
||||
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
|
||||
data: {
|
||||
issueSetSubscription: {
|
||||
issue: {
|
||||
subscribed: subscribedState,
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const payload = {
|
||||
issueId: getters.activeIssue.id,
|
||||
prop: 'subscribed',
|
||||
value: subscribedState,
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.setActiveIssueSubscribed,
|
||||
input,
|
||||
{ ...state, ...getters },
|
||||
[
|
||||
{
|
||||
type: types.UPDATE_ISSUE_BY_ID,
|
||||
payload,
|
||||
},
|
||||
],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if fails', async () => {
|
||||
jest
|
||||
.spyOn(gqlClient, 'mutate')
|
||||
.mockResolvedValue({ data: { issueSetSubscription: { errors: ['failed mutation'] } } });
|
||||
|
||||
await expect(actions.setActiveIssueSubscribed({ getters }, input)).rejects.toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchBacklog', () => {
|
||||
expectNotImplemented(actions.fetchBacklog);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -124,6 +124,22 @@ describe('Boards - Getters', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('projectPathByIssueId', () => {
|
||||
it('returns project path for the active issue', () => {
|
||||
const mockActiveIssue = {
|
||||
referencePath: 'gitlab-org/gitlab-test#1',
|
||||
};
|
||||
expect(getters.projectPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual(
|
||||
'gitlab-org/gitlab-test',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns empty string as project when active issue is an empty object', () => {
|
||||
const mockActiveIssue = {};
|
||||
expect(getters.projectPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIssuesByList', () => {
|
||||
const boardsState = {
|
||||
issuesByListId: mockIssuesByListId,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = `
|
|||
class="code highlight"
|
||||
>
|
||||
<code
|
||||
id="blob-code-content"
|
||||
data-blob-hash="foo-bar"
|
||||
>
|
||||
<span
|
||||
id="LC1"
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@ import { HIGHLIGHT_CLASS_NAME } from '~/vue_shared/components/blob_viewers/const
|
|||
describe('Blob Simple Viewer component', () => {
|
||||
let wrapper;
|
||||
const contentMock = `<span id="LC1">First</span>\n<span id="LC2">Second</span>\n<span id="LC3">Third</span>`;
|
||||
const blobHash = 'foo-bar';
|
||||
|
||||
function createComponent(content = contentMock) {
|
||||
wrapper = shallowMount(SimpleViewer, {
|
||||
provide: {
|
||||
blobHash,
|
||||
},
|
||||
propsData: {
|
||||
content,
|
||||
type: 'text',
|
||||
|
|
|
|||
|
|
@ -166,4 +166,32 @@ RSpec.describe ApplicationSettingsHelper do
|
|||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.signup_enabled?' do
|
||||
subject { helper.signup_enabled? }
|
||||
|
||||
context 'when signup is enabled' do
|
||||
before do
|
||||
stub_application_setting(signup_enabled: true)
|
||||
end
|
||||
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
|
||||
context 'when signup is disabled' do
|
||||
before do
|
||||
stub_application_setting(signup_enabled: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when `signup_enabled` is nil' do
|
||||
before do
|
||||
stub_application_setting(signup_enabled: nil)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -161,4 +161,50 @@ RSpec.describe UserCalloutsHelper do
|
|||
it { is_expected.to be_falsy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.show_registration_enabled_user_callout?' do
|
||||
let_it_be(:admin) { create(:user, :admin) }
|
||||
|
||||
subject { helper.show_registration_enabled_user_callout? }
|
||||
|
||||
context 'when `current_user` is not an admin' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
stub_application_setting(signup_enabled: true)
|
||||
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when signup is disabled' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(admin)
|
||||
stub_application_setting(signup_enabled: false)
|
||||
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when user has dismissed callout' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(admin)
|
||||
stub_application_setting(signup_enabled: true)
|
||||
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { true }
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when `current_user` is an admin, signup is enabled, and user has not dismissed callout' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(admin)
|
||||
stub_application_setting(signup_enabled: true)
|
||||
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
|
||||
end
|
||||
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -115,6 +115,19 @@ RSpec.describe MergeRequests::CleanupRefsService do
|
|||
|
||||
it_behaves_like 'service that does not clean up merge request refs'
|
||||
end
|
||||
|
||||
context 'when repository no longer exists' do
|
||||
before do
|
||||
Repositories::DestroyService.new(merge_request.project.repository).execute
|
||||
end
|
||||
|
||||
it 'does not fail and still mark schedule as complete' do
|
||||
aggregate_failures do
|
||||
expect(result[:status]).to eq(:success)
|
||||
expect(merge_request.cleanup_schedule.completed_at).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'service that does not clean up merge request refs' do
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ RSpec.describe MergeRequests::ReopenService do
|
|||
before do
|
||||
allow(service).to receive(:execute_hooks)
|
||||
merge_request.create_cleanup_schedule(scheduled_at: Time.current)
|
||||
merge_request.update_column(:merge_ref_sha, 'abc123')
|
||||
|
||||
perform_enqueued_jobs do
|
||||
service.execute(merge_request)
|
||||
|
|
@ -48,6 +49,10 @@ RSpec.describe MergeRequests::ReopenService do
|
|||
expect(merge_request.reload.cleanup_schedule).to be_nil
|
||||
end
|
||||
|
||||
it 'clears the cached merge_ref_sha' do
|
||||
expect(merge_request.reload.merge_ref_sha).to be_nil
|
||||
end
|
||||
|
||||
context 'note creation' do
|
||||
it 'creates resource state event about merge_request reopen' do
|
||||
event = merge_request.resource_state_events.last
|
||||
|
|
|
|||
Loading…
Reference in New Issue