Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-05 00:08:11 +00:00
parent 484a245a95
commit 59429d48eb
16 changed files with 132 additions and 170 deletions

View file

@ -1 +1 @@
5c49f857a37ee47158e2caff7c963e7de9563e8b 1d200302f736b50b48bf7e9ec7eb28f0e01dc559

View file

@ -1,5 +1,5 @@
<script> <script>
import { GlButton, GlModal } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import $ from 'jquery'; import $ from 'jquery';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
@ -7,13 +7,23 @@ import Autosave from '~/autosave';
import { isLoggedIn } from '~/lib/utils/common_utils'; import { isLoggedIn } from '~/lib/utils/common_utils';
import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils'; import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
export default { export default {
name: 'DesignReplyForm', name: 'DesignReplyForm',
i18n: {
primaryBtn: s__('DesignManagement|Discard changes'),
cancelBtnCreate: s__('DesignManagement|Continue creating'),
cancelBtnUpdate: s__('DesignManagement|Continue editing'),
cancelCreate: s__('DesignManagement|Are you sure you want to cancel creating this comment?'),
cancelUpdate: s__('DesignManagement|Are you sure you want to cancel editing this comment?'),
newCommentButton: s__('DesignManagement|Comment'),
updateCommentButton: s__('DesignManagement|Save comment'),
},
markdownDocsPath: helpPagePath('user/markdown'),
components: { components: {
MarkdownField, MarkdownField,
GlButton, GlButton,
GlModal,
}, },
props: { props: {
markdownPreviewPath: { markdownPreviewPath: {
@ -54,29 +64,10 @@ export default {
hasValue() { hasValue() {
return this.value.trim().length > 0; return this.value.trim().length > 0;
}, },
modalSettings() {
if (this.isNewComment) {
return {
title: s__('DesignManagement|Cancel comment confirmation'),
okTitle: s__('DesignManagement|Discard comment'),
cancelTitle: s__('DesignManagement|Keep comment'),
content: s__('DesignManagement|Are you sure you want to cancel creating this comment?'),
};
}
return {
title: s__('DesignManagement|Cancel comment update confirmation'),
okTitle: s__('DesignManagement|Cancel changes'),
cancelTitle: s__('DesignManagement|Keep changes'),
content: s__('DesignManagement|Are you sure you want to cancel changes to this comment?'),
};
},
buttonText() { buttonText() {
return this.isNewComment return this.isNewComment
? s__('DesignManagement|Comment') ? this.$options.i18n.newCommentButton
: s__('DesignManagement|Save comment'); : this.$options.i18n.updateCommentButton;
},
markdownDocsPath() {
return helpPagePath('user/markdown');
}, },
shortDiscussionId() { shortDiscussionId() {
return isGid(this.discussionId) ? getIdFromGraphQLId(this.discussionId) : this.discussionId; return isGid(this.discussionId) ? getIdFromGraphQLId(this.discussionId) : this.discussionId;
@ -94,12 +85,30 @@ export default {
}, },
cancelComment() { cancelComment() {
if (this.hasValue && this.formText !== this.value) { if (this.hasValue && this.formText !== this.value) {
this.$refs.cancelCommentModal.show(); this.confirmCancelCommentModal();
} else { } else {
this.$emit('cancel-form'); this.$emit('cancel-form');
} }
}, },
confirmCancelCommentModal() { async confirmCancelCommentModal() {
const msg = this.isNewComment
? this.$options.i18n.cancelCreate
: this.$options.i18n.cancelUpdate;
const cancelBtn = this.isNewComment
? this.$options.i18n.cancelBtnCreate
: this.$options.i18n.cancelBtnUpdate;
const confirmed = await confirmAction(msg, {
primaryBtnText: this.$options.i18n.primaryBtn,
cancelBtnText: cancelBtn,
primaryBtnVariant: 'danger',
});
if (!confirmed) {
return;
}
this.$emit('cancel-form'); this.$emit('cancel-form');
this.autosaveDiscussion.reset(); this.autosaveDiscussion.reset();
}, },
@ -126,7 +135,7 @@ export default {
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:enable-autocomplete="true" :enable-autocomplete="true"
:textarea-value="value" :textarea-value="value"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="$options.markdownDocsPath"
class="bordered-box" class="bordered-box"
> >
<template #textarea> <template #textarea>
@ -171,15 +180,5 @@ export default {
>{{ __('Cancel') }}</gl-button >{{ __('Cancel') }}</gl-button
> >
</div> </div>
<gl-modal
ref="cancelCommentModal"
ok-variant="danger"
:title="modalSettings.title"
:ok-title="modalSettings.okTitle"
:cancel-title="modalSettings.cancelTitle"
modal-id="cancel-comment-modal"
@ok="confirmCancelCommentModal"
>{{ modalSettings.content }}
</gl-modal>
</form> </form>
</template> </template>

View file

@ -311,8 +311,6 @@ class ProjectsController < Projects::ApplicationController
find_tags = true find_tags = true
find_commits = true find_commits = true
use_gitaly_pagination = Feature.enabled?(:use_gitaly_pagination_for_refs, @project)
unless find_refs.nil? unless find_refs.nil?
find_branches = find_refs.include?('branches') find_branches = find_refs.include?('branches')
find_tags = find_refs.include?('tags') find_tags = find_refs.include?('tags')
@ -323,7 +321,7 @@ class ProjectsController < Projects::ApplicationController
if find_branches if find_branches
branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
.execute(gitaly_pagination: use_gitaly_pagination) .execute(gitaly_pagination: true)
.take(REFS_LIMIT) .take(REFS_LIMIT)
.map(&:name) .map(&:name)
@ -332,7 +330,7 @@ class ProjectsController < Projects::ApplicationController
if find_tags && @repository.tag_count.nonzero? if find_tags && @repository.tag_count.nonzero?
tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
.execute(gitaly_pagination: use_gitaly_pagination) .execute(gitaly_pagination: true)
.take(REFS_LIMIT) .take(REFS_LIMIT)
.map(&:name) .map(&:name)

View file

@ -35,9 +35,9 @@ module BulkImports
def export_status def export_status
strong_memoize(:export_status) do strong_memoize(:export_status) do
fetch_export_status&.find { |item| item['relation'] == relation } fetch_export_status&.find { |item| item['relation'] == relation }
rescue StandardError => e
{ 'status' => Export::FAILED, 'error' => e.message }
end end
rescue StandardError => e
{ 'status' => Export::FAILED, 'error' => e.message }
end end
def fetch_export_status def fetch_export_status

View file

@ -1,8 +0,0 @@
---
name: use_gitaly_pagination_for_refs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96448
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372049
milestone: '15.4'
type: development
group: group::source code
default_enabled: true

View file

@ -53,6 +53,10 @@ Each time you implement a new feature/endpoint, whether it is at UI, API or Grap
Be careful to **also test [visibility levels](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/doc/development/permissions.md#feature-specific-permissions)** and not only project access rights. Be careful to **also test [visibility levels](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/doc/development/permissions.md#feature-specific-permissions)** and not only project access rights.
The HTTP status code returned when an authorization check fails should generally be `404 Not Found` in order to avoid revealing information
about whether or not the requested resource exists. `403 Forbidden` may be appropriate if you need to display a specific message to the user
about why they cannot access the resource. If you are displaying a generic message such as "access denied", consider returning `404 Not Found` instead.
Some example of well implemented access controls and tests: Some example of well implemented access controls and tests:
1. [example1](https://dev.gitlab.org/gitlab/gitlab-ee/-/merge_requests/710/diffs?diff_id=13750#af40ef0eaae3c1e018809e1d88086e32bccaca40_43_43) 1. [example1](https://dev.gitlab.org/gitlab/gitlab-ee/-/merge_requests/710/diffs?diff_id=13750#af40ef0eaae3c1e018809e1d88086e32bccaca40_43_43)

View file

@ -136,6 +136,9 @@ We use the following categories to classify a metric:
- `subscription`: Data related to licensing. - `subscription`: Data related to licensing.
- `standard`: Standard set of identifiers that are included when collecting data. - `standard`: Standard set of identifiers that are included when collecting data.
An aggregate metric is a metric that is the sum of two or more child metrics. Service Ping uses the data category of
the aggregate metric to determine whether or not the data is included in the reported Service Ping payload.
### Metric name suggestion examples ### Metric name suggestion examples
#### Metric with `data_source: database` #### Metric with `data_source: database`

View file

@ -176,42 +176,36 @@ To change the password for this customers portal account:
### GitLab for Education ### GitLab for Education
For qualifying non-profit educational institutions, the [GitLab for Education](https://about.gitlab.com/solutions/education/) program provides For qualifying non-profit educational institutions, the [GitLab for Education Program](https://about.gitlab.com/solutions/education/) provides GitLab Ultimate, plus 50,000 CI/CD minutes per month. The subscription granted under GitLab for Education can only be used for instructional use or non-commercial academic research. For more information—including instructions for applying to the program and renewing program membership—see the [GitLab for Education Program page](https://about.gitlab.com/solutions/education/) and the [GitLab handbook](https://about.gitlab.com/handbook/marketing/community-relations/community-programs/education-program/).
the top GitLab tier, plus 50,000 CI/CD minutes per month.
The GitLab for Education license can only be used for instructional-use or
non-commercial academic research.
Find more information on how to apply and renew at
[GitLab for Education](https://about.gitlab.com/solutions/education/).
### GitLab for Open Source ### GitLab for Open Source
For qualifying open source projects, the [GitLab for Open Source Program](https://about.gitlab.com/solutions/open-source/) provides For qualifying open source projects, the [GitLab for Open Source Program](https://about.gitlab.com/solutions/open-source/) provides GitLab Ultimate, plus 50,000 CI/CD minutes per month. For more information—including instructions for applying to the program and renewing program membership—see the [GitLab for Open Source Program page](https://about.gitlab.com/solutions/open-source/) and the [GitLab handbook](https://about.gitlab.com/handbook/marketing/community-relations/opensource-program/).
GitLab Ultimate, plus 50,000 CI/CD minutes per month. For more information, see [program requirements](https://about.gitlab.com/solutions/open-source/join/#requirements), [renewals](https://about.gitlab.com/solutions/open-source/join/#renewals), and [program benefits](https://about.gitlab.com/solutions/open-source/join/).
If you have any questions, send an email to `opensource@gitlab.com` for assistance. #### Meeting GitLab for Open Source Program requirements
#### License requirements for GitLab for Open Source Program members NOTE:
GitLab for Open Source Program benefits apply to an entire GitLab namespace. To qualify for the GitLab for Open Source Program, all projects in an applicant's namespace must meet program requirements. Applicants submit materials related to one project in the applying namespace, and the open source program team uses that project to verify eligibility of the entire namespace.
GitLab for Open Source Program benefits apply to an entire GitLab namespace. To qualify for the GitLab for Open Source Program, **all projects in an applicant's namespace** must carry an [OSI-approved license](https://opensource.org/licenses/). To meet GitLab for Open Source Program requirements, first add an OSI-approved open source license to all projects in your namespace.
To add a license: To add a license to a project:
1. On the top bar, select **Main menu > Projects** and find your project. 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the overview page, select **Add LICENSE**. If the license you want is not available as a license template, manually copy the entire, unaltered [text of your chosen license](https://opensource.org/licenses/alphabetical) into the `LICENSE` file. Note that GitLab defaults to **All rights reserved** if users do not perform this action. 1. On the overview page, select **Add LICENSE**. If the license you want is not available as a license template, manually copy the entire, unaltered [text of your chosen license](https://opensource.org/licenses/alphabetical) into the `LICENSE` file. Note that GitLab defaults to **All rights reserved** if users do not perform this action.
Applicants must add the correct license to each project in their respective groups or namespaces When you're sure you're using OSI-approved licenses for your projects, you can take your screenshots. Applicants must add the correct license to each project in their respective groups or namespaces. When you're sure you're using OSI-approved licenses for your projects, you can take your screenshots.
#### Verification for Open Source Program #### Verification for Open Source Program
As part of the [application verification process](https://about.gitlab.com/solutions/open-source/join/), you must upload **three screenshots**: Next, take screenshots of your project to confirm that project's eligibility. You must upload three screenshots:
- [OSI-approved license overview](#screenshot-1-license-overview) - [OSI-approved license overview](#screenshot-1-license-overview)
- [OSI-approved license contents](#screenshot-2-license-contents) - [OSI-approved license contents](#screenshot-2-license-contents)
- [Publicly visible settings](#screenshot-3-publicly-visible-settings) - [Publicly visible settings](#screenshot-3-publicly-visible-settings)
Benefits of the GitLab Open Source Program apply to all projects in a GitLab namespace. All projects in an eligible namespace must meet program requirements. However, if you submit materials for **one project** in your namespace, the open source program team uses that project to verify the contents of the entire namespace you use when applying to the program. NOTE:
Benefits of the GitLab Open Source Program apply to all projects in a GitLab namespace. All projects in an eligible namespace must meet program requirements.
##### Screenshot 1: License overview ##### Screenshot 1: License overview
@ -243,24 +237,11 @@ To be eligible for the GitLab Open Source Program, projects must be publicly vis
![Publicly visible setting](img/publicly-visible.png) ![Publicly visible setting](img/publicly-visible.png)
NOTE: NOTE:
Exceptions to this public visibility requirement apply in select circumstances (for example, in cases where a project may hold sensitive data). Email `opensource@gitlab.com` with details of your use case to request written permission for exceptions. Exceptions to this public visibility requirement apply in select circumstances (for example, in cases where a project in an applicant's namespace may hold sensitive data). Email `opensource@gitlab.com` with details of your use case to request written permission for exceptions.
### GitLab for Startups ### GitLab for Startups
For qualifying startups, the [GitLab for Startups](https://about.gitlab.com/solutions/startups/) program provides For qualifying startups, the [GitLab for Startups](https://about.gitlab.com/solutions/startups/) program provides GitLab Ultimate, plus 50,000 CI/CD minutes per month for 12 months. For more information—including instructions for applying to the program and renewing program membership—see the [GitLab for Startups Program page](https://about.gitlab.com/solutions/startups/) and the [GitLab handbook](https://about.gitlab.com/handbook/marketing/community-relations/startups-program/).
the top GitLab tier, plus 50,000 CI/CD minutes per month for 12 months.
For more information, including program requirements, see the [Startup program's landing page](https://about.gitlab.com/solutions/startups/).
Send all questions and requests related to the GitLab for Startups program to `startups@gitlab.com`.
### Support for Community Programs
Because these Community Programs are free of cost, regular Priority Support is not included.
As a community member, you can follow this diagram to find support:
![Support diagram](img/support_diagram_c.png)
## Contact Support ## Contact Support
@ -269,12 +250,9 @@ Learn more about:
- The tiers of [GitLab Support](https://about.gitlab.com/support/). - The tiers of [GitLab Support](https://about.gitlab.com/support/).
- [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new). - [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new).
We also encourage all users to search our project trackers for known issues and We also encourage all users to search our project trackers for known issues and existing feature requests in the [GitLab project](https://gitlab.com/gitlab-org/gitlab/-/issues/).
existing feature requests in the
[GitLab project](https://gitlab.com/gitlab-org/gitlab/-/issues/).
These issues are the best avenue for getting updates on specific product plans These issues are the best avenue for getting updates on specific product plans and for communicating directly with the relevant GitLab team members.
and for communicating directly with the relevant GitLab team members.
<!-- ## Troubleshooting <!-- ## Troubleshooting

View file

@ -736,7 +736,7 @@ After DAST has authenticated with the application, all cookies are collected fro
For each cookie a matching session token is created for use by ZAP. This ensures ZAP is recognized For each cookie a matching session token is created for use by ZAP. This ensures ZAP is recognized
by the application as correctly authenticated. by the application as correctly authenticated.
Authentication supports single form logins, multi-step login forms, and authenticating to URLs outside of the configured target URL. Authentication supports single form logins, multi-step login forms, and authenticating to URLs outside of the configured target URL.
WARNING: WARNING:
**Never** run an authenticated scan against a production server. When an authenticated **Never** run an authenticated scan against a production server. When an authenticated
@ -744,6 +744,17 @@ scan is run, it may perform *any* function that the authenticated user can. This
includes actions like modifying and deleting data, submitting forms, and following links. includes actions like modifying and deleting data, submitting forms, and following links.
Only run an authenticated scan against a test server. Only run an authenticated scan against a test server.
### SSO
DAST can authenticate to websites making use of SSO, with the following restrictions:
- DAST cannot bypass a CAPTCHA if the authentication flow includes one.
- DAST cannot handle multi-factor authentication like one-time passwords (OTP) by using SMS or authenticator apps.
- DAST must get a cookie, or a local or session storage, with a sufficiently random value.
The [authentication debug output](index.md#configure-the-authentication-debug-output) can be helpful for troubleshooting SSO authentication
with DAST.
### Log in using automatic detection of the login form ### Log in using automatic detection of the login form
By providing a `DAST_USERNAME`, `DAST_PASSWORD`, and `DAST_AUTH_URL`, DAST attempts to authenticate to the By providing a `DAST_USERNAME`, `DAST_PASSWORD`, and `DAST_AUTH_URL`, DAST attempts to authenticate to the

View file

@ -6,26 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Free user limit **(FREE SAAS)** # Free user limit **(FREE SAAS)**
From October 19, 2022, namespaces in GitLab.com on the Free tier From October 19, 2022, a five-user limit will apply to top-level [namespaces](namespace/index.md) with private visibility on GitLab SaaS. These limits will roll out gradually, and impacted users will be notified in GitLab.com at least 60 days before the limit is applied.
will be limited to five (5) members per [namespace](namespace/index.md).
This limit applies to top-level private groups.
On the transition date, if your namespace has six or more unique members: When the five-user limit is applied, top-level private namespaces exceeding the user limit are placed in a read-only state. These namespaces cannot write new data to repositories, Git Large File Storage (LFS), packages, or registries.
- Five members will keep a status of `Active`.
- Remaining members will get a status of `Over limit` and lose access to the
group.
- Members invited through a group or project invitation outside of the namespace
will be removed. You can add these members back by inviting them through their
username or email address on the **Members** page for your group or project.
## How active members are determined
On the transition date, we'll automatically select the members who keep their `Active` status
in the following order, until we reach a total of five:
1. Members with the Owner or Maintainer role.
1. The most recently active members.
## Manage members in your namespace ## Manage members in your namespace
@ -43,7 +26,7 @@ Prerequisite:
1. To remove a member, select **Remove user**. 1. To remove a member, select **Remove user**.
If you need more time to manage your members, or to try GitLab features If you need more time to manage your members, or to try GitLab features
with a team of more than five members, you can [start a trial](https://about.gitlab.com/free-trial/). with a team of more than five members, you can [start a trial](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com&glm_content=free-user-limit).
A trial lasts for 30 days and includes an unlimited number of members. A trial lasts for 30 days and includes an unlimited number of members.
## Related topics ## Related topics

View file

@ -13518,19 +13518,10 @@ msgstr ""
msgid "DesignManagement|Are you sure you want to archive the selected designs?" msgid "DesignManagement|Are you sure you want to archive the selected designs?"
msgstr "" msgstr ""
msgid "DesignManagement|Are you sure you want to cancel changes to this comment?"
msgstr ""
msgid "DesignManagement|Are you sure you want to cancel creating this comment?" msgid "DesignManagement|Are you sure you want to cancel creating this comment?"
msgstr "" msgstr ""
msgid "DesignManagement|Cancel changes" msgid "DesignManagement|Are you sure you want to cancel editing this comment?"
msgstr ""
msgid "DesignManagement|Cancel comment confirmation"
msgstr ""
msgid "DesignManagement|Cancel comment update confirmation"
msgstr "" msgstr ""
msgid "DesignManagement|Click the image where you'd like to start a new discussion" msgid "DesignManagement|Click the image where you'd like to start a new discussion"
@ -13539,6 +13530,12 @@ msgstr ""
msgid "DesignManagement|Comment" msgid "DesignManagement|Comment"
msgstr "" msgstr ""
msgid "DesignManagement|Continue creating"
msgstr ""
msgid "DesignManagement|Continue editing"
msgstr ""
msgid "DesignManagement|Could not add a new comment. Please try again." msgid "DesignManagement|Could not add a new comment. Please try again."
msgstr "" msgstr ""
@ -13557,7 +13554,7 @@ msgstr ""
msgid "DesignManagement|Designs" msgid "DesignManagement|Designs"
msgstr "" msgstr ""
msgid "DesignManagement|Discard comment" msgid "DesignManagement|Discard changes"
msgstr "" msgstr ""
msgid "DesignManagement|Discussion" msgid "DesignManagement|Discussion"
@ -13578,12 +13575,6 @@ msgstr ""
msgid "DesignManagement|Go to previous design" msgid "DesignManagement|Go to previous design"
msgstr "" msgstr ""
msgid "DesignManagement|Keep changes"
msgstr ""
msgid "DesignManagement|Keep comment"
msgstr ""
msgid "DesignManagement|Requested design version does not exist. Showing latest version instead" msgid "DesignManagement|Requested design version does not exist. Showing latest version instead"
msgstr "" msgstr ""

View file

@ -62,13 +62,9 @@ module QA
Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) } Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) }
# Workaround for race condition with concurrent group API calls while searching fill_element :group_select_dropdown_search_field, group_name
# Remove Retrier after https://gitlab.com/gitlab-org/gitlab/-/issues/349379 is resolved Support::WaitForRequests.wait_for_requests
Support::Retrier.retry_on_exception do click_button group_name
fill_element :group_select_dropdown_search_field, group_name
Support::WaitForRequests.wait_for_requests
click_button group_name
end
set_access_level(access_level) set_access_level(access_level)
end end

View file

@ -1235,26 +1235,6 @@ RSpec.describe ProjectsController do
get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" } get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
end end
context 'when use_gitaly_pagination_for_refs is disabled' do
before do
stub_feature_flags(use_gitaly_pagination_for_refs: false)
end
it 'does not use gitaly pagination' do
expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit!
expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder|
expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
end
expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder|
expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
end
get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
end
end
context 'when gitaly is unavailable' do context 'when gitaly is unavailable' do
before do before do
expect_next_instance_of(TagsFinder) do |finder| expect_next_instance_of(TagsFinder) do |finder|

View file

@ -1,16 +1,10 @@
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import Autosave from '~/autosave'; import Autosave from '~/autosave';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue'; import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
const showModal = jest.fn(); jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
const GlModal = {
template: '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>',
methods: {
show: showModal,
},
};
describe('Design reply form component', () => { describe('Design reply form component', () => {
let wrapper; let wrapper;
@ -19,7 +13,6 @@ describe('Design reply form component', () => {
const findTextarea = () => wrapper.find('textarea'); const findTextarea = () => wrapper.find('textarea');
const findSubmitButton = () => wrapper.findComponent({ ref: 'submitButton' }); const findSubmitButton = () => wrapper.findComponent({ ref: 'submitButton' });
const findCancelButton = () => wrapper.findComponent({ ref: 'cancelButton' }); const findCancelButton = () => wrapper.findComponent({ ref: 'cancelButton' });
const findModal = () => wrapper.findComponent({ ref: 'cancelCommentModal' });
function createComponent(props = {}, mountOptions = {}) { function createComponent(props = {}, mountOptions = {}) {
wrapper = mount(DesignReplyForm, { wrapper = mount(DesignReplyForm, {
@ -29,7 +22,6 @@ describe('Design reply form component', () => {
noteableId: 'gid://gitlab/DesignManagement::Design/6', noteableId: 'gid://gitlab/DesignManagement::Design/6',
...props, ...props,
}, },
stubs: { GlModal },
...mountOptions, ...mountOptions,
}); });
} }
@ -42,6 +34,7 @@ describe('Design reply form component', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
window.gon = originalGon; window.gon = originalGon;
confirmAction.mockReset();
}); });
it('textarea has focus after component mount', () => { it('textarea has focus after component mount', () => {
@ -199,7 +192,7 @@ describe('Design reply form component', () => {
await nextTick(); await nextTick();
findTextarea().trigger('keyup.esc'); findTextarea().trigger('keyup.esc');
expect(showModal).toHaveBeenCalled(); expect(confirmAction).toHaveBeenCalled();
}); });
it('emits cancelForm event on Cancel button click if text was not changed', () => { it('emits cancelForm event on Cancel button click if text was not changed', () => {
@ -213,17 +206,41 @@ describe('Design reply form component', () => {
await nextTick(); await nextTick();
findCancelButton().trigger('click'); findCancelButton().trigger('click');
expect(showModal).toHaveBeenCalled(); expect(confirmAction).toHaveBeenCalled();
}); });
it('emits cancelForm event on modal Ok button click', () => { it('emits cancelForm event when confirmed', async () => {
confirmAction.mockResolvedValueOnce(true);
const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset'); const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
findTextarea().trigger('keyup.esc'); wrapper.setProps({ value: 'test3' });
findModal().vm.$emit('ok'); await nextTick();
expect(wrapper.emitted('cancel-form')).toHaveLength(2); findTextarea().trigger('keyup.esc');
await nextTick();
expect(confirmAction).toHaveBeenCalled();
await nextTick();
expect(wrapper.emitted('cancel-form')).toHaveLength(1);
expect(autosaveResetSpy).toHaveBeenCalled(); expect(autosaveResetSpy).toHaveBeenCalled();
}); });
it("doesn't emit cancelForm event when not confirmed", async () => {
confirmAction.mockResolvedValueOnce(false);
const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
wrapper.setProps({ value: 'test3' });
await nextTick();
findTextarea().trigger('keyup.esc');
await nextTick();
expect(confirmAction).toHaveBeenCalled();
await nextTick();
expect(wrapper.emitted('cancel-form')).toBeUndefined();
expect(autosaveResetSpy).not.toHaveBeenCalled();
});
}); });
}); });

View file

@ -53,6 +53,8 @@ RSpec.describe ChangePublicProjectsCostFactor, migration: :gitlab_ci do
expect(shared_2.public_projects_minutes_cost_factor).to eq(0) expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
expect(shared_3.public_projects_minutes_cost_factor).to eq(1) expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
expect(group_1.public_projects_minutes_cost_factor).to eq(0) expect(group_1.public_projects_minutes_cost_factor).to eq(0)
schema_migrate_up!
end end
end end
end end

View file

@ -157,12 +157,20 @@ RSpec.describe BulkImports::ExportStatus do
end end
context 'when something goes wrong during export status fetch' do context 'when something goes wrong during export status fetch' do
it 'returns exception class as error' do it 'returns exception class as error and memoizes return value' do
allow_next_instance_of(BulkImports::Clients::HTTP) do |client| allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:get).and_raise(StandardError, 'Error!') allow(client).to receive(:get).and_raise(StandardError, 'Error!')
end end
expect(subject.error).to eq('Error!') expect(subject.error).to eq('Error!')
expect(subject.failed?).to eq(true)
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:get).and_return({ 'relation' => relation, 'status' => 'finished' })
end
expect(subject.error).to eq('Error!')
expect(subject.failed?).to eq(true)
end end
end end
end end