Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-01-02 21:12:50 +00:00
parent 0cea0a8f44
commit 3a72ac7750
41 changed files with 454 additions and 249 deletions

View File

@ -64,6 +64,7 @@ start-as-if-foss:
ENABLE_RSPEC_MIGRATION: $ENABLE_RSPEC_MIGRATION
ENABLE_RSPEC_BACKGROUND_MIGRATION: $ENABLE_RSPEC_BACKGROUND_MIGRATION
ENABLE_RSPEC_SYSTEM: $ENABLE_RSPEC_SYSTEM
ENABLE_JEST: $ENABLE_JEST
trigger:
project: gitlab-org/gitlab-foss
branch: as-if-foss/${CI_COMMIT_REF_NAME}

View File

@ -21,7 +21,7 @@
if: '$FORCE_GITLAB_CI'
.if-default-refs: &if-default-refs
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_COMMIT_REF_NAME =~ "^ruby\d+(_\d)*$" || ($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "detached") || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_COMMIT_REF_NAME =~ /^ruby\d+(_\d)*$/ || ($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "detached") || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
.if-default-branch-refs: &if-default-branch-refs
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_MERGE_REQUEST_IID == null'
@ -1250,6 +1250,7 @@
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$ENABLE_JEST == "true"'
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-default-refs
@ -1300,6 +1301,7 @@
when: never
- <<: *if-fork-merge-request
when: never
- if: '$ENABLE_JEST == "true"'
- <<: *if-merge-request-labels-run-all-jest
- <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-merge-request

View File

@ -358,7 +358,7 @@ Layout/SpaceInLambdaLiteral:
- 'lib/gitlab/metrics/exporter/base_exporter.rb'
- 'lib/gitlab/visibility_level.rb'
- 'spec/deprecation_toolkit_env.rb'
- 'spec/features/admin/users/user_spec.rb'
- 'spec/features/admin/users/admin_sees_unconfirmed_user_spec.rb'
- 'spec/helpers/namespaces_helper_spec.rb'
- 'spec/lib/backup/gitaly_backup_spec.rb'
- 'spec/lib/container_registry/client_spec.rb'

View File

@ -800,7 +800,7 @@ RSpec/BeforeAllRoleAssignment:
- 'spec/controllers/projects_controller_spec.rb'
- 'spec/controllers/repositories/lfs_storage_controller_spec.rb'
- 'spec/features/admin/admin_projects_spec.rb'
- 'spec/features/admin/users/user_spec.rb'
- 'spec/features/admin/users/admin_sees_user_spec.rb'
- 'spec/features/admin/users/users_spec.rb'
- 'spec/features/boards/board_filters_spec.rb'
- 'spec/features/boards/new_issue_spec.rb'

View File

@ -1040,8 +1040,7 @@ RSpec/ContextWording:
- 'spec/features/admin/admin_mode_spec.rb'
- 'spec/features/admin/admin_settings_spec.rb'
- 'spec/features/admin/dashboard_spec.rb'
- 'spec/features/admin/users/user_impersonation_spec.rb'
- 'spec/features/admin/users/user_spec.rb'
- 'spec/features/admin/users/admin_impersonates_user_spec.rb'
- 'spec/features/admin/users/users_spec.rb'
- 'spec/features/atom/dashboard_issues_spec.rb'
- 'spec/features/atom/dashboard_spec.rb'

View File

@ -1414,8 +1414,7 @@ RSpec/NamedSubject:
- 'spec/controllers/users/callouts_controller_spec.rb'
- 'spec/experiments/application_experiment_spec.rb'
- 'spec/experiments/in_product_guidance_environments_webide_experiment_spec.rb'
- 'spec/features/admin/users/user_impersonation_spec.rb'
- 'spec/features/admin/users/user_spec.rb'
- 'spec/features/admin/users/admin_impersonates_user_spec.rb'
- 'spec/features/groups/clusters/user_spec.rb'
- 'spec/features/merge_request/user_sees_merge_widget_spec.rb'
- 'spec/features/user_settings/password_spec.rb'

View File

