Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0594381ba7
commit
5169b4a63b
|
@ -2,7 +2,7 @@
|
|||
|
||||
<!--
|
||||
Please describe the engineering productivity problem that needs to be solved backed by charts from
|
||||
https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/#engineering-productivity-team-metrics.
|
||||
https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/#engineering-productivity-metrics.
|
||||
-->
|
||||
|
||||
### Problem identification checklist
|
||||
|
|
|
@ -33,6 +33,6 @@ This will help keep track of expected cost increases to the [GitLab project aver
|
|||
|
||||
### Post-merge
|
||||
|
||||
- [ ] Consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/#pipeline-changes)
|
||||
- [ ] Consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/#pipeline-changes)
|
||||
|
||||
/label ~tooling ~"tooling::pipelines" ~"Engineering Productivity"
|
||||
|
|
|
@ -1 +1 @@
|
|||
ee4b20cc318876c4b237e277fefd9d75186a085c
|
||||
156e65d1de93e779091a0915cf77b2aefa85fadf
|
||||
|
|
|
@ -65,7 +65,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="blob-viewer" :data-type="activeViewer.type">
|
||||
<div class="blob-viewer" :data-type="activeViewer.type" :data-loaded="!loading">
|
||||
<gl-loading-icon v-if="loading" size="md" color="dark" class="my-4 mx-auto" />
|
||||
|
||||
<template v-else>
|
||||
|
|
|
@ -3,12 +3,14 @@ import DefaultActions from './blob_header_default_actions.vue';
|
|||
import BlobFilepath from './blob_header_filepath.vue';
|
||||
import ViewerSwitcher from './blob_header_viewer_switcher.vue';
|
||||
import { SIMPLE_BLOB_VIEWER } from './constants';
|
||||
import TableOfContents from './table_contents.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ViewerSwitcher,
|
||||
DefaultActions,
|
||||
BlobFilepath,
|
||||
TableOfContents,
|
||||
},
|
||||
props: {
|
||||
blob: {
|
||||
|
@ -70,11 +72,14 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div class="js-file-title file-title-flex-parent">
|
||||
<blob-filepath :blob="blob">
|
||||
<template #filepath-prepend>
|
||||
<slot name="prepend"></slot>
|
||||
</template>
|
||||
</blob-filepath>
|
||||
<div class="gl-display-flex">
|
||||
<table-of-contents class="gl-pr-2" />
|
||||
<blob-filepath :blob="blob">
|
||||
<template #filepath-prepend>
|
||||
<slot name="prepend"></slot>
|
||||
</template>
|
||||
</blob-filepath>
|
||||
</div>
|
||||
|
||||
<div class="gl-display-none gl-sm-display-flex">
|
||||
<viewer-switcher v-if="showViewerSwitcher" v-model="viewer" />
|
||||
|
|
|
@ -18,11 +18,12 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
this.blobViewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
||||
const blobViewerAttr = (attr) => this.blobViewer.getAttribute(attr);
|
||||
|
||||
this.observer = new MutationObserver(() => {
|
||||
if (this.blobViewer.classList.contains('hidden')) {
|
||||
if (this.blobViewer.classList.contains('hidden') || blobViewerAttr('data-type') !== 'rich') {
|
||||
this.isHidden = true;
|
||||
} else if (this.blobViewer.getAttribute('data-loaded') === 'true') {
|
||||
} else if (blobViewerAttr('data-loaded') === 'true') {
|
||||
this.isHidden = false;
|
||||
this.generateHeaders();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<span class="folder-caret gl-mr-2">
|
||||
<gl-icon :size="10" :name="iconClass" use-deprecated-sizes />
|
||||
<gl-icon :size="12" :name="iconClass" />
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -192,9 +192,14 @@ export default {
|
|||
class="gl-sm-display-none! w-100"
|
||||
block
|
||||
:text="dropdownText"
|
||||
data-qa-selector="issue_actions_dropdown"
|
||||
:loading="isToggleStateButtonLoading"
|
||||
>
|
||||
<gl-dropdown-item v-if="showToggleIssueStateButton" @click="toggleIssueState">
|
||||
<gl-dropdown-item
|
||||
v-if="showToggleIssueStateButton"
|
||||
:data-qa-selector="`mobile_${qaSelector}`"
|
||||
@click="toggleIssueState"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
|
||||
|
|
|
@ -55,6 +55,7 @@ export default {
|
|||
v-gl-tooltip="{ title: newDropdownViewModel.title }"
|
||||
:view-model="newDropdownViewModel"
|
||||
class="gl-ml-3"
|
||||
data-qa-selector="mobile_new_dropdown"
|
||||
/>
|
||||
</header>
|
||||
<top-nav-menu-sections class="gl-h-full" :sections="menuSections" v-on="$listeners" />
|
||||
|
|
|
@ -46,6 +46,7 @@ export default {
|
|||
link-class="top-nav-menu-item"
|
||||
:href="menuItem.href"
|
||||
data-testid="item"
|
||||
:data-qa-selector="`${menuItem.title.toLowerCase().replace(' ', '_')}_mobile_button`"
|
||||
>
|
||||
{{ menuItem.title }}
|
||||
</gl-dropdown-item>
|
||||
|
|
|
@ -197,7 +197,7 @@ export default {
|
|||
class="gl-display-flex gl-align-items-center gl-py-3 gl-pl-7"
|
||||
data-testid="extension-list-item"
|
||||
>
|
||||
<status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" />
|
||||
<status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" class="gl-pl-0" />
|
||||
<gl-intersection-observer
|
||||
:options="{ rootMargin: '100px', thresholds: 0.1 }"
|
||||
class="gl-flex-wrap gl-align-self-center gl-display-flex"
|
||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
|||
<template>
|
||||
<span>
|
||||
<gl-loading-icon v-if="loading" size="sm" :inline="true" />
|
||||
<gl-icon v-else-if="isSymlink" name="symlink" :size="size" use-deprecated-sizes />
|
||||
<gl-icon v-else-if="isSymlink" name="symlink" :size="size" />
|
||||
<svg v-else-if="!folder" :key="spriteHref" :class="[iconSizeClass, cssClasses]">
|
||||
<use v-bind="{ 'xlink:href': spriteHref }" />
|
||||
</svg>
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
module Mutations
|
||||
module Issues
|
||||
class Create < BaseMutation
|
||||
include Mutations::SpamProtection
|
||||
include FindsProject
|
||||
include CommonMutationArguments
|
||||
|
||||
graphql_name 'CreateIssue'
|
||||
|
||||
authorize :create_issue
|
||||
|
||||
include CommonMutationArguments
|
||||
|
||||
argument :project_path, GraphQL::Types::ID,
|
||||
required: true,
|
||||
description: 'Project full path the issue is associated with.'
|
||||
|
@ -76,9 +77,7 @@ module Mutations
|
|||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||
issue = ::Issues::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute
|
||||
|
||||
if issue.spam?
|
||||
issue.errors.add(:base, 'Spam detected.')
|
||||
end
|
||||
check_spam_action_response!(issue)
|
||||
|
||||
{
|
||||
issue: issue.valid? ? issue : nil,
|
||||
|
|
|
@ -684,7 +684,9 @@ module Ci
|
|||
end
|
||||
|
||||
def freeze_period?
|
||||
Ci::FreezePeriodStatus.new(project: project).execute
|
||||
strong_memoize(:freeze_period) do
|
||||
Ci::FreezePeriodStatus.new(project: project).execute
|
||||
end
|
||||
end
|
||||
|
||||
def has_warnings?
|
||||
|
@ -800,20 +802,7 @@ module Ci
|
|||
variables.append(key: 'CI_PIPELINE_CREATED_AT', value: created_at&.iso8601)
|
||||
|
||||
variables.concat(predefined_commit_variables)
|
||||
|
||||
if merge_request?
|
||||
variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
|
||||
|
||||
diff = self.merge_request_diff
|
||||
if diff.present?
|
||||
variables.append(key: 'CI_MERGE_REQUEST_DIFF_ID', value: diff.id.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_DIFF_BASE_SHA', value: diff.base_commit_sha)
|
||||
end
|
||||
|
||||
variables.concat(merge_request.predefined_variables)
|
||||
end
|
||||
variables.concat(predefined_merge_request_variables)
|
||||
|
||||
if open_merge_requests_refs.any?
|
||||
variables.append(key: 'CI_OPEN_MERGE_REQUESTS', value: open_merge_requests_refs.join(','))
|
||||
|
@ -829,27 +818,49 @@ module Ci
|
|||
end
|
||||
|
||||
def predefined_commit_variables
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.append(key: 'CI_COMMIT_SHA', value: sha)
|
||||
variables.append(key: 'CI_COMMIT_SHORT_SHA', value: short_sha)
|
||||
variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
|
||||
variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref)
|
||||
variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug)
|
||||
variables.append(key: 'CI_COMMIT_BRANCH', value: ref) if branch?
|
||||
variables.append(key: 'CI_COMMIT_TAG', value: ref) if tag?
|
||||
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
|
||||
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
|
||||
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
|
||||
variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
|
||||
variables.append(key: 'CI_COMMIT_TIMESTAMP', value: git_commit_timestamp.to_s)
|
||||
variables.append(key: 'CI_COMMIT_AUTHOR', value: git_author_full_text.to_s)
|
||||
strong_memoize(:predefined_commit_variables) do
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.append(key: 'CI_COMMIT_SHA', value: sha)
|
||||
variables.append(key: 'CI_COMMIT_SHORT_SHA', value: short_sha)
|
||||
variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
|
||||
variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref)
|
||||
variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug)
|
||||
variables.append(key: 'CI_COMMIT_BRANCH', value: ref) if branch?
|
||||
variables.append(key: 'CI_COMMIT_TAG', value: ref) if tag?
|
||||
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
|
||||
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
|
||||
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
|
||||
variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
|
||||
variables.append(key: 'CI_COMMIT_TIMESTAMP', value: git_commit_timestamp.to_s)
|
||||
variables.append(key: 'CI_COMMIT_AUTHOR', value: git_author_full_text.to_s)
|
||||
|
||||
# legacy variables
|
||||
variables.append(key: 'CI_BUILD_REF', value: sha)
|
||||
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
|
||||
variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref)
|
||||
variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug)
|
||||
variables.append(key: 'CI_BUILD_TAG', value: ref) if tag?
|
||||
# legacy variables
|
||||
variables.append(key: 'CI_BUILD_REF', value: sha)
|
||||
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
|
||||
variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref)
|
||||
variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug)
|
||||
variables.append(key: 'CI_BUILD_TAG', value: ref) if tag?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def predefined_merge_request_variables
|
||||
strong_memoize(:predefined_merge_request_variables) do
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
next variables unless merge_request?
|
||||
|
||||
variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
|
||||
|
||||
diff = self.merge_request_diff
|
||||
if diff.present?
|
||||
variables.append(key: 'CI_MERGE_REQUEST_DIFF_ID', value: diff.id.to_s)
|
||||
variables.append(key: 'CI_MERGE_REQUEST_DIFF_BASE_SHA', value: diff.base_commit_sha)
|
||||
end
|
||||
|
||||
variables.concat(merge_request.predefined_variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2056,14 +2056,16 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def predefined_variables
|
||||
Gitlab::Ci::Variables::Collection.new
|
||||
.concat(predefined_ci_server_variables)
|
||||
.concat(predefined_project_variables)
|
||||
.concat(pages_variables)
|
||||
.concat(container_registry_variables)
|
||||
.concat(dependency_proxy_variables)
|
||||
.concat(auto_devops_variables)
|
||||
.concat(api_variables)
|
||||
strong_memoize(:predefined_variables) do
|
||||
Gitlab::Ci::Variables::Collection.new
|
||||
.concat(predefined_ci_server_variables)
|
||||
.concat(predefined_project_variables)
|
||||
.concat(pages_variables)
|
||||
.concat(container_registry_variables)
|
||||
.concat(dependency_proxy_variables)
|
||||
.concat(auto_devops_variables)
|
||||
.concat(api_variables)
|
||||
end
|
||||
end
|
||||
|
||||
def predefined_project_variables
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
|
||||
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-default btn-sign-in'
|
||||
|
||||
%button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: 'gl-border-none!', data: { testid: 'top-nav-responsive-toggle' } }
|
||||
%button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: 'gl-border-none!', data: { testid: 'top-nav-responsive-toggle', qa_selector: 'mobile_navbar_button' } }
|
||||
%span.sr-only= _('Toggle navigation')
|
||||
%span.more-icon.gl-px-3.gl-font-sm.gl-font-weight-bold
|
||||
%span.gl-pr-2= _('Menu')
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%nav.breadcrumbs{ class: [container, @content_class], 'aria-label': _('Breadcrumbs') }
|
||||
.breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
|
||||
- if defined?(@left_sidebar)
|
||||
= button_tag class: 'toggle-mobile-nav', type: 'button' do
|
||||
= button_tag class: 'toggle-mobile-nav', data: { qa_selector: 'toggle_mobile_nav_button' }, type: 'button' do
|
||||
%span.sr-only= _("Open sidebar")
|
||||
= sprite_icon('hamburger', size: 18)
|
||||
.breadcrumbs-links{ data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } }
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
= edit_blob_button(@project, @ref, @path, blob: blob)
|
||||
= ide_edit_button(@project, @ref, @path, blob: blob)
|
||||
- if can_view_pipeline_editor?(@project) && @path == @project.ci_config_path_or_default
|
||||
= link_to "Pipeline Editor", project_ci_pipeline_editor_path(@project), class: "btn gl-button btn-confirm-secondary gl-ml-3"
|
||||
= link_to "Pipeline Editor", project_ci_pipeline_editor_path(@project, branch_name: @ref), class: "btn gl-button btn-confirm-secondary gl-ml-3"
|
||||
.btn-group{ role: "group", class: ("gl-ml-3" if current_user) }>
|
||||
= render_if_exists 'projects/blob/header_file_locks_link'
|
||||
- if current_user
|
||||
|
|
|
@ -14,7 +14,7 @@ Please consider the effect of the changes in this merge request on the following
|
|||
- personal forks
|
||||
- Effects on [pipeline performance](https://about.gitlab.com/handbook/engineering/quality/performance-indicators/#average-merge-request-pipeline-duration-for-gitlab)
|
||||
|
||||
Please consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/#pipeline-changes)
|
||||
Please consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/#pipeline-changes)
|
||||
MESSAGE
|
||||
|
||||
if helper.has_ci_changes?
|
||||
|
|
|
@ -122,7 +122,7 @@ In these cases, use the following workflow:
|
|||
- [User Experience (UX)](https://about.gitlab.com/handbook/engineering/ux/)
|
||||
- [Security](https://about.gitlab.com/handbook/engineering/security/)
|
||||
- [Quality](https://about.gitlab.com/handbook/engineering/quality/)
|
||||
- [Engineering Productivity](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/)
|
||||
- [Engineering Productivity](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/)
|
||||
- [Infrastructure](https://about.gitlab.com/handbook/engineering/infrastructure/)
|
||||
- [Technical Writing](https://about.gitlab.com/handbook/engineering/ux/technical-writing/)
|
||||
|
||||
|
|
|
@ -485,3 +485,109 @@ To run the LDAP tests on your local with TLS disabled, follow these steps:
|
|||
```shell
|
||||
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_DEBUG=true WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All http://localhost qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
|
||||
```
|
||||
|
||||
## Guide to the mobile suite
|
||||
|
||||
### What are mobile tests
|
||||
|
||||
Tests that are tagged with `:mobile` can be run against specified mobile devices using cloud emulator/simulator services.
|
||||
|
||||
### How to run mobile tests with Sauce Labs
|
||||
|
||||
Running directly against an environment like staging is not recommended because Sauce Labs test logs expose credentials. Therefore, it is best practice and the default to use a tunnel.
|
||||
|
||||
Tunnel installation instructions are here [https://docs.saucelabs.com/secure-connections/sauce-connect/installation]. To start the tunnel, after following the installation above, copy the run command in Sauce Labs > Tunnels (must be logged in to Sauce Labs with the credentials found in 1Password) and run in terminal.
|
||||
|
||||
NOTE:
|
||||
It is highly recommended to use `GITLAB_QA_ACCESS_TOKEN` to speed up tests and reduce flakiness.
|
||||
|
||||
`QA_REMOTE_MOBILE_DEVICE_NAME` can be any device name listed in [https://saucelabs.com/platform/supported-browsers-devices] under Emulators/simulators and the latest versions of Android or iOS. `QA_BROWSER` must be set to `safari` for iOS devices and `chrome` for Android devices.
|
||||
|
||||
1. To test against a local instance with a tunnel running, in `gitlab/qa` run:
|
||||
|
||||
```shell
|
||||
$ QA_BROWSER="safari" \
|
||||
QA_REMOTE_MOBILE_DEVICE_NAME="iPhone 12 Simulator" \
|
||||
QA_REMOTE_GRID="ondemand.saucelabs.com:80" \
|
||||
QA_REMOTE_GRID_USERNAME="gitlab-sl" \
|
||||
QA_REMOTE_GRID_ACCESS_KEY="<found in Sauce Lab account>" \
|
||||
GITLAB_QA_ACCESS_TOKEN="<token>" \
|
||||
bundle exec bin/qa Test::Instance::All http://<local_ip>:3000 -- <relative_spec_path>
|
||||
```
|
||||
|
||||
Results can be watched in real time while logged into Sauce Labs under AUTOMATED > Test Results.
|
||||
|
||||
### How to add an existing test to the mobile suite
|
||||
|
||||
The main reason a test might fail when adding the `:mobile` tag is navigation differences in desktop vs mobile layouts, therefore the test needs to be updated to use mobile navigation when running mobile tests.
|
||||
|
||||
If an existing method needs to be changed or a new one created, a new mobile page object should be created in `qa/qa/mobile/page/` and it should be prepended in the original page object by adding:
|
||||
|
||||
```ruby
|
||||
prepend Mobile::Page::NewPageObject if Runtime::Env.mobile_layout?
|
||||
```
|
||||
|
||||
For example to change an existing method when running mobile tests:
|
||||
|
||||
New mobile page object:
|
||||
|
||||
```ruby
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module Project
|
||||
module Show
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
prepend QA::Mobile::Page::Main::Menu
|
||||
|
||||
view 'app/assets/javascripts/nav/components/top_nav_new_dropdown.vue' do
|
||||
element :new_issue_mobile_button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_new_issue
|
||||
open_mobile_new_dropdown
|
||||
|
||||
click_element(:new_issue_mobile_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Original page object prepending the new mobile if there's a mobile layout:
|
||||
|
||||
```ruby
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
class Show < Page::Base
|
||||
prepend Mobile::Page::Project::Show if Runtime::Env.mobile_layout?
|
||||
|
||||
view 'app/views/layouts/header/_new_dropdown.html.haml' do
|
||||
element :new_menu_toggle
|
||||
end
|
||||
|
||||
view 'app/helpers/nav/new_dropdown_helper.rb' do
|
||||
element :new_issue_link
|
||||
end
|
||||
|
||||
def go_to_new_issue
|
||||
click_element(:new_menu_toggle)
|
||||
click_element(:new_issue_link)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
When running mobile tests for phone layouts, both `remote_mobile_device_name` and `mobile_layout` are `true` but when using a tablet layout, only `remote_mobile_device_name` is true. This is because phone layouts have more menus closed by default such as how both tablets and phones have the left nav closed but unlike phone layouts, tablets have the regular top navigation bar, not the mobile one. So in the case where the navigation being edited needs to be used in tablet layouts as well, use `remote_mobile_device_name` instead of `mobile_layout?` when prepending so it will use it if it's a tablet layout as well.
|
||||
|
|
|
@ -360,8 +360,8 @@ always take the latest dependency scanning artifact available.
|
|||
|
||||
### Enable Dependency Scanning via an automatic merge request
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4908) in GitLab 14.1.
|
||||
> - [Enabled with `sec_dependency_scanning_ui_enable` flag](https://gitlab.com/gitlab-org/gitlab/-/issues/282533) for self-managed GitLab in GitLab 14.1 and is ready for production use.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4908) in GitLab 14.1 [with a flag](../../../administration/feature_flags.md) named `sec_dependency_scanning_ui_enable`. Enabled by default.
|
||||
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/282533) in GitLab 14.1.
|
||||
> - [Feature flag sec_dependency_scanning_ui_enable removed](https://gitlab.com/gitlab-org/gitlab/-/issues/326005) in GitLab 14.2.
|
||||
|
||||
To enable Dependency Scanning in a project, you can create a merge request
|
||||
|
|
|
@ -203,7 +203,7 @@ specified the `kas-address` correctly.
|
|||
```
|
||||
|
||||
This error occurs if the `kas-address` doesn't include a trailing slash. To fix it, make sure that the
|
||||
`wss` or `ws` URL ends with a training slash, such as `wss://GitLab.host.tld:443/-/kubernetes-agent/`
|
||||
`wss` or `ws` URL ends with a trailing slash, such as `wss://GitLab.host.tld:443/-/kubernetes-agent/`
|
||||
or `ws://GitLab.host.tld:80/-/kubernetes-agent/`.
|
||||
|
||||
#### ValidationError(Deployment.metadata)
|
||||
|
|
|
@ -23,8 +23,11 @@ module QA
|
|||
end
|
||||
|
||||
def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false)
|
||||
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?)
|
||||
Runtime::Browser.visit(address, Page::Main::Login)
|
||||
unless Page::Main::Login.perform(&:on_login_page?)
|
||||
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?)
|
||||
Runtime::Browser.visit(address, Page::Main::Login)
|
||||
end
|
||||
|
||||
Page::Main::Login.perform do |login|
|
||||
if admin
|
||||
login.sign_in_using_admin_credentials
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module Main
|
||||
module Menu
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
view 'app/views/layouts/header/_default.html.haml' do
|
||||
element :mobile_navbar_button, required: true
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/nav/components/responsive_home.vue' do
|
||||
element :mobile_new_dropdown
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def open_mobile_menu
|
||||
if has_no_element?(:user_avatar)
|
||||
Support::Retrier.retry_until do
|
||||
click_element(:mobile_navbar_button)
|
||||
has_element?(:user_avatar)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def open_mobile_new_dropdown
|
||||
open_mobile_menu
|
||||
|
||||
Support::Retrier.retry_until do
|
||||
find('[data-qa-selector="mobile_new_dropdown"] > button').click
|
||||
has_css?('.dropdown-menu-right.show')
|
||||
end
|
||||
end
|
||||
|
||||
def has_personal_area?(wait: Capybara.default_max_wait_time)
|
||||
open_mobile_menu
|
||||
super
|
||||
end
|
||||
|
||||
def has_no_personal_area?(wait: Capybara.default_max_wait_time)
|
||||
open_mobile_menu
|
||||
super
|
||||
end
|
||||
|
||||
def within_user_menu
|
||||
open_mobile_menu
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module Profile
|
||||
module Menu
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
prepend QA::Mobile::Page::Main::Menu
|
||||
end
|
||||
end
|
||||
|
||||
def within_sidebar
|
||||
open_mobile_nav_sidebar
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module Project
|
||||
module Issue
|
||||
module Show
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
view 'app/assets/javascripts/issue_show/components/header_actions.vue' do
|
||||
element :issue_actions_dropdown
|
||||
element :mobile_close_issue_button
|
||||
element :mobile_reopen_issue_button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def click_close_issue_button
|
||||
find('[data-qa-selector="issue_actions_dropdown"] > button').click
|
||||
find_element(:mobile_close_issue_button, visible: false).click
|
||||
end
|
||||
|
||||
def has_reopen_issue_button?
|
||||
find('[data-qa-selector="issue_actions_dropdown"] > button').click
|
||||
has_element?(:mobile_reopen_issue_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module Project
|
||||
module Show
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
prepend QA::Mobile::Page::Main::Menu
|
||||
|
||||
view 'app/assets/javascripts/nav/components/top_nav_new_dropdown.vue' do
|
||||
element :new_issue_mobile_button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_new_issue
|
||||
open_mobile_new_dropdown
|
||||
|
||||
click_element(:new_issue_mobile_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Mobile
|
||||
module Page
|
||||
module SubMenus
|
||||
module Common
|
||||
def open_mobile_nav_sidebar
|
||||
if has_element?(:project_sidebar, visible: false)
|
||||
Support::Retrier.retry_until do
|
||||
click_element(:toggle_mobile_nav_button)
|
||||
has_element?(:project_sidebar, visible: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def within_sidebar
|
||||
wait_for_requests
|
||||
|
||||
open_mobile_nav_sidebar
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,6 +45,10 @@ module QA
|
|||
has_element?(:sign_in_button)
|
||||
end
|
||||
|
||||
def on_login_page?
|
||||
has_element?(:login_page, wait: 0)
|
||||
end
|
||||
|
||||
def sign_in_using_credentials(user: nil, skip_page_validation: false)
|
||||
# Don't try to log-in if we're already logged-in
|
||||
return if Page::Main::Menu.perform(&:signed_in?)
|
||||
|
@ -164,6 +168,8 @@ module QA
|
|||
fill_element :password_field, user.password
|
||||
click_element :sign_in_button
|
||||
|
||||
Support::WaitForRequests.wait_for_requests
|
||||
|
||||
Page::Main::Terms.perform do |terms|
|
||||
terms.accept_terms if terms.visible?
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ module QA
|
|||
module Page
|
||||
module Main
|
||||
class Menu < Page::Base
|
||||
prepend Mobile::Page::Main::Menu if Runtime::Env.mobile_layout?
|
||||
|
||||
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
|
||||
element :sign_out_link
|
||||
element :edit_profile_link
|
||||
|
@ -12,12 +14,12 @@ module QA
|
|||
view 'app/views/layouts/header/_default.html.haml' do
|
||||
element :navbar, required: true
|
||||
element :canary_badge_link
|
||||
element :user_avatar, required: true
|
||||
element :user_menu, required: true
|
||||
element :user_avatar, required: !QA::Runtime::Env.mobile_layout?
|
||||
element :user_menu, required: !QA::Runtime::Env.mobile_layout?
|
||||
element :stop_impersonation_link
|
||||
element :issues_shortcut_button, required: true
|
||||
element :merge_requests_shortcut_button, required: true
|
||||
element :todos_shortcut_button, required: true
|
||||
element :issues_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
|
||||
element :merge_requests_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
|
||||
element :todos_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/nav/components/top_nav_app.vue' do
|
||||
|
@ -98,10 +100,14 @@ module QA
|
|||
end
|
||||
|
||||
def signed_in?
|
||||
return false if Page::Main::Login.perform(&:on_login_page?)
|
||||
|
||||
has_personal_area?(wait: 0)
|
||||
end
|
||||
|
||||
def not_signed_in?
|
||||
return true if Page::Main::Login.perform(&:on_login_page?)
|
||||
|
||||
has_no_personal_area?
|
||||
end
|
||||
|
||||
|
@ -115,7 +121,7 @@ module QA
|
|||
click_element :sign_out_link
|
||||
end
|
||||
|
||||
has_no_element?(:user_avatar)
|
||||
not_signed_in?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ module QA
|
|||
module Page
|
||||
module Profile
|
||||
class Menu < Page::Base
|
||||
# We need to check remote_mobile_device_name instead of mobile_layout? here
|
||||
# since tablets have the regular top navigation bar but still close the left nav
|
||||
prepend QA::Mobile::Page::Profile::Menu if QA::Runtime::Env.remote_mobile_device_name
|
||||
|
||||
view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
|
||||
element :access_token_link, 'link_to profile_personal_access_tokens_path' # rubocop:disable QA/ElementWithPattern
|
||||
element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern
|
||||
|
|
|
@ -9,6 +9,7 @@ module QA
|
|||
include Page::Component::Note
|
||||
include Page::Component::DesignManagement
|
||||
include Page::Component::Issuable::Sidebar
|
||||
prepend Mobile::Page::Project::Issue::Show if Runtime::Env.mobile_layout?
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue' do
|
||||
element :remove_related_issue_button
|
||||
|
@ -64,6 +65,10 @@ module QA
|
|||
def has_metrics_unfurled?
|
||||
has_element?(:prometheus_graph_widgets, wait: 30)
|
||||
end
|
||||
|
||||
def has_reopen_issue_button?
|
||||
has_element?(:reopen_issue_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ module QA
|
|||
include Page::Component::Breadcrumbs
|
||||
include Page::Project::SubMenus::Settings
|
||||
include Page::File::Shared::CommitMessage
|
||||
prepend Mobile::Page::Project::Show if Runtime::Env.mobile_layout?
|
||||
|
||||
view 'app/assets/javascripts/repository/components/preview/index.vue' do
|
||||
element :blob_viewer_content
|
||||
|
@ -117,7 +118,7 @@ module QA
|
|||
end
|
||||
|
||||
def go_to_new_issue
|
||||
click_element :new_menu_toggle
|
||||
click_element(:new_menu_toggle)
|
||||
click_element(:new_issue_link)
|
||||
end
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ module QA
|
|||
view 'app/views/shared/nav/_sidebar_menu.html.haml' do
|
||||
element :sidebar_menu_link
|
||||
end
|
||||
|
||||
view 'app/views/layouts/nav/_breadcrumbs.html.haml' do
|
||||
element :toggle_mobile_nav_button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ module QA
|
|||
module Page
|
||||
module SubMenus
|
||||
module Common
|
||||
# We need to check remote_mobile_device_name instead of mobile_layout? here
|
||||
# since tablets have the regular top navigation bar but still close the left nav
|
||||
prepend Mobile::Page::SubMenus::Common if QA::Runtime::Env.remote_mobile_device_name
|
||||
|
||||
def hover_element(element)
|
||||
within_sidebar do
|
||||
find_element(element).hover
|
||||
|
|
|
@ -205,6 +205,9 @@ module QA
|
|||
|
||||
simulate_slow_connection if Runtime::Env.simulate_slow_connection?
|
||||
|
||||
# Wait until the new page is ready for us to interact with it
|
||||
Support::WaitForRequests.wait_for_requests
|
||||
|
||||
page_class.validate_elements_present! if page_class.respond_to?(:validate_elements_present!)
|
||||
|
||||
if QA::Runtime::Env.qa_cookies
|
||||
|
|
|
@ -153,6 +153,12 @@ module QA
|
|||
ENV['QA_REMOTE_MOBILE_DEVICE_NAME']
|
||||
end
|
||||
|
||||
def mobile_layout?
|
||||
return false if ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].blank?
|
||||
|
||||
!(ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].downcase.include?('ipad') || ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].downcase.include?('tablet'))
|
||||
end
|
||||
|
||||
def user_username
|
||||
ENV['GITLAB_USERNAME']
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :smoke do
|
||||
RSpec.describe 'Manage', :smoke, :mobile do
|
||||
describe 'basic user login' do
|
||||
it 'user logs in using basic credentials and logs out', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1578' do
|
||||
Flow::Login.sign_in
|
||||
|
|
|
@ -9,7 +9,7 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it 'creates an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1185' do
|
||||
it 'creates an issue', :mobile, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1185' do
|
||||
issue = Resource::Issue.fabricate_via_browser_ui!
|
||||
|
||||
Page::Project::Menu.perform(&:click_issues)
|
||||
|
@ -19,13 +19,13 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'closes an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1222' do
|
||||
it 'closes an issue', :mobile, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1222' do
|
||||
closed_issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform do |issue_page|
|
||||
issue_page.click_close_issue_button
|
||||
|
||||
expect(issue_page).to have_element(:reopen_issue_button)
|
||||
expect(issue_page).to have_reopen_issue_button
|
||||
end
|
||||
|
||||
Page::Project::Menu.perform(&:click_issues)
|
||||
|
|
|
@ -196,8 +196,8 @@ function rspec_rerun_previous_failed_tests() {
|
|||
local test_file_count=$(wc -w "${matching_tests_file}" | awk {'print $1'})
|
||||
|
||||
if [[ "${test_file_count}" -gt "${test_file_count_threshold}" ]]; then
|
||||
echo "This job is intentionally failed because there are more than ${test_file_count_threshold} test files to rerun."
|
||||
exit 1
|
||||
echo "This job is intentionally exited because there are more than ${test_file_count_threshold} test files to rerun."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n $test_files ]]; then
|
||||
|
|
|
@ -4,9 +4,17 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = `
|
|||
<div
|
||||
class="js-file-title file-title-flex-parent"
|
||||
>
|
||||
<blob-filepath-stub
|
||||
blob="[object Object]"
|
||||
/>
|
||||
<div
|
||||
class="gl-display-flex"
|
||||
>
|
||||
<table-of-contents-stub
|
||||
class="gl-pr-2"
|
||||
/>
|
||||
|
||||
<blob-filepath-stub
|
||||
blob="[object Object]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-display-none gl-sm-display-flex"
|
||||
|
|
|
@ -3,6 +3,7 @@ import BlobHeader from '~/blob/components/blob_header.vue';
|
|||
import DefaultActions from '~/blob/components/blob_header_default_actions.vue';
|
||||
import BlobFilepath from '~/blob/components/blob_header_filepath.vue';
|
||||
import ViewerSwitcher from '~/blob/components/blob_header_viewer_switcher.vue';
|
||||
import TableContents from '~/blob/components/table_contents.vue';
|
||||
|
||||
import { Blob } from './mock_data';
|
||||
|
||||
|
@ -43,6 +44,7 @@ describe('Blob Header Default Actions', () => {
|
|||
|
||||
it('renders all components', () => {
|
||||
createComponent();
|
||||
expect(wrapper.find(TableContents).exists()).toBe(true);
|
||||
expect(wrapper.find(ViewerSwitcher).exists()).toBe(true);
|
||||
expect(findDefaultActions().exists()).toBe(true);
|
||||
expect(wrapper.find(BlobFilepath).exists()).toBe(true);
|
||||
|
|
|
@ -32,10 +32,30 @@ describe('Markdown table of contents component', () => {
|
|||
});
|
||||
|
||||
describe('not loaded', () => {
|
||||
const findDropdownItem = () => wrapper.findComponent(GlDropdownItem);
|
||||
|
||||
it('does not populate dropdown', () => {
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(false);
|
||||
expect(findDropdownItem().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not show dropdown when loading blob content', async () => {
|
||||
createComponent();
|
||||
|
||||
await setLoaded(false);
|
||||
|
||||
expect(findDropdownItem().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not show dropdown when viewing non-rich content', async () => {
|
||||
createComponent();
|
||||
|
||||
document.querySelector('.blob-viewer').setAttribute('data-type', 'simple');
|
||||
|
||||
await setLoaded(true);
|
||||
|
||||
expect(findDropdownItem().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -48,5 +48,9 @@ RSpec.describe 'Create an issue' do
|
|||
expect(mutation_response['issue']).to include('discussionLocked' => true)
|
||||
expect(Issue.last.work_item_type.base_type).to eq('issue')
|
||||
end
|
||||
|
||||
it_behaves_like 'has spam protection' do
|
||||
let(:mutation_class) { ::Mutations::Issues::Create }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,8 @@ require 'spec_helper'
|
|||
RSpec.describe Ci::CreatePipelineService do
|
||||
include ProjectForksHelper
|
||||
|
||||
let_it_be(:project, reload: true) { create(:project, :repository) }
|
||||
let_it_be(:user, reload: true) { project.owner }
|
||||
let_it_be_with_refind(:project) { create(:project, :repository) }
|
||||
let_it_be_with_reload(:user) { project.owner }
|
||||
|
||||
let(:ref_name) { 'refs/heads/master' }
|
||||
|
||||
|
|
|
@ -38,6 +38,11 @@ RSpec.shared_context 'stubbed service ping metrics definitions' do
|
|||
)
|
||||
end
|
||||
|
||||
after do |example|
|
||||
Gitlab::Usage::Metric.instance_variable_set(:@all, nil)
|
||||
Gitlab::Usage::MetricDefinition.instance_variable_set(:@all, nil)
|
||||
end
|
||||
|
||||
def metric_attributes(key_path, category, value_type = 'string')
|
||||
{
|
||||
'key_path' => key_path,
|
||||
|
|
Loading…
Reference in New Issue