diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-01 09:09:58 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-01 09:09:58 +0300 |
commit | 01600ff2d11bb7f77bb70bf143a95efdf59931e1 (patch) | |
tree | 40edee42f0472379f77caed98e2494627436734d | |
parent | b42648225b016a6c7645befded54a93332661526 (diff) |
Add latest changes from gitlab-org/gitlab@master
16 files changed, 266 insertions, 148 deletions
diff --git a/app/assets/javascripts/authentication/two_factor_auth/components/manage_two_factor_form.vue b/app/assets/javascripts/authentication/two_factor_auth/components/manage_two_factor_form.vue index 0b748f18cb2..484c6524d0e 100644 --- a/app/assets/javascripts/authentication/two_factor_auth/components/manage_two_factor_form.vue +++ b/app/assets/javascripts/authentication/two_factor_auth/components/manage_two_factor_form.vue @@ -1,26 +1,46 @@ <script> -import { GlFormInput, GlFormGroup, GlButton, GlForm } from '@gitlab/ui'; +import { GlFormInput, GlFormGroup, GlButton, GlForm, GlModal } from '@gitlab/ui'; import csrf from '~/lib/utils/csrf'; import { __ } from '~/locale'; export const i18n = { currentPassword: __('Current password'), + confirmTitle: __('Are you sure?'), confirmWebAuthn: __( - 'Are you sure? This will invalidate your registered applications and U2F / WebAuthn devices.', + 'This will invalidate your registered applications and U2F / WebAuthn devices.', ), - confirm: __('Are you sure? This will invalidate your registered applications and U2F devices.'), + confirm: __('This will invalidate your registered applications and U2F devices.'), disableTwoFactor: __('Disable two-factor authentication'), + disable: __('Disable'), + cancel: __('Cancel'), regenerateRecoveryCodes: __('Regenerate recovery codes'), + currentPasswordInvalidFeedback: __('Please enter your current password.'), }; export default { name: 'ManageTwoFactorForm', i18n, + modalId: 'manage-two-factor-auth-confirm-modal', + modalActions: { + primary: { + text: i18n.disable, + attributes: { + variant: 'danger', + }, + }, + secondary: { + text: i18n.cancel, + attributes: { + variant: 'default', + }, + }, + }, components: { GlForm, GlFormInput, GlFormGroup, GlButton, + GlModal, }, inject: [ 'webauthnEnabled', @@ -32,8 +52,11 @@ export default { ], data() { return { - method: '', - action: '#', + method: null, + action: null, + currentPassword: '', + currentPasswordState: null, + showConfirmModal: false, }; }, computed: { @@ -46,9 +69,34 @@ export default { }, }, methods: { - handleFormSubmit(event) { - this.method = event.submitter.dataset.formMethod; - this.action = event.submitter.dataset.formAction; + submitForm() { + this.$refs.form.$el.submit(); + }, + async handleSubmitButtonClick({ method, action, confirm = false }) { + this.method = method; + this.action = action; + + if (this.isCurrentPasswordRequired && this.currentPassword === '') { + this.currentPasswordState = false; + + return; + } + + this.currentPasswordState = null; + + if (confirm) { + this.showConfirmModal = true; + + return; + } + + // Wait for form action and method to be updated + await this.$nextTick(); + + this.submitForm(); + }, + handleModalPrimary() { + this.submitForm(); }, }, csrf, @@ -57,10 +105,11 @@ export default { <template> <gl-form - class="gl-display-inline-block" + ref="form" + class="gl-sm-display-inline-block" method="post" :action="action" - @submit="handleFormSubmit($event)" + @submit.prevent > <input type="hidden" name="_method" data-testid="test-2fa-method-field" :value="method" /> <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> @@ -69,35 +118,59 @@ export default { v-if="isCurrentPasswordRequired" :label="$options.i18n.currentPassword" label-for="current-password" + :state="currentPasswordState" + :invalid-feedback="$options.i18n.currentPasswordInvalidFeedback" > <gl-form-input id="current-password" + v-model="currentPassword" type="password" name="current_password" - required + :state="currentPasswordState" data-qa-selector="current_password_field" /> </gl-form-group> - <gl-button - type="submit" - class="btn-danger gl-mr-3 gl-display-inline-block" - data-testid="test-2fa-disable-button" - variant="danger" - :data-confirm="confirmText" - :data-form-action="profileTwoFactorAuthPath" - :data-form-method="profileTwoFactorAuthMethod" - > - {{ $options.i18n.disableTwoFactor }} - </gl-button> - <gl-button - type="submit" - class="gl-display-inline-block" - data-testid="test-2fa-regenerate-codes-button" - :data-form-action="codesProfileTwoFactorAuthPath" - :data-form-method="codesProfileTwoFactorAuthMethod" + <div class="gl-display-flex gl-flex-wrap"> + <gl-button + type="submit" + class="gl-sm-mr-3 gl-w-full gl-sm-w-auto" + data-testid="test-2fa-disable-button" + variant="danger" + @click.prevent=" + handleSubmitButtonClick({ + method: profileTwoFactorAuthMethod, + action: profileTwoFactorAuthPath, + confirm: true, + }) + " + > + {{ $options.i18n.disableTwoFactor }} + </gl-button> + <gl-button + type="submit" + class="gl-mt-3 gl-sm-mt-0 gl-w-full gl-sm-w-auto" + data-testid="test-2fa-regenerate-codes-button" + @click.prevent=" + handleSubmitButtonClick({ + method: codesProfileTwoFactorAuthMethod, + action: codesProfileTwoFactorAuthPath, + }) + " + > + {{ $options.i18n.regenerateRecoveryCodes }} + </gl-button> + </div> + <gl-modal + v-model="showConfirmModal" + :modal-id="$options.modalId" + size="sm" + :title="$options.i18n.confirmTitle" + :action-primary="$options.modalActions.primary" + :action-secondary="$options.modalActions.secondary" + @primary="handleModalPrimary" > - {{ $options.i18n.regenerateRecoveryCodes }} - </gl-button> + {{ confirmText }} + </gl-modal> </gl-form> </template> diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md index b097d6b0729..57c8c3bf59c 100644 --- a/doc/development/testing_guide/end_to_end/index.md +++ b/doc/development/testing_guide/end_to_end/index.md @@ -147,7 +147,7 @@ as well as these: | Variable | Description | |-|-| | `QA_SCENARIO` | The scenario to run (default `Test::Instance::Image`) | -| `QA_TESTS` | The test(s) to run (no default, which means run all the tests in the scenario). Use file paths as you would when running tests via RSpec, e.g., `qa/specs/features/ee/browser_ui` would include all the `EE` UI tests. | +| `QA_TESTS` | The test(s) to run (no default, which means run all the tests in the scenario). Use file paths as you would when running tests via RSpec, for example, `qa/specs/features/ee/browser_ui` would include all the `EE` UI tests. | | `QA_RSPEC_TAGS` | The RSpec tags to add (no default) | For now [manual jobs with custom variables don't use the same variable diff --git a/doc/development/testing_guide/end_to_end/style_guide.md b/doc/development/testing_guide/end_to_end/style_guide.md index 22ddae6e836..a9571df1188 100644 --- a/doc/development/testing_guide/end_to_end/style_guide.md +++ b/doc/development/testing_guide/end_to_end/style_guide.md @@ -14,7 +14,7 @@ This document describes the conventions used at GitLab for writing End-to-end (E When clicking in a single link to navigate, use `click_`. -E.g.: +For example: ```ruby def click_ci_cd_pipelines @@ -33,7 +33,7 @@ From a testing perspective, if we want to check that clicking a link, or a butto When interacting with multiple elements to go to a page, use `go_to_`. -E.g.: +For example: ```ruby def go_to_operations_environments @@ -63,12 +63,12 @@ We follow a simple formula roughly based on Hungarian notation. - `type`: A generic control on the page that can be seen by a user. - `_button` - `_checkbox` - - `_container`: an element that includes other elements, but doesn't present visible content itself. E.g., an element that has a third-party editor inside it, but which isn't the editor itself and so doesn't include the editor's content. + - `_container`: an element that includes other elements, but doesn't present visible content itself. For example, an element that has a third-party editor inside it, but which isn't the editor itself and so doesn't include the editor's content. - `_content`: any element that contains text, images, or any other content displayed to the user. - `_dropdown` - `_field`: a text input element. - `_link` - - `_modal`: a popup modal dialog, e.g., a confirmation prompt. + - `_modal`: a popup modal dialog, for example, a confirmation prompt. - `_placeholder`: a temporary element that appears while content is loading. For example, the elements that are displayed instead of discussions while the discussions are being fetched. - `_radio` - `_tab` @@ -116,7 +116,7 @@ we use the name of the page object in [snake_case](https://en.wikipedia.org/wiki (all lowercase, with words separated by an underscore). See good and bad examples below. While we prefer to follow the standard in most cases, it is also acceptable to -use common abbreviations (e.g., `mr`) or other alternatives, as long as +use common abbreviations (for example, `mr`) or other alternatives, as long as the name is not ambiguous. This can include appending `_page` if it helps to avoid confusion or make the code more readable. For example, if a page object is named `New`, it could be confusing to name the block argument `new` because that diff --git a/doc/development/testing_guide/testing_migrations_guide.md b/doc/development/testing_guide/testing_migrations_guide.md index 88b2eb730c1..4092c1a2f6d 100644 --- a/doc/development/testing_guide/testing_migrations_guide.md +++ b/doc/development/testing_guide/testing_migrations_guide.md @@ -38,7 +38,7 @@ ensures proper isolation. ## Testing an `ActiveRecord::Migration` class -To test an `ActiveRecord::Migration` class (i.e., a +To test an `ActiveRecord::Migration` class (for example, a regular migration `db/migrate` or a post-migration `db/post_migrate`), you must load the migration file by using the `require_migration!` helper method because it is not autoloaded by Rails. diff --git a/doc/development/uploads.md b/doc/development/uploads.md index 9c5686db0f6..6d8b951be83 100644 --- a/doc/development/uploads.md +++ b/doc/development/uploads.md @@ -7,12 +7,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Uploads development documentation [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) has special rules for handling uploads. -To prevent occupying a Ruby process on I/O operations, we process the upload in workhorse, where is cheaper. +We process the upload in Workhorse to prevent occupying a Ruby process on I/O operations and because it is cheaper. This process can also directly upload to object storage. ## The problem description -The following graph explains machine boundaries in a scalable GitLab installation. Without any workhorse optimization in place, we can expect incoming requests to follow the numbers on the arrows. +The following graph explains machine boundaries in a scalable GitLab installation. Without any Workhorse optimization in place, we can expect incoming requests to follow the numbers on the arrows. ```mermaid graph TB @@ -27,10 +27,10 @@ graph TB subgraph "redis cluster" r(persisted redis) end - LB-- 1 -->workhorse + LB-- 1 -->Workhorse subgraph "web or API fleet" - workhorse-- 2 -->rails + Workhorse-- 2 -->rails end rails-- "3 (write files)" -->nfs rails-- "4 (schedule a job)" -->r @@ -63,12 +63,12 @@ graph TB subgraph "redis cluster" r(persisted redis) end - LB-- 1 -->workhorse + LB-- 1 -->Workhorse subgraph "web or API fleet" - workhorse-- "3 (without files)" -->rails + Workhorse-- "3 (without files)" -->rails end - workhorse -- "2 (write files)" -->nfs + Workhorse -- "2 (write files)" -->nfs rails-- "4 (schedule a job)" -->r subgraph sidekiq @@ -120,7 +120,7 @@ We have three kinds of file encoding in our uploads: 1. <i class="fa fa-check-circle"></i> **multipart**: `multipart/form-data` is the most common, a file is encoded as a part of a multipart encoded request. 1. <i class="fa fa-check-circle"></i> **body**: some APIs uploads files as the whole request body. -1. <i class="fa fa-times-circle"></i> **JSON**: some JSON API uploads files as base64 encoded strings. This requires a change to GitLab Workhorse, +1. <i class="fa fa-times-circle"></i> **JSON**: some JSON APIs upload files as base64-encoded strings. This requires a change to GitLab Workhorse, which is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/325068). ## Uploading technologies @@ -131,9 +131,9 @@ GitLab supports 3 kinds of uploading technologies, here follows a brief descript ### Rack Multipart upload -This is the default kind of upload, and it's most expensive in terms of resources. +This is the default kind of upload, and it's the most expensive in terms of resources. -In this case, workhorse is unaware of files being uploaded and acts as a regular proxy. +In this case, Workhorse is unaware of files being uploaded and acts as a regular proxy. When a multipart request reaches the rails application, `Rack::Multipart` leaves behind temporary files in `/tmp` and uses valuable Ruby process time to copy files around. @@ -213,7 +213,7 @@ sequenceDiagram This is the more advanced acceleration technique we have in place. -Workhorse asks rails for temporary pre-signed object storage URLs and directly uploads to object storage. +Workhorse asks Rails for temporary pre-signed object storage URLs and directly uploads to object storage. In this setup, an extra Rails route must be implemented in order to handle authorization. Examples of this can be found in: @@ -221,7 +221,7 @@ In this setup, an extra Rails route must be implemented in order to handle autho and [its routes](https://gitlab.com/gitlab-org/gitlab/-/blob/cc723071ad337573e0360a879cbf99bc4fb7adb9/config/routes/git_http.rb#L31-32). - [API endpoints for uploading packages](packages.md#file-uploads). -This falls back to _disk buffered upload_ when `direct_upload` is disabled inside the [object storage setting](../administration/uploads.md#object-storage-settings). +Direct upload falls back to _disk buffered upload_ when `direct_upload` is disabled inside the [object storage setting](../administration/uploads.md#object-storage-settings). The answer to the `/authorize` call contains only a file system path. ```mermaid @@ -275,7 +275,7 @@ sequenceDiagram In this section, we describe how to add a new upload route [accelerated](#uploading-technologies) by Workhorse for [body and multipart](#upload-encodings) encoded uploads. -Uploads routes belong to one of these categories: +Upload routes belong to one of these categories: 1. Rails controllers: uploads handled by Rails controllers. 1. Grape API: uploads handled by a Grape API endpoint. @@ -289,7 +289,7 @@ GraphQL uploads do not support [direct upload](#direct-upload) yet. Depending on For both the Rails controller and Grape API uploads, Workhorse has to be updated in order to get the support for the new upload route. -1. Open an new issue in the [Workhorse tracker](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/new) describing precisely the new upload route: +1. Open a new issue in the [Workhorse tracker](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/new) describing precisely the new upload route: - The route's URL. - The [upload encoding](#upload-encodings). - If possible, provide a dump of the upload request. diff --git a/doc/index.md b/doc/index.md index a638647e3e3..57eb9e195ff 100644 --- a/doc/index.md +++ b/doc/index.md @@ -6,12 +6,18 @@ comments: false description: 'Learn how to use and administer GitLab, the most scalable Git-based fully integrated platform for software development.' --- +<!-- markdownlint-disable MD044 --> +<!-- MD044/proper-names test disabled after this line to make page compatible with markdownlint-cli 0.29.0. --> +<!-- See https://docs.gitlab.com/ee/development/documentation/testing.html#disable-markdownlint-tests --> + <div class="d-none"> <h3>Visit <a href="https://docs.gitlab.com/ee/">docs.gitlab.com</a> for the latest version of this help information with enhanced navigation, discoverability, and readability.</h3> </div> <!-- the div above will not display on the docs site but will display on /help --> +<!-- markdownlint-enable MD044 --> + # GitLab Docs Welcome to [GitLab](https://about.gitlab.com/) documentation. diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 46b4ef6c96b..a6972e464dd 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -60,6 +60,10 @@ maximum of two directory levels from the repository's root. For example, the `gemnasium-dependency_scanning` job is enabled if a repository contains either `Gemfile`, `api/Gemfile`, or `api/client/Gemfile`, but not if the only supported dependency file is `api/v1/client/Gemfile`. +<!-- markdownlint-disable MD044 --> +<!-- MD044/proper-names test disabled after this line to make page compatible with markdownlint-cli 0.29.0. --> +<!-- See https://docs.gitlab.com/ee/development/documentation/testing.html#disable-markdownlint-tests --> + The following languages and dependency managers are supported: <style> @@ -246,6 +250,8 @@ table.supported-languages ul { </tbody> </table> +<!-- markdownlint-enable MD044 --> + ### Notes regarding supported languages and package managers 1. Although Gradle with Java 8 is supported, there are other issues such that Android project builds are not supported at this time. Please see the backlog issue [Android support for Dependency Scanning (gemnasium-maven)](https://gitlab.com/gitlab-org/gitlab/-/issues/336866) for more details. diff --git a/doc/user/project/pages/getting_started/pages_ci_cd_template.md b/doc/user/project/pages/getting_started/pages_ci_cd_template.md index 74bc54c63f0..55a57bd9e07 100644 --- a/doc/user/project/pages/getting_started/pages_ci_cd_template.md +++ b/doc/user/project/pages/getting_started/pages_ci_cd_template.md @@ -13,29 +13,17 @@ the CI/CD pipeline to generate a Pages website. Use a `.gitlab-ci.yml` template when you have an existing project that you want to add a Pages site to. -Your GitLab repository should contain files specific to an SSG, or plain HTML. -After you complete these steps, you may need to do additional -configuration for the Pages site to generate properly. +Your GitLab repository should contain files specific to an SSG, or plain HTML. After you complete +these steps, you may have to do additional configuration for the Pages site to generate properly. 1. On the left sidebar, select **Project information**. -1. Click **Set up CI/CD**. - - ![setup GitLab CI/CD](../img/setup_ci_v13_1.png) - - If this button is not available, CI/CD is already configured for - your project. You may want to browse the `.gitlab-ci.yml` files - [in these projects instead](https://gitlab.com/pages). - +1. Click **Set up CI/CD**. If this button is not available, CI/CD is already configured for your project. + You may want to browse the `.gitlab-ci.yml` files [in these projects instead](https://gitlab.com/pages). 1. From the **Apply a template** list, choose a template for the SSG you're using. - You can also choose plain HTML. - - ![gitlab-ci templates](../img/choose_ci_template_v13_1.png) - - If you don't find a corresponding template, you can view the + You can also choose plain HTML. If you don't find a corresponding template, you can view the [GitLab Pages group of sample projects](https://gitlab.com/pages). These projects contain `.gitlab-ci.yml` files that you can modify for your needs. You can also [learn how to write your own `.gitlab-ci.yml` file for GitLab Pages](pages_from_scratch.md). - 1. Save and commit the `.gitlab-ci.yml` file. If everything is configured correctly, the site can take approximately 30 minutes to deploy. diff --git a/doc/user/project/pages/img/choose_ci_template_v13_1.png b/doc/user/project/pages/img/choose_ci_template_v13_1.png Binary files differdeleted file mode 100644 index a0c25ba1642..00000000000 --- a/doc/user/project/pages/img/choose_ci_template_v13_1.png +++ /dev/null diff --git a/doc/user/project/pages/img/setup_ci_v13_1.png b/doc/user/project/pages/img/setup_ci_v13_1.png Binary files differdeleted file mode 100644 index 2cf1c05d6d8..00000000000 --- a/doc/user/project/pages/img/setup_ci_v13_1.png +++ /dev/null diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9a07fac8368..44fb580caa0 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4585,12 +4585,6 @@ msgstr "" msgid "Are you sure? The device will be signed out of GitLab and all remember me tokens revoked." msgstr "" -msgid "Are you sure? This will invalidate your registered applications and U2F / WebAuthn devices." -msgstr "" - -msgid "Are you sure? This will invalidate your registered applications and U2F devices." -msgstr "" - msgid "Arrange charts" msgstr "" @@ -25747,6 +25741,9 @@ msgstr "" msgid "Please enter or upload a valid license." msgstr "" +msgid "Please enter your current password." +msgstr "" + msgid "Please fill in a descriptive name for your group." msgstr "" @@ -35338,6 +35335,12 @@ msgstr "" msgid "This variable can not be masked." msgstr "" +msgid "This will invalidate your registered applications and U2F / WebAuthn devices." +msgstr "" + +msgid "This will invalidate your registered applications and U2F devices." +msgstr "" + msgid "This will redirect you to an external sign in page." msgstr "" diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb index 3f5789e119a..fcb857df93f 100644 --- a/spec/features/profiles/two_factor_auths_spec.rb +++ b/spec/features/profiles/two_factor_auths_spec.rb @@ -57,7 +57,9 @@ RSpec.describe 'Two factor auths' do click_button 'Disable two-factor authentication' - page.accept_alert + page.within('[role="dialog"]') do + click_button 'Disable' + end expect(page).to have_content('You must provide a valid current password') @@ -65,7 +67,9 @@ RSpec.describe 'Two factor auths' do click_button 'Disable two-factor authentication' - page.accept_alert + page.within('[role="dialog"]') do + click_button 'Disable' + end expect(page).to have_content('Two-factor authentication has been disabled successfully!') expect(page).to have_content('Enable two-factor authentication') @@ -95,7 +99,9 @@ RSpec.describe 'Two factor auths' do click_button 'Disable two-factor authentication' - page.accept_alert + page.within('[role="dialog"]') do + click_button 'Disable' + end expect(page).to have_content('Two-factor authentication has been disabled successfully!') expect(page).to have_content('Enable two-factor authentication') diff --git a/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js b/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js index 870375318e3..694c16a85c4 100644 --- a/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js +++ b/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js @@ -1,7 +1,6 @@ -import { within } from '@testing-library/dom'; -import { GlForm } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { GlForm, GlModal } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import { stubComponent } from 'helpers/stub_component'; import ManageTwoFactorForm, { i18n, } from '~/authentication/two_factor_auth/components/manage_two_factor_form.vue'; @@ -17,100 +16,133 @@ describe('ManageTwoFactorForm', () => { let wrapper; const createComponent = (options = {}) => { - wrapper = extendedWrapper( - mount(ManageTwoFactorForm, { - provide: { - ...defaultProvide, - webauthnEnabled: options?.webauthnEnabled ?? false, - isCurrentPasswordRequired: options?.currentPasswordRequired ?? true, - }, - }), - ); + wrapper = mountExtended(ManageTwoFactorForm, { + provide: { + ...defaultProvide, + webauthnEnabled: options?.webauthnEnabled ?? false, + isCurrentPasswordRequired: options?.currentPasswordRequired ?? true, + }, + stubs: { + GlModal: stubComponent(GlModal, { + template: ` + <div> + <slot name="modal-title"></slot> + <slot></slot> + <slot name="modal-footer"></slot> + </div>`, + }), + }, + }); }; - const queryByText = (text, options) => within(wrapper.element).queryByText(text, options); - const queryByLabelText = (text, options) => - within(wrapper.element).queryByLabelText(text, options); - const findForm = () => wrapper.findComponent(GlForm); const findMethodInput = () => wrapper.findByTestId('test-2fa-method-field'); const findDisableButton = () => wrapper.findByTestId('test-2fa-disable-button'); const findRegenerateCodesButton = () => wrapper.findByTestId('test-2fa-regenerate-codes-button'); + const findConfirmationModal = () => wrapper.findComponent(GlModal); + + const itShowsConfirmationModal = (confirmText) => { + it('shows confirmation modal', async () => { + await wrapper.findByLabelText('Current password').setValue('foo bar'); + await findDisableButton().trigger('click'); + + expect(findConfirmationModal().props('visible')).toBe(true); + expect(findConfirmationModal().html()).toContain(confirmText); + }); + }; + + const itShowsValidationMessageIfCurrentPasswordFieldIsEmpty = (findButtonFunction) => { + it('shows validation message if `Current password` is empty', async () => { + await findButtonFunction().trigger('click'); + + expect(wrapper.findByText(i18n.currentPasswordInvalidFeedback).exists()).toBe(true); + }); + }; beforeEach(() => { createComponent(); }); - describe('Current password field', () => { - it('renders the current password field', () => { - expect(queryByLabelText(i18n.currentPassword).tagName).toEqual('INPUT'); + describe('`Current password` field', () => { + describe('when required', () => { + it('renders the current password field', () => { + expect(wrapper.findByLabelText(i18n.currentPassword).exists()).toBe(true); + }); }); - }); - describe('when current password is not required', () => { - beforeEach(() => { - createComponent({ - currentPasswordRequired: false, + describe('when not required', () => { + beforeEach(() => { + createComponent({ + currentPasswordRequired: false, + }); }); - }); - it('does not render the current password field', () => { - expect(queryByLabelText(i18n.currentPassword)).toBe(null); + it('does not render the current password field', () => { + expect(wrapper.findByLabelText(i18n.currentPassword).exists()).toBe(false); + }); }); }); describe('Disable button', () => { it('renders the component with correct attributes', () => { expect(findDisableButton().exists()).toBe(true); - expect(findDisableButton().attributes()).toMatchObject({ - 'data-confirm': i18n.confirm, - 'data-form-action': defaultProvide.profileTwoFactorAuthPath, - 'data-form-method': defaultProvide.profileTwoFactorAuthMethod, - }); }); - it('has the right confirm text', () => { - expect(findDisableButton().attributes('data-confirm')).toBe(i18n.confirm); - }); + describe('when clicked', () => { + itShowsValidationMessageIfCurrentPasswordFieldIsEmpty(findDisableButton); - describe('when webauthnEnabled', () => { - beforeEach(() => { - createComponent({ - webauthnEnabled: true, + itShowsConfirmationModal(i18n.confirm); + + describe('when webauthnEnabled', () => { + beforeEach(() => { + createComponent({ + webauthnEnabled: true, + }); }); - }); - it('has the right confirm text', () => { - expect(findDisableButton().attributes('data-confirm')).toBe(i18n.confirmWebAuthn); + itShowsConfirmationModal(i18n.confirmWebAuthn); }); - }); - it('modifies the form action and method when submitted through the button', async () => { - const form = findForm(); - const disableButton = findDisableButton().element; - const methodInput = findMethodInput(); + it('modifies the form action and method when submitted through the button', async () => { + const form = findForm(); + const methodInput = findMethodInput(); + const submitSpy = jest.spyOn(form.element, 'submit'); + + await wrapper.findByLabelText('Current password').setValue('foo bar'); + await findDisableButton().trigger('click'); + + expect(form.attributes('action')).toBe(defaultProvide.profileTwoFactorAuthPath); + expect(methodInput.attributes('value')).toBe(defaultProvide.profileTwoFactorAuthMethod); - await form.vm.$emit('submit', { submitter: disableButton }); + findConfirmationModal().vm.$emit('primary'); - expect(form.attributes('action')).toBe(defaultProvide.profileTwoFactorAuthPath); - expect(methodInput.attributes('value')).toBe(defaultProvide.profileTwoFactorAuthMethod); + expect(submitSpy).toHaveBeenCalled(); + }); }); }); describe('Regenerate recovery codes button', () => { it('renders the button', () => { - expect(queryByText(i18n.regenerateRecoveryCodes)).toEqual(expect.any(HTMLElement)); + expect(findRegenerateCodesButton().exists()).toBe(true); }); - it('modifies the form action and method when submitted through the button', async () => { - const form = findForm(); - const regenerateCodesButton = findRegenerateCodesButton().element; - const methodInput = findMethodInput(); + describe('when clicked', () => { + itShowsValidationMessageIfCurrentPasswordFieldIsEmpty(findRegenerateCodesButton); + + it('modifies the form action and method when submitted through the button', async () => { + const form = findForm(); + const methodInput = findMethodInput(); + const submitSpy = jest.spyOn(form.element, 'submit'); - await form.vm.$emit('submit', { submitter: regenerateCodesButton }); + await wrapper.findByLabelText('Current password').setValue('foo bar'); + await findRegenerateCodesButton().trigger('click'); - expect(form.attributes('action')).toBe(defaultProvide.codesProfileTwoFactorAuthPath); - expect(methodInput.attributes('value')).toBe(defaultProvide.codesProfileTwoFactorAuthMethod); + expect(form.attributes('action')).toBe(defaultProvide.codesProfileTwoFactorAuthPath); + expect(methodInput.attributes('value')).toBe( + defaultProvide.codesProfileTwoFactorAuthMethod, + ); + expect(submitSpy).toHaveBeenCalled(); + }); }); }); }); diff --git a/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb b/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb index 0a1f04ed793..352eb596cd9 100644 --- a/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb +++ b/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb @@ -36,7 +36,7 @@ RSpec.describe Gitlab::Email::Hook::SmimeSignatureInterceptor do end before do - allow(Gitlab::X509::Certificate).to receive_messages(from_files: certificate) + allow(Gitlab::Email::Hook::SmimeSignatureInterceptor).to receive(:certificate).and_return(certificate) Mail.register_interceptor(described_class) mail.deliver_now diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index e485ba6f5d8..5e88b71733d 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -201,9 +201,11 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do describe 'reindex' do let(:reindex) { double('reindex') } let(:indexes) { double('indexes') } + let(:databases) { Gitlab::Database.databases } + let(:databases_count) { databases.count } it 'cleans up any leftover indexes' do - expect(Gitlab::Database::Reindexing).to receive(:cleanup_leftovers!) + expect(Gitlab::Database::Reindexing).to receive(:cleanup_leftovers!).exactly(databases_count).times run_rake_task('gitlab:db:reindex') end @@ -212,8 +214,8 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do it 'executes async index creation prior to any reindexing actions' do stub_feature_flags(database_async_index_creation: true) - expect(Gitlab::Database::AsyncIndexes).to receive(:create_pending_indexes!).ordered - expect(Gitlab::Database::Reindexing).to receive(:perform).ordered + expect(Gitlab::Database::AsyncIndexes).to receive(:create_pending_indexes!).ordered.exactly(databases_count).times + expect(Gitlab::Database::Reindexing).to receive(:perform).ordered.exactly(databases_count).times run_rake_task('gitlab:db:reindex') end @@ -231,8 +233,8 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do context 'when no index_name is given' do it 'uses all candidate indexes' do - expect(Gitlab::Database::PostgresIndex).to receive(:reindexing_support).and_return(indexes) - expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes) + expect(Gitlab::Database::PostgresIndex).to receive(:reindexing_support).exactly(databases_count).times.and_return(indexes) + expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes).exactly(databases_count).times run_rake_task('gitlab:db:reindex') end @@ -247,7 +249,8 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do it 'calls the index rebuilder with the proper arguments' do allow(indexes).to receive(:where).with(identifier: 'public.foo_idx').and_return([index]) - expect(Gitlab::Database::Reindexing).to receive(:perform).with([index]) + expect(Gitlab::Database::Reindexing).to receive(:perform).with([index]).ordered + expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes).ordered if databases.many? run_rake_task('gitlab:db:reindex', '[public.foo_idx]') end @@ -255,16 +258,17 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do context 'when database name is provided' do it 'calls the index rebuilder with the proper arguments when the database name match' do allow(indexes).to receive(:where).with(identifier: 'public.foo_idx').and_return([index]) - expect(Gitlab::Database::Reindexing).to receive(:perform).with([index]) + expect(Gitlab::Database::Reindexing).to receive(:perform).with([index]).ordered + expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes).ordered if databases.many? run_rake_task('gitlab:db:reindex', '[public.foo_idx,main]') end it 'ignores the index and uses all candidate indexes if database name does not match' do - expect(Gitlab::Database::PostgresIndex).to receive(:reindexing_support).and_return(indexes) - expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes) + expect(Gitlab::Database::PostgresIndex).to receive(:reindexing_support).exactly(databases_count).times.and_return(indexes) + expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes).exactly(databases_count).times - run_rake_task('gitlab:db:reindex', '[public.foo_idx,ci]') + run_rake_task('gitlab:db:reindex', '[public.foo_idx,no_such_database]') end end diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb index 1993edb804f..3e313610054 100644 --- a/spec/workers/emails_on_push_worker_spec.rb +++ b/spec/workers/emails_on_push_worker_spec.rb @@ -157,7 +157,7 @@ RSpec.describe EmailsOnPushWorker, :mailer do end before do - allow(Gitlab::X509::Certificate).to receive_messages(from_files: certificate) + allow(Gitlab::Email::Hook::SmimeSignatureInterceptor).to receive(:certificate).and_return(certificate) Mail.register_interceptor(Gitlab::Email::Hook::SmimeSignatureInterceptor) end |