@ -449,6 +449,7 @@ export default {
class="gl-mt-3 gl-sm-display-block!"
:is-editing="editMode"
:title="workItem.title"
@updateWorkItem="updateWorkItem"
@updateDraft="updateDraft('title', $event)"
/>
<work-item-title
@ -518,6 +519,7 @@ export default {
:is-editing="editMode"
:class="titleClassComponent"
:title="workItem.title"
@updateWorkItem="updateWorkItem"
@updateDraft="updateDraft('title', $event)"
/>
<work-item-title

View File

@ -27,10 +27,12 @@ export default {
<template>
<gl-form-group v-if="isEditing" :label="$options.i18n.titleLabel" label-for="work-item-title">
<gl-form-input
id="work-item-title"
class="gl-w-full"
:value="title"
@change="$emit('updateDraft', $event)"
data-testid="work-item-title-with-edit"
@keydown.meta.enter="$emit('updateWorkItem')"
@keydown.ctrl.enter="$emit('updateWorkItem')"
@input="$emit('updateDraft', $event)"
/>
</gl-form-group>
<h1

View File

@ -28,7 +28,7 @@ class Ldap::OmniauthCallbacksController < OmniauthCallbacksController
define_providers!
override :set_remember_me
def set_remember_me(user)
def set_remember_me(user, _auth_user)
user.remember_me = params[:remember_me] if user.persisted?
end

View File

@ -139,9 +139,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
link_identity(identity_linker)
set_remember_me(current_user)
store_idp_two_factor_status(build_auth_user(auth_module::User).bypass_two_factor?)
current_auth_user = build_auth_user(auth_module::User)
set_remember_me(current_user, current_auth_user)
store_idp_two_factor_status(current_auth_user.bypass_two_factor?)
if identity_linker.changed?
redirect_identity_linked
@ -193,7 +195,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
track_event(@user, oauth['provider'], 'succeeded')
Gitlab::Tracking.event(self.class.name, "#{oauth['provider']}_sso", user: @user) if new_user
set_remember_me(@user)
set_remember_me(@user, auth_user)
set_session_active_since(oauth['provider']) if ::AuthHelper.saml_providers.include?(oauth['provider'].to_sym)
if @user.two_factor_enabled? && !auth_user.bypass_two_factor?
@ -278,10 +280,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
.for_authentication.security_event
end
def set_remember_me(user)
def set_remember_me(user, auth_user)
return unless remember_me?
if user.two_factor_enabled?
if user.two_factor_enabled? && !auth_user.bypass_two_factor?
params[:remember_me] = '1'
else
remember_me(user)

View File

@ -1712,8 +1712,6 @@ class MergeRequest < ApplicationRecord
actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test))
end
# rubocop: disable Metrics/AbcSize
# Delete a rubocop annotation once FF truncate_ci_merge_request_description is cleaned up
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_MERGE_REQUEST_ID', value: id.to_s)
@ -1726,14 +1724,9 @@ class MergeRequest < ApplicationRecord
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED', value: ProtectedBranch.protected?(target_project, target_branch).to_s)
variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title)
if ::Feature.enabled?(:truncate_ci_merge_request_description)
mr_description, mr_description_truncated = truncate_mr_description
variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION', value: mr_description)
variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED', value: mr_description_truncated)
else
variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION', value: description)
end
mr_description, mr_description_truncated = truncate_mr_description
variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION', value: mr_description)
variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED', value: mr_description_truncated)
variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee_username_list) if assignees.present?
variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone
variables.append(key: 'CI_MERGE_REQUEST_LABELS', value: label_names.join(',')) if labels.present?
@ -1741,8 +1734,6 @@ class MergeRequest < ApplicationRecord
variables.concat(source_project_variables)
end
end
# rubocop: enable Metrics/AbcSize
# Delete a rubocop annotation once FF truncate_ci_merge_request_description is cleaned up
def compare_test_reports
unless has_test_reports?

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module Organizations
class Organization < ApplicationRecord
class Organization < MainClusterwide::ApplicationRecord
DEFAULT_ORGANIZATION_ID = 1
scope :without_default, -> { where.not(id: DEFAULT_ORGANIZATION_ID) }

View File

@ -4,14 +4,14 @@
.omniauth-divider.gl-display-flex.gl-align-items-center
= _("or sign in with")
.gl-mt-5.gl-px-5.omniauth-container.gl-text-center.gl-ml-auto.gl-mr-auto
.gl-mt-5.gl-px-5.omniauth-container.gl-text-center.gl-display-flex.gl-flex-direction-column.gl-gap-3
- enabled_button_based_providers.each do |provider|
- has_icon = provider_has_icon?(provider)
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", data: { testid: "#{test_id_for_provider(provider)}" }, class: "btn gl-button btn-default gl-mb-2 js-oauth-login gl-w-full", form: { class: 'gl-mb-3' } do
- if has_icon
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)
= render 'devise/shared/omniauth_provider_button',
href: omniauth_authorize_path(:user, provider),
provider: provider,
classes: 'js-oauth-login',
data: { testid: test_id_for_provider(provider) },
id: "oauth-login-#{provider}"
- if render_remember_me
= render Pajamas::CheckboxTagComponent.new(name: 'remember_me_omniauth', value: nil) do |c|
- c.with_label do

View File

@ -0,0 +1,7 @@
- button_options = { class: classes, data: data, id: id }
= render Pajamas::ButtonComponent.new(href: href, method: :post, form: true, block: true, button_options: button_options) do
- if provider_has_icon?(provider)
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)

View File

@ -1,8 +1,6 @@
- data = { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }
- button_options = { class: 'js-track-omni-auth', data: data, id: "oauth-login-#{provider}" }
= render Pajamas::ButtonComponent.new(href: href, method: :post, form: true, block: true, button_options: button_options) do
- if provider_has_icon?(provider)
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)
= render 'devise/shared/omniauth_provider_button',
href: href,
provider: provider,
classes: 'js-track-omni-auth',
data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label },
id: "oauth-login-#{provider}"

View File

