Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-11-01 06:09:58 +00:00
parent b42648225b
commit 01600ff2d1
16 changed files with 269 additions and 151 deletions

View File

@ -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"
<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.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"
>
{{ $options.i18n.regenerateRecoveryCodes }}
</gl-button>
{{ confirmText }}
</gl-modal>
</gl-form>
</template>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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 ""

View File

@ -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')

View File

@ -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('when current password is not required', () => {
beforeEach(() => {
createComponent({
currentPasswordRequired: false,
describe('`Current password` field', () => {
describe('when required', () => {
it('renders the current password field', () => {
expect(wrapper.findByLabelText(i18n.currentPassword).exists()).toBe(true);
});
});
it('does not render the current password field', () => {
expect(queryByLabelText(i18n.currentPassword)).toBe(null);
describe('when not required', () => {
beforeEach(() => {
createComponent({
currentPasswordRequired: false,
});
});
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,
});
});
itShowsConfirmationModal(i18n.confirmWebAuthn);
});
it('has the right confirm text', () => {
expect(findDisableButton().attributes('data-confirm')).toBe(i18n.confirmWebAuthn);
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);
findConfirmationModal().vm.$emit('primary');
expect(submitSpy).toHaveBeenCalled();
});
});
it('modifies the form action and method when submitted through the button', async () => {
const form = findForm();
const disableButton = findDisableButton().element;
const methodInput = findMethodInput();
await form.vm.$emit('submit', { submitter: disableButton });
expect(form.attributes('action')).toBe(defaultProvide.profileTwoFactorAuthPath);
expect(methodInput.attributes('value')).toBe(defaultProvide.profileTwoFactorAuthMethod);
});
});
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);
await form.vm.$emit('submit', { submitter: regenerateCodesButton });
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');
expect(form.attributes('action')).toBe(defaultProvide.codesProfileTwoFactorAuthPath);
expect(methodInput.attributes('value')).toBe(defaultProvide.codesProfileTwoFactorAuthMethod);
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(submitSpy).toHaveBeenCalled();
});
});
});
});

View File

@ -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

View File

@ -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

View File

@ -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