Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
778772c8d9
commit
0820b29dca
47 changed files with 197 additions and 154 deletions
|
@ -27,17 +27,7 @@ module Boards
|
|||
issues = list_service.execute
|
||||
issues = issues.page(params[:page]).per(params[:per] || 20).without_count
|
||||
Issue.move_nulls_to_end(issues) if Gitlab::Database.read_write?
|
||||
issues = issues.preload(:milestone,
|
||||
:assignees,
|
||||
project: [
|
||||
:route,
|
||||
{
|
||||
namespace: [:route]
|
||||
}
|
||||
],
|
||||
labels: [:priorities],
|
||||
notes: [:award_emoji, :author]
|
||||
)
|
||||
issues = issues.preload(associations_to_preload)
|
||||
|
||||
render_issues(issues, list_service.metadata)
|
||||
end
|
||||
|
@ -74,6 +64,21 @@ module Boards
|
|||
|
||||
private
|
||||
|
||||
def associations_to_preload
|
||||
[
|
||||
:milestone,
|
||||
:assignees,
|
||||
project: [
|
||||
:route,
|
||||
{
|
||||
namespace: [:route]
|
||||
}
|
||||
],
|
||||
labels: [:priorities],
|
||||
notes: [:award_emoji, :author]
|
||||
]
|
||||
end
|
||||
|
||||
def can_move_issues?
|
||||
head(:forbidden) unless can?(current_user, :admin_issue, board)
|
||||
end
|
||||
|
@ -139,3 +144,5 @@ module Boards
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Boards::IssuesController.prepend_if_ee('EE::Boards::IssuesController')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add indexes for authenticated Project API calls
|
||||
merge_request: 22886
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexesForProjectsApiAuthenticated < ActiveRecord::Migration[5.2]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
COLUMNS = %i(updated_at name)
|
||||
|
||||
def up
|
||||
add_concurrent_index :projects, %i(created_at id), order: { id: :desc }, name: 'index_projects_api_created_at_id_desc'
|
||||
|
||||
add_concurrent_index :projects, %i(last_activity_at id), name: 'index_projects_on_last_activity_at_and_id'
|
||||
remove_concurrent_index :projects, :last_activity_at
|
||||
add_concurrent_index :projects, %i(last_activity_at id), order: { id: :desc }, name: 'index_projects_api_last_activity_at_id_desc'
|
||||
|
||||
add_concurrent_index :projects, %i(path id), name: 'index_projects_on_path_and_id'
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_on_path'
|
||||
add_concurrent_index :projects, %i(path id), order: { id: :desc }, name: 'index_projects_api_path_id_desc'
|
||||
|
||||
COLUMNS.each do |column|
|
||||
add_concurrent_index :projects, [column, :id], name: "index_projects_on_#{column}_and_id"
|
||||
add_concurrent_index :projects, [column, :id], order: { id: :desc }, name: "index_projects_api_#{column}_id_desc"
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_api_created_at_id_desc'
|
||||
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_on_last_activity_at_and_id'
|
||||
add_concurrent_index :projects, :last_activity_at, name: 'index_projects_on_last_activity_at'
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_api_last_activity_at_id_desc'
|
||||
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_on_path_and_id'
|
||||
add_concurrent_index :projects, :path, name: 'index_projects_on_path'
|
||||
remove_concurrent_index_by_name :projects, 'index_projects_api_path_id_desc'
|
||||
|
||||
COLUMNS.each do |column|
|
||||
remove_concurrent_index_by_name :projects, "index_projects_on_#{column}_and_id"
|
||||
remove_concurrent_index_by_name :projects, "index_projects_api_#{column}_id_desc"
|
||||
end
|
||||
end
|
||||
end
|
13
db/schema.rb
13
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_01_10_144316) do
|
||||
ActiveRecord::Schema.define(version: 2020_01_13_133352) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_trgm"
|
||||
|
@ -3353,6 +3353,7 @@ ActiveRecord::Schema.define(version: 2020_01_10_144316) do
|
|||
t.boolean "autoclose_referenced_issues"
|
||||
t.string "suggestion_commit_message", limit: 255
|
||||
t.index "lower((name)::text)", name: "index_projects_on_lower_name"
|
||||
t.index ["created_at", "id"], name: "index_projects_api_created_at_id_desc", order: { id: :desc }
|
||||
t.index ["created_at", "id"], name: "index_projects_api_vis20_created_at", where: "(visibility_level = 20)"
|
||||
t.index ["created_at", "id"], name: "index_projects_api_vis20_created_at_id_desc", order: { id: :desc }, where: "(visibility_level = 20)"
|
||||
t.index ["created_at", "id"], name: "index_projects_on_created_at_and_id"
|
||||
|
@ -3362,9 +3363,10 @@ ActiveRecord::Schema.define(version: 2020_01_10_144316) do
|
|||
t.index ["id"], name: "index_on_id_partial_with_legacy_storage", where: "((storage_version < 2) OR (storage_version IS NULL))"
|
||||
t.index ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))"
|
||||
t.index ["id"], name: "index_projects_on_mirror_and_mirror_trigger_builds_both_true", where: "((mirror IS TRUE) AND (mirror_trigger_builds IS TRUE))"
|
||||
t.index ["last_activity_at", "id"], name: "index_projects_api_last_activity_at_id_desc", order: { id: :desc }
|
||||
t.index ["last_activity_at", "id"], name: "index_projects_api_vis20_last_activity_at", where: "(visibility_level = 20)"
|
||||
t.index ["last_activity_at", "id"], name: "index_projects_api_vis20_last_activity_at_id_desc", order: { id: :desc }, where: "(visibility_level = 20)"
|
||||
t.index ["last_activity_at"], name: "index_projects_on_last_activity_at"
|
||||
t.index ["last_activity_at", "id"], name: "index_projects_on_last_activity_at_and_id"
|
||||
t.index ["last_repository_check_at"], name: "index_projects_on_last_repository_check_at", where: "(last_repository_check_at IS NOT NULL)"
|
||||
t.index ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed"
|
||||
t.index ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at"
|
||||
|
@ -3372,13 +3374,16 @@ ActiveRecord::Schema.define(version: 2020_01_10_144316) do
|
|||
t.index ["marked_for_deletion_by_user_id"], name: "index_projects_on_marked_for_deletion_by_user_id", where: "(marked_for_deletion_by_user_id IS NOT NULL)"
|
||||
t.index ["mirror_last_successful_update_at"], name: "index_projects_on_mirror_last_successful_update_at"
|
||||
t.index ["mirror_user_id"], name: "index_projects_on_mirror_user_id"
|
||||
t.index ["name", "id"], name: "index_projects_api_name_id_desc", order: { id: :desc }
|
||||
t.index ["name", "id"], name: "index_projects_api_vis20_name", where: "(visibility_level = 20)"
|
||||
t.index ["name", "id"], name: "index_projects_api_vis20_name_id_desc", order: { id: :desc }, where: "(visibility_level = 20)"
|
||||
t.index ["name", "id"], name: "index_projects_on_name_and_id"
|
||||
t.index ["name"], name: "index_projects_on_name_trigram", opclass: :gin_trgm_ops, using: :gin
|
||||
t.index ["namespace_id"], name: "index_projects_on_namespace_id"
|
||||
t.index ["path", "id"], name: "index_projects_api_path_id_desc", order: { id: :desc }
|
||||
t.index ["path", "id"], name: "index_projects_api_vis20_path", where: "(visibility_level = 20)"
|
||||
t.index ["path", "id"], name: "index_projects_api_vis20_path_id_desc", order: { id: :desc }, where: "(visibility_level = 20)"
|
||||
t.index ["path"], name: "index_projects_on_path"
|
||||
t.index ["path", "id"], name: "index_projects_on_path_and_id"
|
||||
t.index ["path"], name: "index_projects_on_path_trigram", opclass: :gin_trgm_ops, using: :gin
|
||||
t.index ["pending_delete"], name: "index_projects_on_pending_delete"
|
||||
t.index ["pool_repository_id"], name: "index_projects_on_pool_repository_id", where: "(pool_repository_id IS NOT NULL)"
|
||||
|
@ -3387,8 +3392,10 @@ ActiveRecord::Schema.define(version: 2020_01_10_144316) do
|
|||
t.index ["runners_token"], name: "index_projects_on_runners_token"
|
||||
t.index ["runners_token_encrypted"], name: "index_projects_on_runners_token_encrypted"
|
||||
t.index ["star_count"], name: "index_projects_on_star_count"
|
||||
t.index ["updated_at", "id"], name: "index_projects_api_updated_at_id_desc", order: { id: :desc }
|
||||
t.index ["updated_at", "id"], name: "index_projects_api_vis20_updated_at", where: "(visibility_level = 20)"
|
||||
t.index ["updated_at", "id"], name: "index_projects_api_vis20_updated_at_id_desc", order: { id: :desc }, where: "(visibility_level = 20)"
|
||||
t.index ["updated_at", "id"], name: "index_projects_on_updated_at_and_id"
|
||||
end
|
||||
|
||||
create_table "prometheus_alert_events", force: :cascade do |t|
|
||||
|
|
|
@ -209,6 +209,36 @@ I, [2020-01-13T19:01:17.091Z #11056] INFO -- : {"message"=>"Message", "project_
|
|||
{:severity=>"INFO", :time=>"2020-01-13T11:06:09.851Z", :correlation_id=>"d7e0886f096db9a8526a4f89da0e45f6", :message=>"This is my message", :project_id=>123}
|
||||
```
|
||||
|
||||
### Logging context metadata (through Rails or Grape requests)
|
||||
|
||||
`Gitlab::ApplicationContext` stores metadata in a request
|
||||
lifecycle, which can then be added to the web request
|
||||
or Sidekiq logs.
|
||||
|
||||
Entry points can be seen at:
|
||||
|
||||
- [`ApplicationController`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/controllers/application_controller.rb)
|
||||
- [External API](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/api.rb)
|
||||
- [Internal API](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/internal/base.rb)
|
||||
|
||||
#### Adding attributes
|
||||
|
||||
When adding new attributes, make sure they're exposed within the context of the entry points above and:
|
||||
|
||||
- Pass them within the hash to the `with_context` (or `push`) method (make sure to pass a Proc if the
|
||||
method or variable shouldn't be evaluated right away)
|
||||
- Change `Gitlab::ApplicationContext` to accept these new values
|
||||
- Make sure the new attributes are accepted at [`Labkit::Context`](https://gitlab.com/gitlab-org/labkit-ruby/blob/master/lib/labkit/context.rb)
|
||||
|
||||
See our [HOWTO: Use Sidekiq metadata logs](https://www.youtube.com/watch?v=_wDllvO_IY0) for further knowledge on
|
||||
creating visualizations in Kibana.
|
||||
|
||||
**Note:**
|
||||
The fields of the context are currently only logged for Sidekiq jobs triggered
|
||||
through web requests. See the
|
||||
[follow-up work](https://gitlab.com/gitlab-com/gl-infra/scalability/issues/68)
|
||||
for more information.
|
||||
|
||||
## Exception Handling
|
||||
|
||||
It often happens that you catch the exception and want to track it.
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
config:
|
||||
target: "{{ $processEnvironment.HOST_URL }}"
|
||||
http:
|
||||
pool: 10 # All HTTP requests from all virtual users will be sent over the same <pool> connections.
|
||||
# This also means that there is a limit on the number of requests sent per second.
|
||||
phases:
|
||||
- duration: 30
|
||||
arrivalRate: 10
|
||||
name: "Warm up"
|
||||
- duration: 90
|
||||
arrivalRate: 10
|
||||
rampTo: 100
|
||||
name: "Gradual ramp up"
|
||||
- duration: 90
|
||||
arrivalRate: 100
|
||||
name: "Sustained max load"
|
||||
scenarios:
|
||||
- name: "Visit large issue url"
|
||||
flow:
|
||||
- get:
|
||||
url: "{{ $processEnvironment.LARGE_ISSUE_URL }}"
|
||||
- name: "Visit large MR url"
|
||||
flow:
|
||||
- get:
|
||||
url: "{{ $processEnvironment.LARGE_MR_URL }}"
|
|
@ -26,8 +26,8 @@ module QA
|
|||
wait_for_requests
|
||||
end
|
||||
|
||||
def wait(max: 60, interval: 0.1, reload: true, raise_on_failure: false)
|
||||
Support::Waiter.wait_until(max_duration: max, sleep_interval: interval, raise_on_failure: raise_on_failure) do
|
||||
def wait_until(max_duration: 60, sleep_interval: 0.1, reload: true, raise_on_failure: false)
|
||||
Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval, raise_on_failure: raise_on_failure) do
|
||||
yield || (reload && refresh && false)
|
||||
end
|
||||
end
|
||||
|
@ -71,7 +71,7 @@ module QA
|
|||
xhr.send();
|
||||
JS
|
||||
|
||||
return false unless wait(interval: 0.5, max: 60, reload: false) do
|
||||
return false unless wait_until(sleep_interval: 0.5, max_duration: 60, reload: false) do
|
||||
page.evaluate_script('xhr.readyState == XMLHttpRequest.DONE')
|
||||
end
|
||||
|
||||
|
@ -115,8 +115,8 @@ module QA
|
|||
end
|
||||
|
||||
# replace with (..., page = self.class)
|
||||
def click_element(name, page = nil, text: nil)
|
||||
find_element(name, text: text).click
|
||||
def click_element(name, page = nil, text: nil, wait: Capybara.default_max_wait_time)
|
||||
find_element(name, text: text, wait: wait).click
|
||||
page.validate_elements_present! if page
|
||||
end
|
||||
|
||||
|
@ -161,10 +161,10 @@ module QA
|
|||
page.has_text?(text, wait: wait)
|
||||
end
|
||||
|
||||
def has_no_text?(text)
|
||||
def has_no_text?(text, wait: Capybara.default_max_wait_time)
|
||||
wait_for_requests
|
||||
|
||||
page.has_no_text? text
|
||||
page.has_no_text?(text, wait: wait)
|
||||
end
|
||||
|
||||
def has_normalized_ws_text?(text, wait: Capybara.default_max_wait_time)
|
||||
|
@ -191,7 +191,7 @@ module QA
|
|||
# This loop gives time for the img tags to be rendered and for
|
||||
# images to start loading.
|
||||
previous_total_images = 0
|
||||
wait(interval: 1) do
|
||||
wait_until(sleep_interval: 1) do
|
||||
current_total_images = all("img").size
|
||||
result = previous_total_images == current_total_images
|
||||
previous_total_images = current_total_images
|
||||
|
|
|
@ -26,7 +26,7 @@ module QA
|
|||
private
|
||||
|
||||
def completed?(timeout: 60)
|
||||
wait(reload: false, max: timeout) do
|
||||
wait_until(reload: false, max_duration: timeout) do
|
||||
COMPLETED_STATUSES.include?(status_badge)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ module QA
|
|||
private
|
||||
|
||||
def repository_clone_location(kind)
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
click_element :clone_dropdown
|
||||
|
||||
within_element :clone_options do
|
||||
|
|
|
@ -5,9 +5,7 @@ module QA
|
|||
module Component
|
||||
module DropdownFilter
|
||||
def filter_and_select(item)
|
||||
wait(reload: false) do
|
||||
page.has_css?('.dropdown-input-field')
|
||||
end
|
||||
page.has_css?('.dropdown-input-field', wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
|
||||
find('.dropdown-input-field').set(item)
|
||||
click_link item
|
||||
|
|
|
@ -23,7 +23,7 @@ module QA
|
|||
page.attach_file(attachment, class: 'dz-hidden-input', make_visible: field_style)
|
||||
|
||||
# Wait for link to be appended to dropzone text
|
||||
page.wait(reload: false) do
|
||||
page.wait_until(reload: false) do
|
||||
page.find("#{container} textarea").value.match(filename)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,9 +23,7 @@ module QA
|
|||
# Since we submitted after filtering, the presence of
|
||||
# groups_list_tree_container means we have the complete filtered list
|
||||
# of groups
|
||||
wait(reload: false) do
|
||||
page.has_css?(element_selector_css(:groups_list_tree_container))
|
||||
end
|
||||
has_element?(:groups_list_tree_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
|
||||
# If there are no groups we'll know immediately because we filtered the list
|
||||
return false if page.has_text?('No groups or projects matched your search', wait: 0)
|
||||
|
|
|
@ -30,7 +30,7 @@ module QA
|
|||
private
|
||||
|
||||
def choose_repository_clone(kind, detect_text)
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
click_element :clone_dropdown
|
||||
|
||||
page.within('.clone-options-dropdown') do
|
||||
|
|
|
@ -14,7 +14,7 @@ module QA
|
|||
def commit_changes
|
||||
click_element(:commit_button)
|
||||
|
||||
wait(reload: false, max: 60) do
|
||||
wait_until(reload: false, max_duration: 60) do
|
||||
finished_loading?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,7 +58,7 @@ module QA
|
|||
QA::Support::Retrier.retry_on_exception(sleep_interval: 1.0) do
|
||||
within_element(:new_project_or_subgroup_dropdown) do
|
||||
# May need to click again because it is possible to click the button quicker than the JS is bound
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
click_element :new_project_or_subgroup_dropdown_toggle
|
||||
|
||||
has_element?(kind)
|
||||
|
|
|
@ -73,7 +73,7 @@ module QA
|
|||
end
|
||||
|
||||
def add_comment_to_diff(text)
|
||||
wait(interval: 5) do
|
||||
wait_until(sleep_interval: 5) do
|
||||
has_text?("No newline at end of file")
|
||||
end
|
||||
all_elements(:new_diff_line, minimum: 1).first.hover
|
||||
|
@ -140,12 +140,12 @@ module QA
|
|||
|
||||
def mark_to_squash
|
||||
# The squash checkbox is disabled on load
|
||||
wait do
|
||||
wait_until do
|
||||
has_element?(:squash_checkbox)
|
||||
end
|
||||
|
||||
# The squash checkbox is enabled via JS
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
!find_element(:squash_checkbox).disabled?
|
||||
end
|
||||
|
||||
|
@ -164,30 +164,30 @@ module QA
|
|||
|
||||
def ready_to_merge?
|
||||
# The merge button is disabled on load
|
||||
wait do
|
||||
wait_until do
|
||||
has_element?(:merge_button)
|
||||
end
|
||||
|
||||
# The merge button is enabled via JS
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
!find_element(:merge_button).disabled?
|
||||
end
|
||||
end
|
||||
|
||||
def rebase!
|
||||
# The rebase button is disabled on load
|
||||
wait do
|
||||
wait_until do
|
||||
has_element?(:mr_rebase_button)
|
||||
end
|
||||
|
||||
# The rebase button is enabled via JS
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
!find_element(:mr_rebase_button).disabled?
|
||||
end
|
||||
|
||||
click_element :mr_rebase_button
|
||||
|
||||
success = wait do
|
||||
success = wait_until do
|
||||
has_text?('Fast-forward merge without a merge commit')
|
||||
end
|
||||
|
||||
|
@ -209,7 +209,7 @@ module QA
|
|||
end
|
||||
|
||||
def wait_for_merge_request_error_message
|
||||
wait(max: 30, reload: false) do
|
||||
wait_until(max_duration: 30, reload: false) do
|
||||
has_element?(:merge_request_error_content)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ module QA
|
|||
end
|
||||
|
||||
def has_no_branch?(branch_name, reload: false)
|
||||
wait(reload: reload) do
|
||||
wait_until(reload: reload) do
|
||||
within_element(:all_branches) do
|
||||
has_no_element?(:branch_name, text: branch_name)
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ module QA
|
|||
private
|
||||
|
||||
def within_repo_path(full_path)
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
has_element?(:project_import_row, text: full_path)
|
||||
end
|
||||
|
||||
|
@ -67,7 +67,7 @@ module QA
|
|||
end
|
||||
|
||||
def wait_for_success
|
||||
wait(max: 60, interval: 1.0, reload: false) do
|
||||
wait_until(max_duration: 60, sleep_interval: 1.0, reload: false) do
|
||||
page.has_content?('Done', wait: 1.0)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,9 +89,7 @@ module QA
|
|||
end
|
||||
|
||||
def has_comment?(comment_text)
|
||||
wait(reload: false) do
|
||||
has_element?(:noteable_note_item, text: comment_text)
|
||||
end
|
||||
has_element?(:noteable_note_item, text: comment_text, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
end
|
||||
|
||||
def more_assignees_link
|
||||
|
@ -155,7 +153,7 @@ module QA
|
|||
|
||||
def wait_assignees_block_finish_loading
|
||||
within_element(:assignee_block) do
|
||||
wait(reload: false, max: 10, interval: 1) do
|
||||
wait_until(reload: false, max_duration: 10, sleep_interval: 1) do
|
||||
finished_loading_block?
|
||||
yield
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ module QA::Page
|
|||
def output(wait: 5)
|
||||
result = ''
|
||||
|
||||
wait(reload: false, max: wait, interval: 1) do
|
||||
wait_until(reload: false, max_duration: wait, sleep_interval: 1) do
|
||||
result = find_element(:job_log_content).text
|
||||
|
||||
result.include?('Job')
|
||||
|
@ -36,7 +36,7 @@ module QA::Page
|
|||
private
|
||||
|
||||
def loaded?(wait: 60)
|
||||
wait(reload: true, max: wait, interval: 1) do
|
||||
wait_until(reload: true, max_duration: wait, sleep_interval: 1) do
|
||||
has_element?(:job_log_content, wait: 1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,9 +11,7 @@ module QA
|
|||
end
|
||||
|
||||
def click_environment_link(environment_name)
|
||||
wait(reload: false) do
|
||||
find(element_selector_css(:environment_link), text: environment_name).click
|
||||
end
|
||||
click_element(:environment_link, text: environment_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module QA::Page
|
|||
end
|
||||
|
||||
def wait_for_latest_pipeline_success
|
||||
wait(reload: false, max: 300) do
|
||||
wait_until(reload: false, max_duration: 300) do
|
||||
within_element_by_index(:pipeline_commit_status, 0) do
|
||||
has_text?('passed')
|
||||
end
|
||||
|
|
|
@ -67,13 +67,7 @@ module QA::Page
|
|||
end
|
||||
|
||||
def click_on_first_job
|
||||
css = '.js-pipeline-graph-job-link'
|
||||
|
||||
wait(reload: false) do
|
||||
has_css?(css)
|
||||
end
|
||||
|
||||
first(css).click
|
||||
first('.js-pipeline-graph-job-link', wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).click
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module QA
|
|||
private
|
||||
|
||||
def toggle_masked(masked_node, masked)
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
masked_node.click
|
||||
|
||||
masked ? masked_enabled?(masked_node) : masked_disabled?(masked_node)
|
||||
|
|
|
@ -56,9 +56,7 @@ module QA
|
|||
private
|
||||
|
||||
def within_project_deploy_keys
|
||||
wait(reload: false) do
|
||||
has_element?(:project_deploy_keys)
|
||||
end
|
||||
has_element?(:project_deploy_keys, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
|
||||
within_element(:project_deploy_keys) do
|
||||
yield
|
||||
|
|
|
@ -51,9 +51,7 @@ module QA
|
|||
private
|
||||
|
||||
def within_new_project_deploy_token
|
||||
wait(reload: false) do
|
||||
has_css?(element_selector_css(:created_deploy_token_section))
|
||||
end
|
||||
has_element?(:created_deploy_token_section, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
|
||||
within_element(:created_deploy_token_section) do
|
||||
yield
|
||||
|
|
|
@ -77,9 +77,7 @@ module QA
|
|||
|
||||
# The host key detection process is interrupted if we navigate away
|
||||
# from the page before the fingerprint appears.
|
||||
wait(max: 5) do
|
||||
find_element(:fingerprints_list).has_text? /.*/
|
||||
end
|
||||
find_element(:fingerprints_list, text: /.*/)
|
||||
end
|
||||
|
||||
def mirror_repository
|
||||
|
@ -100,7 +98,7 @@ module QA
|
|||
sleep 5
|
||||
refresh
|
||||
|
||||
wait(interval: 1) do
|
||||
wait_until(sleep_interval: 1) do
|
||||
within_element_by_index(:mirrored_repository_row, row_index) do
|
||||
last_update = find_element(:mirror_last_update_at_cell, wait: 0)
|
||||
last_update.has_text?('just now') || last_update.has_text?('seconds')
|
||||
|
@ -117,7 +115,7 @@ module QA
|
|||
private
|
||||
|
||||
def find_repository_row_index(target_url)
|
||||
wait(max: 5, reload: false) do
|
||||
wait_until(max_duration: 5, reload: false) do
|
||||
all_elements(:mirror_repository_url_cell, minimum: 1).index do |url|
|
||||
# The url might be a sanitized url but the target_url won't be so
|
||||
# we compare just the paths instead of the full url
|
||||
|
|
|
@ -46,7 +46,7 @@ module QA
|
|||
end
|
||||
|
||||
def protect_branch
|
||||
click_element :protect_button
|
||||
click_element(:protect_button, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -61,9 +61,7 @@ module QA
|
|||
end
|
||||
|
||||
def wait_for_viewers_to_load
|
||||
wait(reload: false) do
|
||||
has_no_element?(:spinner)
|
||||
end
|
||||
has_no_element?(:spinner, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
end
|
||||
|
||||
def create_first_new_file!
|
||||
|
@ -103,7 +101,7 @@ module QA
|
|||
end
|
||||
|
||||
def new_merge_request
|
||||
wait(reload: true) do
|
||||
wait_until(reload: true) do
|
||||
has_css?(element_selector_css(:create_merge_request))
|
||||
end
|
||||
|
||||
|
@ -127,7 +125,7 @@ module QA
|
|||
end
|
||||
|
||||
def wait_for_import
|
||||
wait(reload: true) do
|
||||
wait_until(reload: true) do
|
||||
has_css?('.tree-holder')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ module QA
|
|||
# Wait for the modal to fade out too
|
||||
has_no_element?(:new_file_modal)
|
||||
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
within_element(:file_templates_bar) do
|
||||
click_element :file_template_dropdown
|
||||
fill_element :dropdown_filter_input, template
|
||||
|
@ -97,7 +97,7 @@ module QA
|
|||
#
|
||||
# Wait for the animation to complete before clicking :commit_button
|
||||
# otherwise the click will quietly do nothing.
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
has_no_element?(:begin_commit_button) &&
|
||||
has_element?(:commit_button)
|
||||
end
|
||||
|
@ -112,7 +112,7 @@ module QA
|
|||
click_element(:commit_to_current_branch_radio) if has_element?(:commit_to_current_branch_radio)
|
||||
click_element(:commit_button) if has_element?(:commit_button)
|
||||
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
has_text?('Your changes have been committed')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ module QA
|
|||
def expand_section(element_name)
|
||||
within_element(element_name) do
|
||||
# Because it is possible to click the button before the JS toggle code is bound
|
||||
wait(reload: false) do
|
||||
wait_until(reload: false) do
|
||||
click_button 'Expand' unless has_css?('button', text: 'Collapse', wait: 1)
|
||||
|
||||
has_content?('Collapse')
|
||||
|
|
|
@ -11,7 +11,7 @@ module QA
|
|||
elements.each do |element|
|
||||
next unless element.required?
|
||||
|
||||
unless base_page.wait(reload: false) { base_page.has_element?(element.name, wait: 15) }
|
||||
unless base_page.has_element?(element.name, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
raise Validatable::PageValidationError, "#{element.name} did not appear on #{self.name} as expected"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,7 +71,7 @@ module QA
|
|||
|
||||
Support::Retrier.retry_until do
|
||||
visit(web_url)
|
||||
wait { current_url.include?(URI.parse(web_url).path.split('/').last || web_url) }
|
||||
wait_until { current_url.include?(URI.parse(web_url).path.split('/').last || web_url) }
|
||||
end
|
||||
|
||||
# Wait until the new page is ready for us to interact with it
|
||||
|
@ -82,8 +82,8 @@ module QA
|
|||
attributes.each(&method(:public_send))
|
||||
end
|
||||
|
||||
def wait(max: 60, interval: 0.1)
|
||||
QA::Support::Waiter.wait(max: max, interval: interval) do
|
||||
def wait_until(max_duration: 60, sleep_interval: 0.1)
|
||||
QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ module QA
|
|||
end
|
||||
|
||||
# Ensure that the group was actually created
|
||||
group_show.wait(interval: 1) do
|
||||
group_show.wait_until(sleep_interval: 1) do
|
||||
group_show.has_text?(path) &&
|
||||
group_show.has_new_project_or_subgroup_dropdown?
|
||||
end
|
||||
|
|
|
@ -49,11 +49,6 @@ module QA
|
|||
page.select_branch(branch_name)
|
||||
page.select_allowed_to_merge(allowed_to_merge)
|
||||
page.select_allowed_to_push(allowed_to_push)
|
||||
|
||||
page.wait(reload: false) do
|
||||
!page.first('.btn-success').disabled?
|
||||
end
|
||||
|
||||
page.protect_branch
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,7 +33,7 @@ module QA
|
|||
|
||||
is_enabled = false
|
||||
|
||||
QA::Support::Waiter.wait(interval: 1) do
|
||||
QA::Support::Waiter.wait_until(sleep_interval: 1) do
|
||||
is_enabled = enabled?(key)
|
||||
end
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ module QA
|
|||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform do |show|
|
||||
reopen_issue_button_visible = show.wait(reload: true) do
|
||||
reopen_issue_button_visible = show.wait_until(reload: true) do
|
||||
show.has_element?(:reopen_issue_button, wait: 1.0)
|
||||
end
|
||||
expect(reopen_issue_button_visible).to be_truthy
|
||||
|
|
|
@ -16,7 +16,7 @@ module QA
|
|||
super
|
||||
end
|
||||
|
||||
def wait(max: 60, interval: 0.1, reload: true)
|
||||
def wait_until(max_duration: 60, sleep_interval: 0.1, reload: true)
|
||||
log("next wait uses reload: #{reload}")
|
||||
# Logging of wait start/end/duration is handled by QA::Support::Waiter
|
||||
|
||||
|
@ -119,10 +119,10 @@ module QA
|
|||
found
|
||||
end
|
||||
|
||||
def has_no_text?(text)
|
||||
def has_no_text?(text, **kwargs)
|
||||
found = super
|
||||
|
||||
log(%Q{has_no_text?('#{text}') returned #{found}})
|
||||
log(%Q{has_no_text?('#{text}', wait: #{kwargs[:wait] || Capybara.default_max_wait_time}) returned #{found}})
|
||||
|
||||
found
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module QA
|
|||
module_function
|
||||
|
||||
def wait_for_requests
|
||||
Waiter.wait do
|
||||
Waiter.wait_until do
|
||||
finished_all_ajax_requests? && finished_all_axios_requests?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,12 +7,6 @@ module QA
|
|||
|
||||
module_function
|
||||
|
||||
def wait(max: singleton_class::DEFAULT_MAX_WAIT_TIME, interval: 0.1)
|
||||
wait_until(max_duration: max, sleep_interval: interval, raise_on_failure: false) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def wait_until(max_duration: singleton_class::DEFAULT_MAX_WAIT_TIME, reload_page: nil, sleep_interval: 0.1, raise_on_failure: false, retry_on_exception: false)
|
||||
QA::Runtime::Logger.debug(
|
||||
<<~MSG.tr("\n", ' ')
|
||||
|
|
2
qa/qa/vendor/jenkins/page/new_credentials.rb
vendored
2
qa/qa/vendor/jenkins/page/new_credentials.rb
vendored
|
@ -39,7 +39,7 @@ module QA
|
|||
end
|
||||
|
||||
def wait_for_page_to_load
|
||||
QA::Support::Waiter.wait(interval: 1.0) do
|
||||
QA::Support::Waiter.wait_until(sleep_interval: 1.0) do
|
||||
page.has_css?('.setting-name', text: "Description")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
describe QA::Page::Base do
|
||||
describe 'page helpers' do
|
||||
it 'exposes helpful page helpers' do
|
||||
expect(subject).to respond_to :refresh, :wait, :scroll_to
|
||||
expect(subject).to respond_to :refresh, :wait_until, :scroll_to
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,11 +69,11 @@ describe QA::Page::Base do
|
|||
it 'does not refresh' do
|
||||
expect(subject).not_to receive(:refresh)
|
||||
|
||||
subject.wait(max: 0.01, raise_on_failure: false) { true }
|
||||
subject.wait_until(max_duration: 0.01, raise_on_failure: false) { true }
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.wait(max: 0.1, raise_on_failure: false) { true }).to be_truthy
|
||||
expect(subject.wait_until(max_duration: 0.1, raise_on_failure: false) { true }).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -81,13 +81,13 @@ describe QA::Page::Base do
|
|||
it 'refreshes' do
|
||||
expect(subject).to receive(:refresh).at_least(:once)
|
||||
|
||||
subject.wait(max: 0.01, raise_on_failure: false) { false }
|
||||
subject.wait_until(max_duration: 0.01, raise_on_failure: false) { false }
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
allow(subject).to receive(:refresh)
|
||||
|
||||
expect(subject.wait(max: 0.01, raise_on_failure: false) { false }).to be_falsey
|
||||
expect(subject.wait_until(max_duration: 0.01, raise_on_failure: false) { false }).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,20 +28,20 @@ describe QA::Support::Page::Logging do
|
|||
end
|
||||
|
||||
it 'logs wait' do
|
||||
expect { subject.wait(max: 0) {} }
|
||||
expect { subject.wait_until(max_duration: 0) {} }
|
||||
.to output(/next wait uses reload: true/).to_stdout_from_any_process
|
||||
expect { subject.wait(max: 0) {} }
|
||||
expect { subject.wait_until(max_duration: 0) {} }
|
||||
.to output(/with wait_until/).to_stdout_from_any_process
|
||||
expect { subject.wait(max: 0) {} }
|
||||
expect { subject.wait_until(max_duration: 0) {} }
|
||||
.to output(/ended wait_until$/).to_stdout_from_any_process
|
||||
end
|
||||
|
||||
it 'logs wait with reload false' do
|
||||
expect { subject.wait(max: 0, reload: false) {} }
|
||||
expect { subject.wait_until(max_duration: 0, reload: false) {} }
|
||||
.to output(/next wait uses reload: false/).to_stdout_from_any_process
|
||||
expect { subject.wait(max: 0, reload: false) {} }
|
||||
expect { subject.wait_until(max_duration: 0, reload: false) {} }
|
||||
.to output(/with wait_until/).to_stdout_from_any_process
|
||||
expect { subject.wait(max: 0, reload: false) {} }
|
||||
expect { subject.wait_until(max_duration: 0, reload: false) {} }
|
||||
.to output(/ended wait_until$/).to_stdout_from_any_process
|
||||
end
|
||||
|
||||
|
@ -121,10 +121,10 @@ describe QA::Support::Page::Logging do
|
|||
end
|
||||
|
||||
it 'logs has_no_text?' do
|
||||
allow(page).to receive(:has_no_text?).with('foo').and_return(true)
|
||||
allow(page).to receive(:has_no_text?).with('foo', any_args).and_return(true)
|
||||
|
||||
expect { subject.has_no_text? 'foo' }
|
||||
.to output(/has_no_text\?\('foo'\) returned true/).to_stdout_from_any_process
|
||||
.to output(/has_no_text\?\('foo', wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned true/).to_stdout_from_any_process
|
||||
end
|
||||
|
||||
it 'logs finished_loading?' do
|
||||
|
|
|
@ -36,7 +36,8 @@
|
|||
"real_path": { "type": "string" },
|
||||
"issue_sidebar_endpoint": { "type": "string" },
|
||||
"toggle_subscription_endpoint": { "type": "string" },
|
||||
"assignable_labels_endpoint": { "type": "string" }
|
||||
"assignable_labels_endpoint": { "type": "string" },
|
||||
"blocked": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
|
@ -1475,7 +1475,11 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
|
||||
describe '#index_exists_by_name?' do
|
||||
it 'returns true if an index exists' do
|
||||
expect(model.index_exists_by_name?(:projects, 'index_projects_on_path'))
|
||||
ActiveRecord::Base.connection.execute(
|
||||
'CREATE INDEX test_index_for_index_exists ON projects (path);'
|
||||
)
|
||||
|
||||
expect(model.index_exists_by_name?(:projects, 'test_index_for_index_exists'))
|
||||
.to be_truthy
|
||||
end
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ issues:
|
|||
- vulnerability_links
|
||||
- related_vulnerabilities
|
||||
- user_mentions
|
||||
- blocked_by_issue_links
|
||||
- blocked_by_issues
|
||||
events:
|
||||
- author
|
||||
- project
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe IssueBoardEntity do
|
||||
let(:project) { create(:project) }
|
||||
let(:resource) { create(:issue, project: project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:milestone) { create(:milestone, project: project) }
|
||||
let(:label) { create(:label, project: project, title: 'Test Label') }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:resource) { create(:issue, project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:label) { create(:label, project: project, title: 'Test Label') }
|
||||
let(:request) { double('request', current_user: user) }
|
||||
|
||||
subject { described_class.new(resource, request: request).as_json }
|
||||
|
|
Loading…
Reference in a new issue