@ -24,7 +24,7 @@
= sprite_icon('mail', css_class: 'gl-mr-2')
= @emails.load.size
.gl-new-card-actions
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content", data: { testid: 'toggle_email_address_field' } }) do
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content", data: { testid: 'toggle-email-address-field' } }) do
= s_('Profiles|Add new email')
- c.with_body do
.gl-new-card-add-form.gl-m-3.gl-mb-4.gl-display-none.js-toggle-content
@ -33,9 +33,9 @@
= gitlab_ui_form_for 'email', url: profile_emails_path do |f|
.form-group
= f.label :email, s_('Profiles|Email address'), class: 'label-bold'
= f.text_field :email, class: 'form-control gl-form-input gl-form-input-xl', data: { qa_selector: 'email_address_field' }
= f.text_field :email, class: 'form-control gl-form-input gl-form-input-xl', data: { testid: 'email-address-field' }
.gl-mt-3
= f.submit s_('Profiles|Add email address'), data: { qa_selector: 'add_email_address_button' }, pajamas_button: true
= f.submit s_('Profiles|Add email address'), data: { testid: 'add-email-address-button' }, pajamas_button: true
= render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do
= _('Cancel')
- if @emails.any?
@ -59,7 +59,7 @@
= s_('Profiles|Default notification email')
.gl-text-secondary.gl-font-sm= notification_message.html_safe
- @emails.reject(&:user_primary_email?).each do |email|
%li{ class: 'gl-px-5!', data: { qa_selector: 'email_row_content' } }
%li{ class: 'gl-px-5!', data: { testid: 'email-row-content' } }
.gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-gap-3
%div
= render partial: 'shared/email_with_badge', locals: { email: email.email, verified: email.confirmed? }
@ -81,4 +81,4 @@
- confirm_title = "#{email.confirmation_sent_at ? s_('Profiles|Resend confirmation email') : s_('Profiles|Send confirmation email')}"
= link_button_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, size: :small
= link_button_to nil, profile_email_path(email), data: { confirm: _('Are you sure?'), confirm_btn_variant: 'danger', qa_selector: 'delete_email_link'}, method: :delete, size: :small, icon: 'remove', 'aria-label': _('Remove')
= link_button_to nil, profile_email_path(email), data: { confirm: _('Are you sure?'), confirm_btn_variant: 'danger', testid: 'delete-email-link'}, method: :delete, size: :small, icon: 'remove', 'aria-label': _('Remove')

View File

@ -3,7 +3,7 @@
- if @gpg_keys.any?
.table-holder
%table.table.b-table.gl-table.b-table-stacked-md.gl-mt-n1.gl-mb-n2.ssh-keys-list{ data: { qa_selector: 'ssh_keys_list' } }
%table.table.b-table.gl-table.b-table-stacked-md.gl-mt-n1.gl-mb-n2.ssh-keys-list
%thead.d-none.d-md-table-header-group
%tr
%th= s_('Profiles|Key')

View File

@ -18,18 +18,18 @@
- unless @user.password_automatically_set?
.form-group
= f.label :password, _('Current password'), class: 'label-bold'
= f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input gl-max-w-80', data: { qa_selector: 'current_password_field' }
= f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input gl-max-w-80', data: { testid: 'current-password-field' }
%p.form-text.text-muted
= _('You must provide your current password in order to change it.')
.form-group
= f.label :new_password, _('New password'), class: 'label-bold'
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation gl-max-w-80', data: { qa_selector: 'new_password_field' }
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation gl-max-w-80', data: { testid: 'new-password-field' }
= render_if_exists 'shared/password_requirements_list'
.form-group
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input gl-max-w-80', data: { qa_selector: 'confirm_password_field' }
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input gl-max-w-80', data: { testid: 'confirm-password-field' }
.gl-mt-3.gl-mb-3
= f.submit _('Save password'), class: "gl-mr-3", data: { qa_selector: 'save_password_button' }, pajamas_button: true
= f.submit _('Save password'), class: "gl-mr-3", data: { testid: 'save-password-button' }, pajamas_button: true
- unless @user.password_automatically_set?
= render Pajamas::ButtonComponent.new(href: reset_user_settings_password_path, variant: :link, method: :put) do
= _('I forgot my password')

View File

@ -16,17 +16,17 @@
.col-sm-2.col-form-label
= f.label :password, _('Current password')
.col-sm-10
= f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
= f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input', data: { testid: 'current-password-field' }
.form-group.row
.col-sm-2.col-form-label
= f.label :new_password, _('New password')
.col-sm-10
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation', data: { qa_selector: 'new_password_field' }
= f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input js-password-complexity-validation', data: { testid: 'new-password-field' }
= render_if_exists 'shared/password_requirements_list'
.form-group.row
.col-sm-2.col-form-label
= f.label :password_confirmation, _('Password confirmation')
.col-sm-10
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
= f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { testid: 'confirm-password-field' }
.form-actions
= f.submit _('Set new password'), data: { qa_selector: 'set_new_password_button' }, pajamas_button: true
= f.submit _('Set new password'), data: { testid: 'set-new-password-button' }, pajamas_button: true

View File

@ -1,8 +0,0 @@
---
name: truncate_ci_merge_request_description
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139605
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435099
milestone: '16.7'
type: development
group: group::pipeline security
default_enabled: false

View File

@ -5,5 +5,5 @@ feature_category: web_ide
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138355
milestone: '16.7'
queued_migration_version: 20231130140901
finalize_after: '2023-12-17'
finalize_after: '2023-01-31'
finalized_by: # version of the migration that finalized this BBM

View File

@ -24256,8 +24256,8 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectcicdsettings"></a>`ciCdSettings` | [`ProjectCiCdSetting`](#projectcicdsetting) | CI/CD settings for the project. |
| <a id="projectciconfigpathordefault"></a>`ciConfigPathOrDefault` | [`String!`](#string) | Path of the CI configuration file. |
| <a id="projectcijobtokenscope"></a>`ciJobTokenScope` | [`CiJobTokenScopeType`](#cijobtokenscopetype) | The CI Job Tokens scope of access. |
| <a id="projectcisubscribedprojects"></a>`ciSubscribedProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Triggers a new pipeline in the downstream project when a pipeline successfullycompletes on the(upstream) project. (see [Connections](#connections)) |
| <a id="projectcisubscriptionsprojects"></a>`ciSubscriptionsProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Triggers a new pipeline in the(downstream) project when a pipeline successfullycompletes on the upstream project. (see [Connections](#connections)) |
| <a id="projectcisubscribedprojects"></a>`ciSubscribedProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Pipeline subscriptions for projects subscribed to the project. (see [Connections](#connections)) |
| <a id="projectcisubscriptionsprojects"></a>`ciSubscriptionsProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Pipeline subscriptions for the project. (see [Connections](#connections)) |
| <a id="projectcodecoveragesummary"></a>`codeCoverageSummary` | [`CodeCoverageSummary`](#codecoveragesummary) | Code coverage summary associated with the project. |
| <a id="projectcomplianceframeworks"></a>`complianceFrameworks` | [`ComplianceFrameworkConnection`](#complianceframeworkconnection) | Compliance frameworks associated with the project. (see [Connections](#connections)) |
| <a id="projectcontainerexpirationpolicy"></a>`containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. |

View File

@ -165,7 +165,8 @@ These variables are available when:
| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff. |
| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request. Can be `detached`, `merged_result` or `merge_train`. |
| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on the GitLab instance. |
| `CI_MERGE_REQUEST_DESCRIPTION` | 16.7 | all | The description of the merge request. |
| `CI_MERGE_REQUEST_DESCRIPTION` | 16.7 | all | The description of the merge request. If the description is more than 2700 characters long, only the first 2700 characters are stored in the variable. |
| `CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED` | 16.8 | all | `true` if `CI_MERGE_REQUEST_DESCRIPTION` is truncated down to 2700 characters because the description of the merge request is too long. |
| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project, and is the number used in the merge request URL, page title, and other visible locations. |
| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. |
| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. |

View File

@ -50,7 +50,7 @@ To create a requirement:
1. In a project, go to **Plan > Requirements**.
1. Select **New requirement**.
1. Enter a title and description and select **Create requirement**.
1. Enter a title and description and select **New requirement**.
![requirement create view](img/requirement_create_v13_5.png)
@ -240,7 +240,8 @@ To import requirements:
1. In a project, go to **Plan > Requirements**.
- For a project with requirements, in the
upper-right corner, select the import icon (**{import}**).
upper-right corner, select the vertical ellipsis (**{ellipsis_v}**),
then select **Import requirements** (**{import}**).
- For a project without requirements, in the middle of the page, select **Import CSV**.
1. Select the file and select **Import requirements**.
@ -300,7 +301,8 @@ Prerequisites:
To export requirements:
1. In a project, go to **Plan > Requirements**.
1. In the upper-right corner, select **Export as CSV** (**{export}**).
1. In the upper-right corner, select the vertical ellipsis (**{ellipsis_v}**),
then select **Export as CSV** (**{export}**).
A confirmation modal appears.

View File

@ -1,5 +1,5 @@
variables:
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.72.0'
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.76.0'
.dast-auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.72.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.76.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.72.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.76.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -5,7 +5,7 @@ ENV GITLAB_LICENSE_MODE=test \
# Clone GDK at specific sha and bootstrap packages
#
ARG GDK_SHA=221d3cc81d3ffc17beb178fd5021cd0d93ca55b6
ARG GDK_SHA=88c3231079278a49ba59d37362357e5908fb7179
RUN set -eux; \
git clone --depth 1 https://gitlab.com/gitlab-org/gitlab-development-kit.git && cd gitlab-development-kit; \
git fetch --depth 1 origin ${GDK_SHA} && git -c advice.detachedHead=false checkout ${GDK_SHA}; \

View File

@ -7,27 +7,27 @@ module QA
include QA::Page::Component::ConfirmModal
view 'app/views/profiles/emails/index.html.haml' do
element :email_address_field
element :add_email_address_button
element :email_row_content
element :delete_email_link
element :toggle_email_address_field
element 'email-address-field'
element 'add-email-address-button'
element 'email-row-content'
element 'delete-email-link'
element 'toggle-email-address-field'
end
def expand_email_input
click_element(:toggle_email_address_field) if has_no_element?(:email_address_field)
has_element?(:email_address_field)
click_element('toggle-email-address-field') if has_no_element?('email-address-field')
has_element?('email-address-field')
end
def add_email_address(email_address)
expand_email_input
find_element(:email_address_field).set email_address
click_element(:add_email_address_button)
find_element('email-address-field').set email_address
click_element('add-email-address-button')
end
def delete_email_address(email_address)
within_element(:email_row_content, text: email_address) do
click_element(:delete_email_link)
within_element('email-row-content', text: email_address) do
click_element('delete-email-link')
end
click_confirmation_ok_button
end

View File

@ -5,31 +5,31 @@ module QA
module Profile
class Password < Page::Base
view 'app/views/user_settings/passwords/edit.html.haml' do
element :current_password_field
element :new_password_field
element :confirm_password_field
element :save_password_button
element 'current-password-field'
element 'new-password-field'
element 'confirm-password-field'
element 'save-password-button'
end
view 'app/views/user_settings/passwords/new.html.haml' do
element :current_password_field
element :new_password_field
element :confirm_password_field
element :set_new_password_button
element 'current-password-field'
element 'new-password-field'
element 'confirm-password-field'
element 'set-new-password-button'
end
def update_password(new_password, current_password)
find_element(:current_password_field).set current_password
find_element(:new_password_field).set new_password
find_element(:confirm_password_field).set new_password
click_element(:save_password_button)
find_element('current-password-field').set current_password
find_element('new-password-field').set new_password
find_element('confirm-password-field').set new_password
click_element('save-password-button')
end
def set_new_password(new_password, current_password)
fill_element :current_password_field, current_password
fill_element :new_password_field, new_password
fill_element :confirm_password_field, new_password
click_element :set_new_password_button
fill_element('current-password-field', current_password)
fill_element('new-password-field', new_password)
fill_element('confirm-password-field', new_password)
click_element('set-new-password-button')
end
end
end

View File

@ -1,22 +1,79 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'gitlab'
# In spec/scripts/setup/generate_as_if_foss_env_spec.rb we completely stub it
require 'gitlab' unless Object.const_defined?(:Gitlab)
require 'set'
client = Gitlab.client(endpoint: ENV['CI_API_V4_URL'], private_token: '')
class GenerateAsIfFossEnv
def initialize
@client = Gitlab.client(endpoint: ENV['CI_API_V4_URL'], private_token: '')
@rspec_jobs = Set.new
@jest_jobs = Set.new
end
rspec_jobs = Set.new
def variables
@variables ||= generate_variables
end
client.pipeline_jobs(ENV['CI_PROJECT_ID'], ENV['CI_PIPELINE_ID']).auto_paginate do |job|
rspec_type = job.name[/^rspec ([\w\-]+)/, 1]
def display
variables.each do |key, value|
puts "#{key}=#{value}"
end
end
rspec_jobs << rspec_type if rspec_type
private
attr_reader :client, :rspec_jobs, :jest_jobs
def generate_variables
scan_jobs
{
START_AS_IF_FOSS: 'true',
RUBY_VERSION: ENV['RUBY_VERSION']
}.merge(rspec_variables).merge(jest_variables)
end
def scan_jobs
each_job do |job|
detect_rspec(job) || detect_jest(job)
end
end
def each_job
client.pipeline_jobs(ENV['CI_PROJECT_ID'], ENV['CI_PIPELINE_ID']).auto_paginate do |job|
yield(job)
end
end
def detect_rspec(job)
rspec_type = job.name[/^rspec ([\w\-]+)/, 1]
rspec_jobs << rspec_type if rspec_type
end
def detect_jest(job)
jest_type = job.name[/^jest([\w\-]*)/, 1]
jest_jobs << jest_type if jest_type
end
def rspec_variables
return {} if rspec_jobs.empty?
rspec_jobs.inject({ ENABLE_RSPEC: 'true' }) do |result, rspec|
result.merge("ENABLE_RSPEC_#{rspec.upcase.tr('-', '_')}": 'true')
end
end
def jest_variables
return {} if jest_jobs.empty?
jest_jobs.inject({ ENABLE_JEST: 'true' }) do |result, jest|
result.merge("ENABLE_JEST#{jest.upcase.tr('-', '_')}": 'true')
end
end
end
puts 'START_AS_IF_FOSS=true', "RUBY_VERSION=#{ENV['RUBY_VERSION']}"
puts 'ENABLE_RSPEC=true' if rspec_jobs.any?
rspec_jobs.each do |rspec|
puts "ENABLE_RSPEC_#{rspec.upcase.tr('-', '_')}=true"
end
GenerateAsIfFossEnv.new.display if $PROGRAM_NAME == __FILE__

View File

@ -31,6 +31,67 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
end
end
shared_examples 'omniauth sign in that remembers user' do
before do
stub_omniauth_setting(allow_bypass_two_factor: allow_bypass_two_factor)
(request.env['omniauth.params'] ||= {}).deep_merge!('remember_me' => omniauth_params_remember_me)
end
if params[:call_remember_me]
it 'calls devise method remember_me' do
expect(controller).to receive(:remember_me).with(user).and_call_original
post_action
end
else
it 'does not calls devise method remember_me' do
expect(controller).not_to receive(:remember_me)
post_action
end
end
end
shared_examples 'omniauth sign in that remembers user with two factor enabled' do
using RSpec::Parameterized::TableSyntax
subject(:post_action) { post provider }
where(:allow_bypass_two_factor, :omniauth_params_remember_me, :call_remember_me) do
true | '1' | true
true | '0' | false
true | nil | false
false | '1' | false
false | '0' | false
false | nil | false
end
with_them do
it_behaves_like 'omniauth sign in that remembers user'
end
end
shared_examples 'omniauth sign in that remembers user with two factor disabled' do
context "when user selects remember me for omniauth sign in flow" do
using RSpec::Parameterized::TableSyntax
subject(:post_action) { post provider }
where(:allow_bypass_two_factor, :omniauth_params_remember_me, :call_remember_me) do
true | '1' | true
true | '0' | false
true | nil | false
false | '1' | true
false | '0' | false
false | nil | false
end
with_them do
it_behaves_like 'omniauth sign in that remembers user'
end
end
end
describe 'omniauth' do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
let(:additional_info) { {} }
@ -190,6 +251,8 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
request.env['omniauth.params'] = { 'redirect_fragment' => 'L101' }
end
it_behaves_like 'omniauth sign in that remembers user with two factor disabled'
context 'when a redirect url is stored' do
it 'redirects with fragment' do
post provider, session: { user_return_to: '/fake/url' }
@ -214,6 +277,12 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
expect(response.location).not_to include('#L101')
end
end
context 'when a user has 2FA enabled' do
let(:user) { create(:omniauth_user, :two_factor, extern_uid: extern_uid, provider: provider) }
it_behaves_like 'omniauth sign in that remembers user with two factor enabled'
end
end
context 'with strategies' do
@ -271,6 +340,8 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
end
end
it_behaves_like 'omniauth sign in that remembers user with two factor disabled'
context 'when a user has 2FA enabled' do
render_views
@ -296,6 +367,8 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
expect(response).to have_gitlab_http_status(:ok)
end
end
it_behaves_like 'omniauth sign in that remembers user with two factor enabled'
end
context 'for sign up' do
@ -357,6 +430,10 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
let(:extern_uid) { '' }
let(:provider) { :auth0 }
it_behaves_like 'omniauth sign in that remembers user with two factor disabled' do
let(:extern_uid) { 'my-uid' }
end
it 'does not allow sign in without extern_uid' do
post 'auth0'
@ -364,6 +441,14 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
expect(response).to have_gitlab_http_status(:found)
expect(controller).to set_flash[:alert].to('Wrong extern UID provided. Make sure Auth0 is configured correctly.')
end
context 'when a user has 2FA enabled' do
let(:user) { create(:omniauth_user, :two_factor, extern_uid: extern_uid, provider: provider) }
it_behaves_like 'omniauth sign in that remembers user with two factor enabled' do
let(:extern_uid) { 'my-uid' }
end
end
end
context 'for atlassian_oauth2' do
@ -373,6 +458,8 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
context 'when the user and identity already exist' do
let(:user) { create(:atlassian_user, extern_uid: extern_uid) }
it_behaves_like 'omniauth sign in that remembers user with two factor disabled'
it 'allows sign-in' do
post :atlassian_oauth2
@ -391,6 +478,12 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
post :atlassian_oauth2
end
context 'when a user has 2FA enabled' do
let(:user) { create(:atlassian_user, :two_factor, extern_uid: extern_uid) }
it_behaves_like 'omniauth sign in that remembers user with two factor enabled'
end
end
context 'for a new user' do
@ -443,11 +536,21 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
include_context 'with sign_up'
let(:additional_info) { { extra: { email_verified: true } } }
it_behaves_like 'omniauth sign in that remembers user with two factor disabled' do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
end
it 'allows sign in' do
post 'salesforce'
expect(request.env['warden']).to be_authenticated
end
context 'when a user has 2FA enabled' do
let(:user) { create(:omniauth_user, :two_factor, extern_uid: extern_uid, provider: provider) }
it_behaves_like 'omniauth sign in that remembers user with two factor enabled'
end
end
end
end
@ -497,11 +600,19 @@ RSpec.describe OmniauthCallbacksController, type: :controller, feature_category:
let(:post_action) { post provider }
end
it_behaves_like 'omniauth sign in that remembers user with two factor disabled'
it 'allows sign in' do
post provider
expect(request.env['warden']).to be_authenticated
end
context 'when a user has 2FA enabled' do
let(:user) { create(:omniauth_user, :two_factor, extern_uid: extern_uid, provider: provider) }
it_behaves_like 'omniauth sign in that remembers user with two factor enabled'
end
end
describe '#saml' do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Admin::Users::UserImpersonation', feature_category: :user_management do
RSpec.describe 'Admin impersonates user', feature_category: :user_management do
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:current_user) { create(:admin) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Admin::Users::UserIdentities', feature_category: :user_management do
RSpec.describe 'Admin manages user identities', feature_category: :user_management do
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:current_user) { create(:admin) }

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Admin sees unconfirmed user', feature_category: :user_management do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
enable_admin_mode!(current_user, use_ui: true)
end
context 'when user has an unconfirmed email', :js do
# Email address contains HTML to ensure email address is displayed in an HTML safe way.
let_it_be(:unconfirmed_email) { "#{generate(:email)}<h2>testing<img/src=http://localhost:8000/test.png>" }
let_it_be(:unconfirmed_user) { create(:user, :unconfirmed, unconfirmed_email: unconfirmed_email) }
where(:path_helper) do
[
[-> (user) { admin_user_path(user) }],
[-> (user) { projects_admin_user_path(user) }],
[-> (user) { keys_admin_user_path(user) }],
[-> (user) { admin_user_identities_path(user) }],
[-> (user) { admin_user_impersonation_tokens_path(user) }]
]
end
with_them do
it "allows an admin to force confirmation of the user's email", :aggregate_failures do
visit path_helper.call(unconfirmed_user)
click_button 'Confirm user'
within_modal do
expect(page).to have_content("Confirm user #{unconfirmed_user.name}?")
expect(page).to(
have_content(
"This user has an unconfirmed email address (#{unconfirmed_email}). You may force a confirmation.")
)
click_button 'Confirm user'
end
expect(page).to have_content('Successfully confirmed')
expect(page).not_to have_button('Confirm user')
end
end
end
end

View File

@ -193,34 +193,6 @@ RSpec.describe 'Admin::Users::User', feature_category: :user_management do
end
end
describe 'show user identities' do
it 'shows user identities', :aggregate_failures do
visit admin_user_identities_path(user)
expect(page).to have_content(user.name)
expect(page).to have_content('twitter')
end
end
describe 'update user identities' do
before do
allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
end
it 'modifies twitter identity', :aggregate_failures do
visit admin_user_identities_path(user)
find('.table').find(:link, 'Edit').click
fill_in 'identity_extern_uid', with: '654321'
select 'twitter_updated', from: 'identity_provider'
click_button 'Save changes'
expect(page).to have_content(user.name)
expect(page).to have_content('twitter_updated')
expect(page).to have_content('654321')
end
end
describe 'remove users secondary email', :js do
let_it_be(:secondary_email) do
create :email, email: 'secondary@example.com', user: user
@ -237,17 +209,6 @@ RSpec.describe 'Admin::Users::User', feature_category: :user_management do
end
end
describe 'remove user with identities' do
it 'removes user with twitter identity', :aggregate_failures do
visit admin_user_identities_path(user)
click_link 'Delete'
expect(page).to have_content(user.name)
expect(page).not_to have_content('twitter')
end
end
describe 'show user keys', :js do
it do
key1 = create(:key, user: user, title: 'ssh-rsa Key1')
@ -284,40 +245,4 @@ RSpec.describe 'Admin::Users::User', feature_category: :user_management do
end
end
end
context 'when user has an unconfirmed email', :js do
# Email address contains HTML to ensure email address is displayed in an HTML safe way.
let_it_be(:unconfirmed_email) { "#{generate(:email)}<h2>testing<img/src=http://localhost:8000/test.png>" }
let_it_be(:unconfirmed_user) { create(:user, :unconfirmed, unconfirmed_email: unconfirmed_email) }
where(:path_helper) do
[
[-> (user) { admin_user_path(user) }],
[-> (user) { projects_admin_user_path(user) }],
[-> (user) { keys_admin_user_path(user) }],
[-> (user) { admin_user_identities_path(user) }],
[-> (user) { admin_user_impersonation_tokens_path(user) }]
]
end
with_them do
it "allows an admin to force confirmation of the user's email", :aggregate_failures do
visit path_helper.call(unconfirmed_user)
click_button 'Confirm user'
within_modal do
expect(page).to have_content("Confirm user #{unconfirmed_user.name}?")
expect(page).to have_content(
"This user has an unconfirmed email address (#{unconfirmed_email}). You may force a confirmation."
)
click_button 'Confirm user'
end
expect(page).to have_content('Successfully confirmed')
expect(page).not_to have_button('Confirm user')
end
end
end
end

View File

@ -118,7 +118,7 @@ RSpec.describe 'Projects > Members > Manage members', :js, feature_category: :on
context 'when maintainer' do
let(:current_user) { project_maintainer }
it 'does not show the Owner option' do
it 'does not show the Owner option', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/436958' do
within_modal do
toggle_listbox
expect_listbox_items(%w[Guest Reporter Developer Maintainer])

View File

@ -51,7 +51,7 @@ describe('Work Item title with edit', () => {
});
it('emits `updateDraft` event on change of the input', () => {
findEditableTitleInput().vm.$emit('change', 'updated title');
findEditableTitleInput().vm.$emit('input', 'updated title');
expect(wrapper.emitted('updateDraft')).toEqual([['updated title']]);
});

View File

@ -152,51 +152,6 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Pipeline, feature_category: :secr
end
end
context 'when truncate_ci_merge_request_description feature flag is disabled' do
before do
stub_feature_flags(truncate_ci_merge_request_description: false)
end
context 'when merge request description hits the limit' do
let(:merge_request_description) { 'a' * (MergeRequest::CI_MERGE_REQUEST_DESCRIPTION_MAX_LENGTH + 1) }
it 'does not truncate the exposed description' do
expect(subject.to_hash)
.to include(
'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description
)
expect(subject.to_hash)
.not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED')
end
end
context 'when merge request description fits the length limit' do
let(:merge_request_description) { 'a' * (MergeRequest::CI_MERGE_REQUEST_DESCRIPTION_MAX_LENGTH - 1) }
it 'does not truncate the exposed description' do
expect(subject.to_hash)
.to include(
'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description
)
expect(subject.to_hash)
.not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED')
end
end
context 'when merge request description does not exist' do
let(:merge_request_description) { nil }
it 'does not truncate the exposed description' do
expect(subject.to_hash)
.to include(
'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description
)
expect(subject.to_hash)
.not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED')
end
end
end
it 'exposes diff variables' do
expect(subject.to_hash)
.to include(

View File

@ -0,0 +1,84 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'gitlab/rspec/stub_env'
# NOTE: Under the context of fast_spec_helper, when we `require 'gitlab'`
# we do not load the Gitlab client, but our own Gitlab module.
# Keep this in mind and just stub anything which might touch it!
require_relative '../../../scripts/setup/generate-as-if-foss-env'
RSpec.describe GenerateAsIfFossEnv, feature_category: :tooling do
include StubENV
subject(:generate) { described_class.new }
before do
stub_env(RUBY_VERSION: '3.1')
end
shared_context 'when there are all jobs' do
let(:jobs) do
[
'rspec fast_spec_helper',
'rspec unit',
'rspec integration',
'rspec system',
'rspec migration',
'rspec background-migration',
'jest',
'jest-integration'
]
end
before do
messages = receive_message_chain(:client, :pipeline_jobs, :auto_paginate)
yield_jobs = jobs.inject(messages) do |stub, job|
stub.and_yield(double(name: job)) # rubocop:disable RSpec/VerifiedDoubles -- As explained at the top of this file, we do not load the Gitlab client
end
allow(Gitlab).to yield_jobs
end
end
describe '#variables' do
include_context 'when there are all jobs'
it 'returns correct variables' do
expect(generate.variables).to eq({
START_AS_IF_FOSS: 'true',
RUBY_VERSION: ENV['RUBY_VERSION'],
ENABLE_RSPEC: 'true',
ENABLE_RSPEC_FAST_SPEC_HELPER: 'true',
ENABLE_RSPEC_UNIT: 'true',
ENABLE_RSPEC_INTEGRATION: 'true',
ENABLE_RSPEC_SYSTEM: 'true',
ENABLE_RSPEC_MIGRATION: 'true',
ENABLE_RSPEC_BACKGROUND_MIGRATION: 'true',
ENABLE_JEST: 'true',
ENABLE_JEST_INTEGRATION: 'true'
})
end
end
describe '#display' do
include_context 'when there are all jobs'
it 'puts correct variables' do
expect { generate.display }.to output(<<~ENV).to_stdout
START_AS_IF_FOSS=true
RUBY_VERSION=#{ENV['RUBY_VERSION']}
ENABLE_RSPEC=true
ENABLE_RSPEC_FAST_SPEC_HELPER=true
ENABLE_RSPEC_UNIT=true
ENABLE_RSPEC_INTEGRATION=true
ENABLE_RSPEC_SYSTEM=true
ENABLE_RSPEC_MIGRATION=true
ENABLE_RSPEC_BACKGROUND_MIGRATION=true
ENABLE_JEST=true
ENABLE_JEST_INTEGRATION=true
ENV
end
end
end

View File

@ -2,22 +2,45 @@
RSpec.shared_examples 'work items title' do
let(:title_selector) { '[data-testid="work-item-title"]' }
let(:title_with_edit_selector) { '[data-testid="work-item-title-with-edit"]' }
before do
stub_feature_flags(work_items_mvc_2: false)
context 'when the work_items_mvc_2 FF is disabled' do
before do
stub_feature_flags(work_items_mvc_2: false)
page.refresh
wait_for_all_requests
page.refresh
wait_for_all_requests
end
it 'successfully shows and changes the title of the work item' do
expect(work_item.reload.title).to eq work_item.title
find(title_selector).set("Work item title")
find(title_selector).native.send_keys(:return)
wait_for_requests
expect(work_item.reload.title).to eq 'Work item title'
end
end
it 'successfully shows and changes the title of the work item' do
expect(work_item.reload.title).to eq work_item.title
context 'when the work_items_mvc_2 FF is enabled' do
before do
stub_feature_flags(work_items_mvc_2: true)
find(title_selector).set("Work item title")
find(title_selector).native.send_keys(:return)
wait_for_requests
page.refresh
wait_for_all_requests
end
expect(work_item.reload.title).to eq 'Work item title'
it 'successfully shows and changes the title of the work item' do
expect(work_item.reload.title).to eq work_item.title
click_button 'Edit'
find(title_with_edit_selector).set("Work item title")
send_keys([:command, :enter])
wait_for_requests
expect(work_item.reload.title).to eq 'Work item title'
end
end
end