Commit 6c01376c authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'ee/master' into ce-to-ee-2017-11-03

* ee/master: (192 commits)
  fix flaky tests by removing unneeded clicks and focus actions
  explicitly close select2 dropdown after use
  fix flaky test within protected_branches_access_control.rb
  fix flaky test in gfm_autocomplete_spec.rb
  Resolve "Add group actions to Audit events"
  replace calls to .trigger("click") with .click
  don't attempt to click on hidden elements
  replace use of unsupported trigger method with click
  prevent race condition when attempting to cancel a file upload
  confirm javascript modal when resetting email tokens
  Update CHANGELOG.md for 10.0.5
  Update CHANGELOG-EE.md for 10.0.5-ee
  Add reconfigure logs location to docs
  exclude issuable author from approvers autocomplete list
  close dropdown after selection
  close dropdown by clicking on page body after selection
  fix reference to hidden element
  fix broken click actions
  fix broken references to page.status_code
  add accept_confirm around actions which require it
  ...
parents ad1608c4 807f4b16
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-phantomjs-2.1-node-8.x-yarn-1.0-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-62.0-node-8.x-yarn-1.2-postgresql-9.6"
.default-cache: &default-cache .default-cache: &default-cache
key: "ruby-235-with-yarn" key: "ruby-235-with-yarn"
...@@ -24,7 +24,6 @@ variables: ...@@ -24,7 +24,6 @@ variables:
SIMPLECOV: "true" SIMPLECOV: "true"
GIT_DEPTH: "20" GIT_DEPTH: "20"
GIT_SUBMODULE_STRATEGY: "none" GIT_SUBMODULE_STRATEGY: "none"
PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3" GET_SOURCES_ATTEMPTS: "3"
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json
...@@ -576,7 +575,6 @@ karma: ...@@ -576,7 +575,6 @@ karma:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache <<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-61.0-node-8.x-yarn-1.0-postgresql-9.6"
stage: test stage: test
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
......
...@@ -42,6 +42,14 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -42,6 +42,14 @@ Please view this file on the master branch, on stable branches it's out of date.
- [OTHER] Add partial index on push_rules.is_sample. - [OTHER] Add partial index on push_rules.is_sample.
- Add new push rule to reject unsigned commits. !2913 - Add new push rule to reject unsigned commits. !2913
## 10.0.5 (2017-11-03)
- [FIXED] Find stuck scheduled import jobs and also mark them as failed. !3055
- [FIXED] Fix removing the username from the git repository URL for pull mirroring. !3060
- [FIXED] Fix base link for issues on group boards.
- [FIXED] Move group boards routes under - and remove "boards" from reserved paths.
- [FIXED] Geo: Fix attachments/avatars saving to the wrong directory.
## 10.0.4 (2017-10-16) ## 10.0.4 (2017-10-16)
- [SECURITY] Prevent Related Issues from leaking confidential issues. !541 - [SECURITY] Prevent Related Issues from leaking confidential issues. !541
......
...@@ -219,6 +219,24 @@ entry. ...@@ -219,6 +219,24 @@ entry.
- creation of keys moved to services. !13331 (haseebeqx) - creation of keys moved to services. !13331 (haseebeqx)
- Add username as GL_USERNAME in hooks. - Add username as GL_USERNAME in hooks.
## 10.0.5 (2017-11-03)
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
- [FIXED] Fix `rake gitlab:incoming_email:check` and make it report the actual error. !14423
- [FIXED] Does not check if an invariant hashed storage path exists on disk when renaming projects. !14428
- [FIXED] Fix bottom spacing for dropdowns that open upwards. !14535
- [FIXED] Fix the project import with issues and milestones. !14657
- [FIXED] Fix broken Y-axis scaling in some Prometheus graphs. !14693
- [FIXED] Fixed duplicate notifications when added multiple labels on an issue. !14798
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
- [FIXED] Fixed issue/merge request breadcrumb titles not having links.
- [FIXED] Fix application setting to cache nil object.
- [FIXED] Fix missing Import/Export issue assignees.
- [FIXED] Allow boards as top level route.
- [FIXED] Fixed milestone breadcrumb links.
- [FIXED] Fixed merge request widget merged & closed date tooltip text.
- [FIXED] fix merge request widget status icon for failed CI.
## 10.0.4 (2017-10-16) ## 10.0.4 (2017-10-16)
- [SECURITY] Move project repositories between namespaces when renaming users. - [SECURITY] Move project repositories between namespaces when renaming users.
......
...@@ -336,9 +336,9 @@ group :development, :test do ...@@ -336,9 +336,9 @@ group :development, :test do
# Generate Fake data # Generate Fake data
gem 'ffaker', '~> 2.4' gem 'ffaker', '~> 2.4'
gem 'capybara', '~> 2.15.0' gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0' gem 'selenium-webdriver', '~> 3.5'
gem 'spring', '~> 2.0.0' gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-rspec', '~> 1.0.4'
......
...@@ -121,12 +121,13 @@ GEM ...@@ -121,12 +121,13 @@ GEM
mime-types (>= 1.16) mime-types (>= 1.16)
cause (0.1) cause (0.1)
charlock_holmes (0.7.5) charlock_holmes (0.7.5)
childprocess (0.7.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2) chronic (0.10.2)
chronic_duration (0.10.6) chronic_duration (0.10.6)
numerizer (~> 0.1.1) numerizer (~> 0.1.1)
chunky_png (1.3.5) chunky_png (1.3.5)
citrus (3.0.2) citrus (3.0.2)
cliver (0.3.2)
coderay (1.1.1) coderay (1.1.1)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
...@@ -633,11 +634,6 @@ GEM ...@@ -633,11 +634,6 @@ GEM
pg (0.18.4) pg (0.18.4)
po_to_json (1.0.1) po_to_json (1.0.1)
json (>= 1.6.0) json (>= 1.6.0)
poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
posix-spawn (0.3.13) posix-spawn (0.3.13)
powerpack (0.1.1) powerpack (0.1.1)
premailer (1.10.4) premailer (1.10.4)
...@@ -847,6 +843,9 @@ GEM ...@@ -847,6 +843,9 @@ GEM
activesupport (>= 3.1) activesupport (>= 3.1)
select2-rails (3.5.9.3) select2-rails (3.5.9.3)
thor (~> 0.14) thor (~> 0.14)
selenium-webdriver (3.5.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
sentry-raven (2.5.3) sentry-raven (2.5.3)
faraday (>= 0.7.6, < 1.0) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
...@@ -979,9 +978,6 @@ GEM ...@@ -979,9 +978,6 @@ GEM
hashdiff hashdiff
webpack-rails (0.9.10) webpack-rails (0.9.10)
railties (>= 3.2.0) railties (>= 3.2.0)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
wikicloth (0.8.1) wikicloth (0.8.1)
builder builder
expression_parser expression_parser
...@@ -1019,7 +1015,7 @@ DEPENDENCIES ...@@ -1019,7 +1015,7 @@ DEPENDENCIES
browser (~> 2.2) browser (~> 2.2)
bullet (~> 5.5.0) bullet (~> 5.5.0)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 2.15.0) capybara (~> 2.15)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 1.2) carrierwave (~> 1.2)
charlock_holmes (~> 0.7.5) charlock_holmes (~> 0.7.5)
...@@ -1142,7 +1138,6 @@ DEPENDENCIES ...@@ -1142,7 +1138,6 @@ DEPENDENCIES
peek-redis (~> 1.2.0) peek-redis (~> 1.2.0)
peek-sidekiq (~> 1.0.3) peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.7) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta18) prometheus-client-mmap (~> 0.7.0.beta18)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
...@@ -1188,6 +1183,7 @@ DEPENDENCIES ...@@ -1188,6 +1183,7 @@ DEPENDENCIES
scss_lint (~> 0.54.0) scss_lint (~> 0.54.0)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
sentry-raven (~> 2.5.3) sentry-raven (~> 2.5.3)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
......
...@@ -79,8 +79,6 @@ const Filter = { ...@@ -79,8 +79,6 @@ const Filter = {
this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown); this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown); this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
this.debounceKeydown({ detail: { hook: this.hook } });
}, },
destroy: function destroy() { destroy: function destroy() {
......
import 'core-js/es6/map'; import 'core-js/es6/map';
import 'core-js/es6/set'; import 'core-js/es6/set';
import simulateDrag from './simulate_drag'; import simulateDrag from './simulate_drag';
import simulateInput from './simulate_input';
// Export to global space for rspec to use // Export to global space for rspec to use
window.simulateDrag = simulateDrag; window.simulateDrag = simulateDrag;
window.simulateInput = simulateInput;
function triggerEvents(input) {
input.dispatchEvent(new Event('keydown'));
input.dispatchEvent(new Event('keypress'));
input.dispatchEvent(new Event('input'));
input.dispatchEvent(new Event('keyup'));
}
export default function simulateInput(target, text) {
const input = document.querySelector(target);
if (!input || !input.matches('textarea, input')) {
return false;
}
if (text.length > 0) {
Array.prototype.forEach.call(text, (char) => {
input.value += char;
triggerEvents(input);
});
} else {
triggerEvents(input);
}
return true;
}
...@@ -484,10 +484,7 @@ ...@@ -484,10 +484,7 @@
height: calc(100vh - #{$header-height}); height: calc(100vh - #{$header-height});
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty
height: calc(100vh - 180px); height: calc(100vh - 180px);
// scss-lint:enable DuplicateProperty
} }
} }
......
...@@ -268,12 +268,6 @@ ...@@ -268,12 +268,6 @@
.filtered-search-box-input-container { .filtered-search-box-input-container {
flex: 1; flex: 1;
position: relative; position: relative;
// Fix PhantomJS not supporting `flex: 1;` properly.
// This is important because it can change the expected `e.target` when clicking things in tests.
// See https://gitlab.com/gitlab-org/gitlab-ce/blob/b54acba8b732688c59fe2f38510c469dc86ee499/spec/features/issues/filtered_search/visual_tokens_spec.rb#L61
// - With `width: 100%`: `e.target` = `.tokens-container`, https://i.imgur.com/jGq7wbx.png
// - Without `width: 100%`: `e.target` = `.filtered-search`, https://i.imgur.com/cNI2CyT.png
width: 100%;
min-width: 0; min-width: 0;
} }
......
...@@ -25,7 +25,10 @@ ...@@ -25,7 +25,10 @@
margin: 0; margin: 0;
padding: $gl-padding 0; padding: $gl-padding 0;
border: none; border: none;
border-bottom: 1px solid $white-normal;
&:not(:last-child) {
border-bottom: 1px solid $white-normal;
}
} }
} }
......
...@@ -117,10 +117,7 @@ ...@@ -117,10 +117,7 @@
white-space: nowrap; white-space: nowrap;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty
height: calc(100vh - 222px); height: calc(100vh - 222px);
// scss-lint:enable DuplicateProperty
min-height: 475px; min-height: 475px;
} }
} }
......
...@@ -469,8 +469,6 @@ ul.notes { ...@@ -469,8 +469,6 @@ ul.notes {
flex-shrink: 0; flex-shrink: 0;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
// For PhantomJS that does not support flex
float: right;
margin-left: 10px; margin-left: 10px;
color: $gray-darkest; color: $gray-darkest;
...@@ -481,7 +479,6 @@ ul.notes { ...@@ -481,7 +479,6 @@ ul.notes {
} }
.more-actions { .more-actions {
float: right; // phantomjs fallback
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
......
...@@ -4,11 +4,6 @@ ...@@ -4,11 +4,6 @@
-ms-transition: none !important; -ms-transition: none !important;
-webkit-transition: none !important; -webkit-transition: none !important;
transition: none !important; transition: none !important;
-o-transform: none !important;
-moz-transform: none !important;
-ms-transform: none !important;
-webkit-transform: none !important;
transform: none !important;
-webkit-animation: none !important; -webkit-animation: none !important;
-moz-animation: none !important; -moz-animation: none !important;
-o-animation: none !important; -o-animation: none !important;
......
...@@ -54,8 +54,6 @@ module MembershipActions ...@@ -54,8 +54,6 @@ module MembershipActions
"You left the \"#{membershipable.human_name}\" #{source_type}." "You left the \"#{membershipable.human_name}\" #{source_type}."
end end
log_audit_event(member, action: :destroy) unless member.request?
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize] redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize]
......
...@@ -27,7 +27,7 @@ AuditEventPresenter < Gitlab::View::Presenter::Simple ...@@ -27,7 +27,7 @@ AuditEventPresenter < Gitlab::View::Presenter::Simple
return nil unless entity return nil unless entity
link_to(details[:entity_path], entity).html_safe link_to(details[:entity_path] || entity.name, entity).html_safe
end end
def date def date
......
module Groups module Groups
class CreateService < Groups::BaseService class CreateService < Groups::BaseService
prepend ::EE::Groups::CreateService
def initialize(user, params = {}) def initialize(user, params = {})
@current_user, @params = user, params.dup @current_user, @params = user, params.dup
@chat_team = @params.delete(:create_chat_team) @chat_team = @params.delete(:create_chat_team)
......
module Groups module Groups
class DestroyService < Groups::BaseService class DestroyService < Groups::BaseService
prepend ::EE::Groups::DestroyService
def async_execute def async_execute
group.soft_delete_without_removing_associations group.soft_delete_without_removing_associations
job_id = GroupDestroyWorker.perform_async(group.id, current_user.id) job_id = GroupDestroyWorker.perform_async(group.id, current_user.id)
......
module Groups module Groups
class UpdateService < Groups::BaseService class UpdateService < Groups::BaseService
include UpdateVisibilityLevel include UpdateVisibilityLevel
prepend ::EE::Groups::UpdateService
def execute def execute
reject_parent_id! reject_parent_id!
......
module Projects module Projects
module GroupLinks module GroupLinks
class CreateService < BaseService class CreateService < BaseService
prepend ::EE::Projects::GroupLinks::CreateService
def execute(group) def execute(group)
return false unless group return false unless group
......
module Projects module Projects
module GroupLinks module GroupLinks
class DestroyService < BaseService class DestroyService < BaseService
prepend ::EE::Projects::GroupLinks::DestroyService
def execute(group_link) def execute(group_link)
return false unless group_link return false unless group_link
group_link.destroy group_link.destroy
end end
end end
......
Merge Request #{@merge_request.to_reference} was approved by #{@approved_by_user} Merge Request #{@merge_request.to_reference} was approved by #{@approved_by.name}
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
Merge Request #{@merge_request.to_reference} was unapproved by #{@unapproved_by_user} Merge Request #{@merge_request.to_reference} was unapproved by #{@unapproved_by.name}
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
cluster_status: @cluster.status_name, cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason } } cluster_status_reason: @cluster.status_reason } }
%section.settings %section.settings.no-animate.expanded
%h4= s_('ClusterIntegration|Enable cluster integration') %h4= s_('ClusterIntegration|Enable cluster integration')
.settings-content.expanded .settings-content
.hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' } .hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
= s_('ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine') = s_('ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine')
...@@ -49,14 +49,14 @@ ...@@ -49,14 +49,14 @@
.form-group .form-group
= field.submit _('Save'), class: 'btn btn-success' = field.submit _('Save'), class: 'btn btn-success'
%section.settings#js-cluster-details %section.settings.no-animate#js-cluster-details{ class: ('expanded' if expanded) }
.settings-header .settings-header
%h4= s_('ClusterIntegration|Cluster details') %h4= s_('ClusterIntegration|Cluster details')
%button.btn.js-settings-toggle %button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand' = expanded ? 'Collapse' : 'Expand'
%p= s_('ClusterIntegration|See and edit the details for your cluster') %p= s_('ClusterIntegration|See and edit the details for your cluster')
.settings-content.no-animate{ class: ('expanded' if expanded) } .settings-content
.form_group.append-bottom-20 .form_group.append-bottom-20
%label.append-bottom-10{ for: 'cluter-name' } %label.append-bottom-10{ for: 'cluter-name' }
...@@ -66,11 +66,11 @@ ...@@ -66,11 +66,11 @@
%span.input-group-addon.clipboard-addon %span.input-group-addon.clipboard-addon
= clipboard_button(text: @cluster.gcp_cluster_name, title: s_('ClusterIntegration|Copy cluster name')) = clipboard_button(text: @cluster.gcp_cluster_name, title: s_('ClusterIntegration|Copy cluster name'))
%section.settings#js-cluster-advanced-settings %section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
.settings-header .settings-header
%h4= _('Advanced settings') %h4= _('Advanced settings')
%button.btn.js-settings-toggle %button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand' = expanded ? 'Collapse' : 'Expand'
%p= s_('ClusterIntegration|Manage Cluster integration on your GitLab project') %p= s_('ClusterIntegration|Manage Cluster integration on your GitLab project')
.settings-content.no-animate{ class: ('expanded' if expanded) } .settings-content
= render 'advanced_settings' = render 'advanced_settings'
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
- return unless issuable.is_a?(MergeRequest) - return unless issuable.is_a?(MergeRequest)
- return unless issuable.requires_approve? - return unless issuable.requires_approve?
- ineligible_approver = issuable.author || current_user
- can_update_approvers = can?(current_user, :update_approvers, issuable) - can_update_approvers = can?(current_user, :update_approvers, issuable)
.form-group .form-group
...@@ -11,7 +12,7 @@ ...@@ -11,7 +12,7 @@
Approvers Approvers
.col-sm-10 .col-sm-10
- if can_update_approvers - if can_update_approvers
= users_select_tag("merge_request[approver_ids]", multiple: true, class: 'input-large', email_user: true, skip_users: issuable.all_approvers_including_groups, project: issuable.target_project) = users_select_tag("merge_request[approver_ids]", multiple: true, class: 'input-large', email_user: true, skip_users: issuable.all_approvers_including_groups << ineligible_approver, project: issuable.target_project)
.help-block .help-block
This merge request must be approved by these users. This merge request must be approved by these users.
You can override the project settings by setting your own list of approvers. You can override the project settings by setting your own list of approvers.
......
---
title: Add group actions in Audit events
merge_request: 3176
author:
type: changed
---
title: Fix (un)approver names not being shown in plaintext emails
merge_request: 3266
author:
type: fixed
--- ---
title: Fix application setting to cache nil object title: Fix double border UI bug on pipelines/environments table and pagination
merge_request: merge_request:
author: author:
type: fixed type: fixed
---
title: Fix missing Import/Export issue assignees
merge_request:
author:
type: fixed
Rails.application.configure do Rails.application.configure do
# Make sure the middleware is inserted first in middleware chain # Make sure the middleware is inserted first in middleware chain
config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestBlockerMiddleware') config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestBlockerMiddleware')
config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestInspectorMiddleware')
# Settings specified here will take precedence over those in config/application.rb # Settings specified here will take precedence over those in config/application.rb
......
...@@ -192,4 +192,13 @@ installations from source. ...@@ -192,4 +192,13 @@ installations from source.
It logs information whenever a [repository check is run][repocheck] on a project. It logs information whenever a [repository check is run][repocheck] on a project.
## Reconfigure Logs
Reconfigure log files live in `/var/log/gitlab/reconfigure` for Omnibus GitLab
packages. Installations from source don't have reconfigure logs. A reconfigure log
is populated whenever `gitlab-ctl reconfigure` is run manually or as part of an upgrade.
Reconfigure logs files are named according to the UNIX timestamp of when the reconfigure
was initiated, such as `1509705644.log`
[repocheck]: repository_checks.md [repocheck]: repository_checks.md
...@@ -41,6 +41,16 @@ module EE ...@@ -41,6 +41,16 @@ module EE
self self
end end
def for_project_group_link(group_link)
@details = custom_project_link_group_attributes(group_link)
.merge(author_name: @author.name,
target_id: group_link.project.id,
target_type: 'Project',
target_details: group_link.project.full_path)
self
end
def for_failed_login def for_failed_login
ip = @details[:ip_address] ip = @details[:ip_address]
auth = @details[:with] || 'STANDARD' auth = @details[:with] || 'STANDARD'
...@@ -97,6 +107,10 @@ module EE ...@@ -97,6 +107,10 @@ module EE
for_custom_model('project', @entity.full_path) for_custom_model('project', @entity.full_path)
end end
def for_group
for_custom_model('group', @entity.full_path)
end
def entity_audit_events_enabled? def entity_audit_events_enabled?
@entity.respond_to?(:feature_available?) && @entity.feature_available?(:audit_events) @entity.respond_to?(:feature_available?) && @entity.feature_available?(:audit_events)
end end
...@@ -167,5 +181,23 @@ module EE ...@@ -167,5 +181,23 @@ module EE
@details.merge!(ip_address: ip_address, @details.merge!(ip_address: ip_address,
entity_path: @entity.full_path) entity_path: @entity.full_path)
end end
def custom_project_link_group_attributes(group_link)
case @details[:action]
when :destroy
{ remove: 'project_access' }
when :create
{
add: 'project_access',
as: group_link.human_access
}
when :update
{
change: 'access_level',
from: @details[:old_access_level],
to: group_link.human_access
}
end
end
end end
end end
module EE
module Groups
module CreateService
def execute
raise NotImplementedError unless defined?(super)
super.tap { |group| log_audit_event if group&.persisted? }
end
private
def log_audit_event
::AuditEventService.new(
current_user,
group,
action: :create
).for_group.security_event
end
end
end
end
module EE
module Groups
module DestroyService
def execute
raise NotImplementedError unless defined?(super)
super.tap { |group| log_audit_event unless group&.persisted? }
end
private
def log_audit_event
::AuditEventService.new(
current_user,
group,
action: :destroy
).for_group.security_event
end
end
end
end
module EE
module Groups
module UpdateService
def execute
raise NotImplementedError unless defined?(super)
super.tap { |success| log_audit_event if success }
end
private
def log_audit_event
EE::Audit::GroupChangesAuditor.new(current_user, group).execute
end
end
end
end
module EE
module Projects
module GroupLinks
module CreateService
def execute(group)
raise NotImplementedError unless defined?(super)
super.tap { |link| log_audit_event(link) if link && link&.persisted? }
end
private
def log_audit_event(group_link)
::AuditEventService.new(
current_user,
group_link.group,
action: :create
).for_project_group_link(group_link).security_event
end
end
end
end
end
module EE
module Projects
module GroupLinks
module DestroyService
def execute(group_link)
raise NotImplementedError unless defined?(super)
super.tap { |link| log_audit_event(link) if link && !link&.persisted? }
end
private
def log_audit_event(group_link)
::AuditEventService.new(
current_user,
group_link.group,
action: :destroy
).for_project_group_link(group_link).security_event
end
end
end
end
end
...@@ -11,7 +11,7 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps ...@@ -11,7 +11,7 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps
end end
step 'I select Mention setting from dropdown' do step 'I select Mention setting from dropdown' do
first(:link, "On mention").trigger('click') first(:link, "On mention").click
end end
step 'I should see Notification saved message' do step 'I should see Notification saved message' do
......
...@@ -40,6 +40,7 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps ...@@ -40,6 +40,7 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
step 'I submit new branch form with invalid name' do step 'I submit new branch form with invalid name' do
fill_in 'branch_name', with: '1.0 stable' fill_in 'branch_name', with: '1.0 stable'
page.find("body").click # defocus the branch_name input
select_branch('master') select_branch('master')
click_button 'Create branch' click_button 'Create branch'
end end
...@@ -70,17 +71,16 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps ...@@ -70,17 +71,16 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
step "I click branch 'improve/awesome' delete link" do step "I click branch 'improve/awesome' delete link" do
page.within '.js-branch-improve\/awesome' do page.within '.js-branch-improve\/awesome' do
find('.btn-remove').click accept_alert { find('.btn-remove').click }
sleep 0.05
end end
end end
step "I should not see branch 'improve/awesome'" do step "I should not see branch 'improve/awesome'" do
expect(page.all(visible: true)).not_to have_content 'improve/awesome' expect(page).to have_css('.js-branch-improve\\/awesome', visible: :hidden)
end end
def select_branch(branch_name) def select_branch(branch_name)
click_button 'master' find('.git-revision-dropdown-toggle').click
page.within '#new-branch-form .dropdown-menu' do page.within '#new-branch-form .dropdown-menu' do
click_link branch_name click_link branch_name
......
...@@ -16,7 +16,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps ...@@ -16,7 +16,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
step 'I delete all labels' do step 'I delete all labels' do
page.within '.labels' do page.within '.labels' do
page.all('.remove-row').each do page.all('.remove-row').each do
first('.remove-row').click accept_confirm { first('.remove-row').click }
end end
end end
end end
......
...@@ -3,6 +3,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps ...@@ -3,6 +3,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedPaths include SharedPaths
include SharedMarkdown include SharedMarkdown
include CapybaraHelpers
step 'I should see milestone "v2.2"' do step 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by(title: "v2.2") milestone = @project.milestones.find_by(title: "v2.2")
...@@ -65,7 +66,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps ...@@ -65,7 +66,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
end end
step 'I click link to remove milestone' do step 'I click link to remove milestone' do
click_link 'Delete' confirm_modal_if_present { click_link 'Delete' }
end end
step 'I should see no milestones' do step 'I should see no milestones' do
......
...@@ -215,7 +215,7 @@ module SharedDiffNote ...@@ -215,7 +215,7 @@ module SharedDiffNote
end end
step 'I click side-by-side diff button' do step 'I click side-by-side diff button' do
find('#parallel-diff-btn').trigger('click') find('#parallel-diff-btn').click
end end
step 'I see side-by-side diff button' do step 'I see side-by-side diff button' do
...@@ -227,12 +227,11 @@ module SharedDiffNote ...@@ -227,12 +227,11 @@ module SharedDiffNote
end end
def click_diff_line(code) def click_diff_line(code)
find(".line_holder[id='#{code}'] td:nth-of-type(1)").trigger 'mouseover' find(".line_holder[id='#{code}'] button").click
find(".line_holder[id='#{code}'] button").trigger 'click'
end end
def click_parallel_diff_line(code, line_type) def click_parallel_diff_line(code, line_type)
find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').trigger 'mouseover' find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').hover
find(".line_holder.parallel button[data-line-code='#{code}']").trigger 'click' find(".line_holder.parallel button[data-line-code='#{code}']").click
end end
end end
...@@ -14,7 +14,7 @@ module SharedNote ...@@ -14,7 +14,7 @@ module SharedNote
find('.more-actions').click find('.more-actions').click
find('.more-actions .dropdown-menu li', match: :first) find('.more-actions .dropdown-menu li', match: :first)
find(".js-note-delete").click accept_confirm { find(".js-note-delete").click }
end end
end end
......
require 'capybara/poltergeist'
require 'capybara-screenshot/spinach' require 'capybara-screenshot/spinach'
# Give CI some extra time # Give CI some extra time
timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30
Capybara.javascript_driver = :poltergeist Capybara.javascript_driver = :chrome
Capybara.register_driver :poltergeist do |app| Capybara.register_driver :chrome do |app|
Capybara::Poltergeist::Driver.new( extra_args = []
app, extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
js_errors: true,
timeout: timeout, capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
window_size: [1366, 768], chromeOptions: {
url_whitelist: %w[localhost 127.0.0.1], 'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args
url_blacklist: %w[.mp4 .png .gif .avi .bmp .jpg .jpeg], }
phantomjs_options: [
'--load-images=yes'
]
) )
Capybara::Selenium::Driver
.new(app, browser: :chrome, desired_capabilities: capabilities)
end end
Capybara.default_max_wait_time = timeout Capybara.default_max_wait_time = timeout
...@@ -24,6 +23,10 @@ Capybara.ignore_hidden_elements = false ...@@ -24,6 +23,10 @@ Capybara.ignore_hidden_elements = false
# Keep only the screenshots generated from the last failing test suite # Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run Capybara::Screenshot.prune_strategy = :keep_last_run
# From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326
Capybara::Screenshot.register_driver(:chrome) do |driver, path|
driver.browser.save_screenshot(path)
end
Spinach.hooks.before_run do Spinach.hooks.before_run do
TestEnv.eager_load_driver_server TestEnv.eager_load_driver_server
......
module CapybaraHelpers
def confirm_modal_if_present
if Capybara.current_driver == Capybara.javascript_driver
accept_confirm { yield }
return
end
yield
end
end
module EE
module Audit
class GroupChangesAuditor < ProjectChangesAuditor
def execute
audit_changes(:visibility_level, as: 'visibility', model: model)
end
end
end
end
...@@ -7,6 +7,7 @@ module Gitlab ...@@ -7,6 +7,7 @@ module Gitlab
class RequestBlockerMiddleware class RequestBlockerMiddleware
@@num_active_requests = Concurrent::AtomicFixnum.new(0) @@num_active_requests = Concurrent::AtomicFixnum.new(0)
@@block_requests = Concurrent::AtomicBoolean.new(false) @@block_requests = Concurrent::AtomicBoolean.new(false)
@@slow_requests = Concurrent::AtomicBoolean.new(false)
# Returns the number of requests the server is currently processing. # Returns the number of requests the server is currently processing.
def self.num_active_requests def self.num_active_requests
...@@ -19,9 +20,15 @@ module Gitlab ...@@ -19,9 +20,15 @@ module Gitlab
@@block_requests.value = true @@block_requests.value = true
end end
# Slows down incoming requests (useful for race conditions).
def self.slow_requests!
@@slow_requests.value = true
end
# Allows the server to accept requests again. # Allows the server to accept requests again.
def self.allow_requests! def self.allow_requests!
@@block_requests.value = false @@block_requests.value = false
@@slow_requests.value = false
end end
def initialize(app) def initialize(app)
...@@ -33,6 +40,7 @@ module Gitlab ...@@ -33,6 +40,7 @@ module Gitlab
if block_requests? if block_requests?
block_request(env) block_request(env)
else else
sleep 0.2 if slow_requests?
@app.call(env) @app.call(env)
end end
ensure ensure
...@@ -45,6 +53,10 @@ module Gitlab ...@@ -45,6 +53,10 @@ module Gitlab
@@block_requests.true? @@block_requests.true?
end end
def slow_requests?
@@slow_requests.true?
end
def block_request(env) def block_request(env)
[503, {}, []] [503, {}, []]
end end
......
# rubocop:disable Style/ClassVars
module Gitlab
module Testing
class RequestInspectorMiddleware
@@log_requests = Concurrent::AtomicBoolean.new(false)
@@logged_requests = Concurrent::Array.new
@@inject_headers = Concurrent::Hash.new
# Resets the current request log and starts logging requests
def self.log_requests!(headers = {})
@@inject_headers.replace(headers)
@@logged_requests.replace([])
@@log_requests.value = true
end
# Stops logging requests
def self.stop_logging!
@@log_requests.value = false
end
def self.requests
@@logged_requests
end
def initialize(app)
@app = app
end
def call(env)
return @app.call(env) unless @@log_requests.true?
url = env['REQUEST_URI']
env.merge! http_headers_env(@@inject_headers) if @@inject_headers.any?
request_headers = env_http_headers(env)
status, headers, body = @app.call(env)
request = OpenStruct.new(
url: url,
status_code: status,
request_headers: request_headers,
response_headers: headers
)
log_request request
[status, headers, body]
end
private
def env_http_headers(env)
Hash[*env.select { |k, v| k.start_with? 'HTTP_' }
.collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
.collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
.sort
.flatten]
end
def http_headers_env(headers)
Hash[*headers
.collect { |k, v| [k.split('-').collect(&:upcase).join('_'), v] }
.collect { |k, v| [k.prepend('HTTP_'), v] }
.flatten]
end
def log_request(response)
@@logged_requests.push(response)
end
end
end
end
...@@ -50,6 +50,14 @@ describe Groups::GroupMembersController do ...@@ -50,6 +50,14 @@ describe Groups::GroupMembersController do
expect(group.users).to include group_user expect(group.users).to include group_user
end end
it 'creates an audit event' do
expect do
post :create, group_id: group,
user_ids: group_user.id,
access_level: Gitlab::Access::GUEST
end.to change(AuditEvent, :count).by(1)
end
it 'adds no user to members' do it 'adds no user to members' do
post :create, group_id: group, post :create, group_id: group,
user_ids: '', user_ids: '',
...@@ -147,6 +155,10 @@ describe Groups::GroupMembersController do ...@@ -147,6 +155,10 @@ describe Groups::GroupMembersController do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['notice']).to eq "You left the \"#{group.name}\" group." expect(json_response['notice']).to eq "You left the \"#{group.name}\" group."
end end
it 'creates an audit event' do
expect { delete :leave, group_id: group }.to change(AuditEvent, :count).by(1)
end
end end
context 'and is an owner' do context 'and is an owner' do
...@@ -159,6 +171,10 @@ describe Groups::GroupMembersController do ...@@ -159,6 +171,10 @@ describe Groups::GroupMembersController do
expect(response).to have_gitlab_http_status(403) expect(response).to have_gitlab_http_status(403)
end end
it 'does not create an audit event' do
expect { delete :leave, group_id: group }.not_to change(AuditEvent, :count)
end
end end
context 'and is a requester' do context 'and is a requester' do
...@@ -174,6 +190,10 @@ describe Groups::GroupMembersController do ...@@ -174,6 +190,10 @@ describe Groups::GroupMembersController do
expect(group.requesters).to be_empty expect(group.requesters).to be_empty
expect(group.users).not_to include user expect(group.users).not_to include user
end end
it 'creates an audit event' do
expect { delete :leave, group_id: group }.to change(AuditEvent, :count).by(1)
end
end end
end end
end end
......
...@@ -129,7 +129,7 @@ describe 'Issue Boards', :js do ...@@ -129,7 +129,7 @@ describe 'Issue Boards', :js do
end end
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
find('.card:nth-child(2)').trigger('click') find('.card:nth-child(2)').click
end end
page.within('.assignee') do page.within('.assignee') do
......
...@@ -25,7 +25,7 @@ feature 'Update Epic', :js do ...@@ -25,7 +25,7 @@ feature 'Update Epic', :js do
end end
it 'updates the issue' do it 'updates the issue' do
find('.btn-edit').trigger('click') find('.btn-edit').click
fill_in 'issuable-title', with: 'New epic title' fill_in 'issuable-title', with: 'New epic title'
fill_in 'issue-description', with: 'New epic description' fill_in 'issue-description', with: 'New epic description'
......
...@@ -117,9 +117,9 @@ describe 'New/edit issue', :js do ...@@ -117,9 +117,9 @@ describe 'New/edit issue', :js do
page.within '.js-assignee-search' do page.within '.js-assignee-search' do
expect(page).to have_content user2.name expect(page).to have_content user2.name
end end
expect(find('a', text: 'Assign to me')).to be_visible find('.dropdown-menu-user .dropdown-menu-close').click
find('a', text: 'Assign to me').trigger('click') find('a', text: 'Assign to me').click
assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false) assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false)
expect(assignee_ids[0].value).to match(user2.id.to_s) expect(assignee_ids[0].value).to match(user2.id.to_s)
......
...@@ -15,7 +15,7 @@ feature 'New project' do ...@@ -15,7 +15,7 @@ feature 'New project' do
it 'shows mirror repository checkbox enabled', :js do it 'shows mirror repository checkbox enabled', :js do
visit new_project_path visit new_project_path
find('#import-project-tab').trigger('click') find('#import-project-tab').click
first('.import_git').click first('.import_git').click
expect(page).to have_unchecked_field('Mirror repository', disabled: false) expect(page).to have_unchecked_field('Mirror repository', disabled: false)
......
...@@ -77,7 +77,7 @@ describe 'Project settings > [EE] Merge Requests', :js do ...@@ -77,7 +77,7 @@ describe 'Project settings > [EE] Merge Requests', :js do
expect(find('.js-current-approvers')).to have_content(group.name) expect(find('.js-current-approvers')).to have_content(group.name)
within('.js-current-approvers') do within('.js-current-approvers') do
click_on "Remove" accept_confirm { click_on "Remove" }
end end
expect(find('.js-current-approvers')).not_to have_content(group.name) expect(find('.js-current-approvers')).not_to have_content(group.name)
......
require 'spec_helper'
describe Groups::CreateService, '#execute' do
let!(:user) { create :user }
let!(:opts) do
{
name: 'GitLab',
path: 'group_path',
visibility_level: Gitlab::VisibilityLevel::PUBLIC
}
end
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { create_group(user, opts) }
let(:fail_condition!) do
allow(Gitlab::VisibilityLevel).to receive(:allowed_for?).and_return(false)
end
let(:attributes) do
{
author_id: user.id,
entity_id: @resource.id,
entity_type: 'Group',
details: {
add: 'group',
author_name: user.name,
target_id: @resource.full_path,
target_type: 'Group',
target_details: @resource.full_path
}
}
end
end
end
def create_group(user, opts)
described_class.new(user, opts).execute
end
end
require 'spec_helper'
describe Groups::DestroyService do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
subject { described_class.new(group, user, {}) }
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { subject.execute }
let(:fail_condition!) do
expect_any_instance_of(Group)
.to receive(:really_destroy!).and_return(group)
end
let(:attributes) do
{
author_id: user.id,
entity_id: group.id,
entity_type: 'Group',
details: {
remove: 'group',
author_name: user.name,
target_id: group.full_path,
target_type: 'Group',
target_details: group.full_path
}
}
end
end
end
end
require 'spec_helper'
describe Groups::UpdateService, '#execute' do
let!(:user) { create(:user) }
let!(:group) { create(:group, :public) }
context 'audit events' do
let(:audit_event_params) do
{
author_id: user.id,
entity_id: group.id,
entity_type: 'Group',
details: {
author_name: user.name,
target_id: group.id,
target_type: 'Group',
target_details: group.full_path
}
}
end
context '#visibility' do
before do
group.add_owner(user)
end
include_examples 'audit event logging' do
let(:operation) do
update_group(group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
let(:fail_condition!) do
allow(group).to receive(:save).and_return(false)
end
let(:attributes) do
audit_event_params.tap do |param|
param[:details].merge!(
change: 'visibility',
from: 'Public',
to: 'Private'
)
end
end
end
end
end
def update_group(group, user, opts)
Groups::UpdateService.new(group, user, opts).execute
end
end
require 'spec_helper'
describe Projects::GroupLinks::CreateService, '#execute' do
let!(:user) { create :user }
let!(:project) { create :project }
let!(:group) { create(:group, visibility_level: 0) }
let(:opts) do
{
link_group_access: '30',
expires_at: nil
}
end
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { create_group_link(user, project, group, opts) }
let(:fail_condition!) do
create(:project_group_link, project: project, group: group)
end
let(:attributes) do
{
author_id: user.id,
entity_id: group.id,
entity_type: 'Group',
details: {
add: 'project_access',
as: 'Developer',
author_name: user.name,
target_id: project.id,
target_type: 'Project',
target_details: project.full_path
}
}
end
end
end
def create_group_link(user, project, group, opts)
described_class.new(project, user, opts).execute(group)
end
end
require 'spec_helper'
describe Projects::GroupLinks::DestroyService do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project) }
let!(:group_link) { create(:project_group_link, project: project, group: group) }
subject { described_class.new(project, user, {}) }
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { subject.execute(group_link) }
let(:fail_condition!) do
expect_any_instance_of(ProjectGroupLink)
.to receive(:destroy).and_return(group_link)
end
let(:attributes) do
{
author_id: user.id,
entity_id: group.id,
entity_type: 'Group',
details: {
remove: 'project_access',
author_name: user.name,
target_id: project.id,
target_type: 'Project',
target_details: project.full_path
}
}
end
end
end
end
...@@ -2,12 +2,12 @@ module EE ...@@ -2,12 +2,12 @@ module EE
module ProtectedBranchHelpers module ProtectedBranchHelpers
def set_allowed_to(operation, option = 'Masters', form: '.js-new-protected-branch') def set_allowed_to(operation, option = 'Masters', form: '.js-new-protected-branch')
within form do within form do
find(".js-allowed-to-#{operation}").trigger('click') find(".js-allowed-to-#{operation}").click
wait_for_requests wait_for_requests
Array(option).each { |opt| click_on(opt) } Array(option).each { |opt| click_on(opt) }
find(".js-allowed-to-#{operation}").trigger('click') # needed to submit form in some cases find(".js-allowed-to-#{operation}").click # needed to submit form in some cases
end end
end end
end end
......
...@@ -96,23 +96,21 @@ shared_examples "protected branches > access control > EE" do ...@@ -96,23 +96,21 @@ shared_examples "protected branches > access control > EE" do
# Update Protected Branch # Update Protected Branch
within(".protected-branches-list") do within(".protected-branches-list") do
find(".js-allowed-to-#{git_operation}").trigger(:click) find(".js-allowed-to-#{git_operation}").click
find(".dropdown-input-field").set(users.last.name) # Find a user that is not loaded find(".dropdown-input-field").set(users.last.name) # Find a user that is not loaded
wait_for_requests wait_for_requests
expect(page).to have_selector('.dropdown-header', count: 3) %w{Roles Groups Users}.each do |header|
expect(page).to have_selector('.dropdown-header', text: header)
%w{Roles Groups Users}.each_with_index do |header, index|
expect(all('.dropdown-header')[index]).to have_content(header)
end end
click_on users.last.name click_on users.last.name
find(".js-allowed-to-#{git_operation}").trigger(:click) # close find(".js-allowed-to-#{git_operation}").click # close
end end
wait_for_requests wait_for_requests
# Verify the user is appended in the dropdown # Verify the user is appended in the dropdown
find(".protected-branches-list .js-allowed-to-#{git_operation}").trigger(:click) find(".protected-branches-list .js-allowed-to-#{git_operation}").click
wait_for_requests wait_for_requests
expect(page).to have_selector '.dropdown-content .is-active', text: users.last.name expect(page).to have_selector '.dropdown-content .is-active', text: users.last.name
......
...@@ -15,9 +15,11 @@ describe 'Admin::AuditLogs', :js do ...@@ -15,9 +15,11 @@ describe 'Admin::AuditLogs', :js do
end end
it 'returns 404' do it 'returns 404' do
visit admin_audit_logs_path reqs = inspect_requests do
visit admin_audit_logs_path
end
expect(page.status_code).to eq(404) expect(reqs.first.status_code).to eq(404)
end end
end end
......
...@@ -7,7 +7,7 @@ feature 'Admin disables 2FA for a user' do ...@@ -7,7 +7,7 @@ feature 'Admin disables 2FA for a user' do
edit_user(user) edit_user(user)
page.within('.two-factor-status') do page.within('.two-factor-status') do
click_link 'Disable' accept_confirm { click_link 'Disable' }
end end
page.within('.two-factor-status') do page.within('.two-factor-status') do
......
...@@ -153,7 +153,7 @@ feature 'Admin Groups' do ...@@ -153,7 +153,7 @@ feature 'Admin Groups' do
expect(page).to have_content('Developer') expect(page).to have_content('Developer')
end end
find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click accept_confirm { find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click }
visit group_group_members_path(group) visit group_group_members_path(group)
......
...@@ -62,14 +62,14 @@ describe 'Admin::Hooks', :js do ...@@ -62,14 +62,14 @@ describe 'Admin::Hooks', :js do
it 'from hooks list page' do it 'from hooks list page' do
visit admin_hooks_path visit admin_hooks_path
expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1) expect { accept_confirm { find(:link, 'Remove').send_keys(:return) } }.to change(SystemHook, :count).by(-1)
end end
it 'from hook edit page' do it 'from hook edit page' do
visit admin_hooks_path visit admin_hooks_path
click_link 'Edit' click_link 'Edit'
expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1) expect { accept_confirm { find(:link, 'Remove').send_keys(:return) } }.to change(SystemHook, :count).by(-1)
end end
end end
end end
......
...@@ -33,7 +33,7 @@ RSpec.describe 'admin issues labels' do ...@@ -33,7 +33,7 @@ RSpec.describe 'admin issues labels' do
it 'deletes all labels', :js do it 'deletes all labels', :js do
page.within '.labels' do page.within '.labels' do
page.all('.btn-remove').each do |remove| page.all('.btn-remove').each do |remove|
remove.click accept_confirm { remove.click }
wait_for_requests wait_for_requests
end end
end end
......
...@@ -96,7 +96,7 @@ feature 'Admin updates settings' do ...@@ -96,7 +96,7 @@ feature 'Admin updates settings' do
context 'sign-in restrictions', :js do context 'sign-in restrictions', :js do
it 'de-activates oauth sign-in source' do it 'de-activates oauth sign-in source' do
find('.btn', text: 'GitLab.com').click find('input#application_setting_enabled_oauth_sign_in_sources_[value=gitlab]').send_keys(:return)
expect(find('.btn', text: 'GitLab.com')).not_to have_css('.active') expect(find('.btn', text: 'GitLab.com')).not_to have_css('.active')
end end
......
...@@ -24,7 +24,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do ...@@ -24,7 +24,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
fill_in "Name", with: name fill_in "Name", with: name
# Set date to 1st of next month # Set date to 1st of next month
find_field("Expires at").trigger('focus') find_field("Expires at").click
find(".pika-next").click find(".pika-next").click
click_on "1" click_on "1"
...@@ -60,7 +60,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do ...@@ -60,7 +60,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
it "allows revocation of an active impersonation token" do it "allows revocation of an active impersonation token" do
visit admin_user_impersonation_tokens_path(user_id: user.username) visit admin_user_impersonation_tokens_path(user_id: user.username)
click_on "Revoke" accept_confirm { click_on "Revoke" }
expect(page).to have_selector(".settings-message") expect(page).to have_selector(".settings-message")
expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.") expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.")
......
...@@ -369,7 +369,7 @@ describe "Admin::Users" do ...@@ -369,7 +369,7 @@ describe "Admin::Users" do
it 'allows group membership to be revoked', :js do it 'allows group membership to be revoked', :js do
page.within(first('.group_member')) do page.within(first('.group_member')) do
find('.btn-remove').click accept_confirm { find('.btn-remove').click }
end end
wait_for_requests wait_for_requests
...@@ -398,7 +398,7 @@ describe "Admin::Users" do ...@@ -398,7 +398,7 @@ describe "Admin::Users" do
expect(page).to have_content("Secondary email: #{secondary_email.email}") expect(page).to have_content("Secondary email: #{secondary_email.email}")
find("#remove_email_#{secondary_email.id}").click accept_confirm { find("#remove_email_#{secondary_email.id}").click }
expect(page).not_to have_content(secondary_email.email) expect(page).not_to have_content(secondary_email.email)
end end
......
...@@ -37,7 +37,7 @@ feature 'Admin uses repository checks' do ...@@ -37,7 +37,7 @@ feature 'Admin uses repository checks' do
expect(RepositoryCheck::ClearWorker).to receive(:perform_async) expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
click_link 'Clear all repository checks' accept_confirm { find(:link, 'Clear all repository checks').send_keys(:return) }
expect(page).to have_content('Started asynchronous removal of all repository check states.') expect(page).to have_content('Started asynchronous removal of all repository check states.')
end end
......
...@@ -109,7 +109,7 @@ describe 'Issue Boards add issue modal', :js do ...@@ -109,7 +109,7 @@ describe 'Issue Boards add issue modal', :js do
click_button 'Cancel' click_button 'Cancel'
end end
first('.board-delete').click accept_confirm { first('.board-delete').click }
click_button('Add issues') click_button('Add issues')
......
...@@ -210,9 +210,9 @@ describe 'Board with milestone', :js do ...@@ -210,9 +210,9 @@ describe 'Board with milestone', :js do
find('#board-new-name').set 'test' find('#board-new-name').set 'test'
find('button', text: 'Any Milestone').trigger('click') find('button', text: 'Any Milestone').click
find('a', text: milestone.title).trigger('click') find('a', text: milestone.title).click
click_button 'Create' click_button 'Create'
end end
......
...@@ -2,6 +2,7 @@ require 'rails_helper' ...@@ -2,6 +2,7 @@ require 'rails_helper'
describe 'Issue Boards', :js do describe 'Issue Boards', :js do
include DragTo include DragTo
include MobileHelpers
let(:group) { create(:group, :nested) } let(:group) { create(:group, :nested) }
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
...@@ -15,7 +16,7 @@ describe 'Issue Boards', :js do ...@@ -15,7 +16,7 @@ describe 'Issue Boards', :js do
project.team << [user, :master] project.team << [user, :master]
project.team << [user2, :master] project.team << [user2, :master]
page.driver.set_cookie('sidebar_collapsed', 'true') set_cookie('sidebar_collapsed', 'true')
sign_in(user) sign_in(user)
end end
...@@ -137,7 +138,7 @@ describe 'Issue Boards', :js do ...@@ -137,7 +138,7 @@ describe 'Issue Boards', :js do
it 'allows user to delete board' do it 'allows user to delete board' do
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
find('.board-delete').click accept_confirm { find('.board-delete').click }
end end
wait_for_requests wait_for_requests
...@@ -152,7 +153,7 @@ describe 'Issue Boards', :js do ...@@ -152,7 +153,7 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close').click find('.dropdown-menu-close').click
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
find('.board-delete').click accept_confirm { find('.board-delete').click }
end end
wait_for_requests wait_for_requests
...@@ -381,7 +382,7 @@ describe 'Issue Boards', :js do ...@@ -381,7 +382,7 @@ describe 'Issue Boards', :js do
end end
it 'filters by milestone' do it 'filters by milestone' do
set_filter("milestone", "\"#{milestone.title}\"") set_filter("milestone", "\"#{milestone.title}")
click_filter_link(milestone.title) click_filter_link(milestone.title)
submit_filter submit_filter
...@@ -402,7 +403,7 @@ describe 'Issue Boards', :js do ...@@ -402,7 +403,7 @@ describe 'Issue Boards', :js do
end end
it 'filters by label with space after reload' do it 'filters by label with space after reload' do
set_filter("label", "\"#{accepting.title}\"") set_filter("label", "\"#{accepting.title}")
click_filter_link(accepting.title) click_filter_link(accepting.title)
submit_filter submit_filter
...@@ -539,7 +540,7 @@ describe 'Issue Boards', :js do ...@@ -539,7 +540,7 @@ describe 'Issue Boards', :js do
end end
it 'allows user to use keyboard shortcuts' do it 'allows user to use keyboard shortcuts' do
find('.boards-list').native.send_keys('i') find('body').native.send_keys('i')
expect(page).to have_content('New Issue') expect(page).to have_content('New Issue')
end end
end end
...@@ -581,6 +582,9 @@ describe 'Issue Boards', :js do ...@@ -581,6 +582,9 @@ describe 'Issue Boards', :js do
end end
def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0) def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0)
# ensure there is enough horizontal space for four boards
resize_window(2000, 800)
drag_to(selector: selector, drag_to(selector: selector,
scrollable: '#board-app', scrollable: '#board-app',
list_from_index: list_from_index, list_from_index: list_from_index,
......
...@@ -52,7 +52,7 @@ describe 'Issue Boards', :js do ...@@ -52,7 +52,7 @@ describe 'Issue Boards', :js do
expect(page).to have_selector('.issue-boards-sidebar') expect(page).to have_selector('.issue-boards-sidebar')
find('.gutter-toggle').trigger('click') find('.gutter-toggle').click
expect(page).not_to have_selector('.issue-boards-sidebar') expect(page).not_to have_selector('.issue-boards-sidebar')
end end
...@@ -172,7 +172,7 @@ describe 'Issue Boards', :js do ...@@ -172,7 +172,7 @@ describe 'Issue Boards', :js do
end end
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
find('.card:nth-child(2)').trigger('click') find('.card:nth-child(2)').click
end end
page.within('.assignee') do page.within('.assignee') do
......
...@@ -63,8 +63,8 @@ feature 'Contributions Calendar', :js do ...@@ -63,8 +63,8 @@ feature 'Contributions Calendar', :js do
Event.create(note_comment_params) Event.create(note_comment_params)
end end
def selected_day_activities def selected_day_activities(visible: true)
find('.user-calendar-activities').text find('.user-calendar-activities', visible: visible).text
end end
before do before do
...@@ -112,7 +112,7 @@ feature 'Contributions Calendar', :js do ...@@ -112,7 +112,7 @@ feature 'Contributions Calendar', :js do
end end
it 'hides calendar day activities' do it 'hides calendar day activities' do
expect(selected_day_activities).to be_empty expect(selected_day_activities(visible: false)).to be_empty
end end
end end
end end
......
...@@ -47,7 +47,7 @@ describe "Container Registry", :js do ...@@ -47,7 +47,7 @@ describe "Container Registry", :js do
scenario 'user removes a specific tag from container repository' do scenario 'user removes a specific tag from container repository' do
visit_container_registry visit_container_registry
find('.js-toggle-repo').trigger('click') find('.js-toggle-repo').click
wait_for_requests wait_for_requests
expect_any_instance_of(ContainerRegistry::Tag) expect_any_instance_of(ContainerRegistry::Tag)
......
...@@ -13,7 +13,7 @@ RSpec.describe 'Dashboard Group' do ...@@ -13,7 +13,7 @@ RSpec.describe 'Dashboard Group' do
it 'creates new group', :js do it 'creates new group', :js do
visit dashboard_groups_path visit dashboard_groups_path
find('.btn-new').trigger('click') find('.btn-new').click
new_path = 'Samurai' new_path = 'Samurai'
new_description = 'Tokugawa Shogunate' new_description = 'Tokugawa Shogunate'
......
...@@ -138,7 +138,7 @@ feature 'Dashboard Groups page', :js do ...@@ -138,7 +138,7 @@ feature 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group.id}") expect(page).not_to have_selector("#group-#{group.id}")
# Go to next page # Go to next page
find(".gl-pagination .page:not(.active) a").trigger('click') find(".gl-pagination .page:not(.active) a").click
wait_for_requests wait_for_requests
......
...@@ -33,7 +33,7 @@ RSpec.describe 'Dashboard Issues' do ...@@ -33,7 +33,7 @@ RSpec.describe 'Dashboard Issues' do
end end
it 'shows issues when current user is author', :js do it 'shows issues when current user is author', :js do
find('#assignee_id', visible: false).set('') execute_script("document.querySelector('#assignee_id').value=''")
find('.js-author-search', match: :first).click find('.js-author-search', match: :first).click
expect(find('li[data-user-id="null"] a.is-active')).to be_visible expect(find('li[data-user-id="null"] a.is-active')).to be_visible
...@@ -71,7 +71,7 @@ RSpec.describe 'Dashboard Issues' do ...@@ -71,7 +71,7 @@ RSpec.describe 'Dashboard Issues' do
describe 'new issue dropdown' do describe 'new issue dropdown' do
it 'shows projects only with issues feature enabled', :js do it 'shows projects only with issues feature enabled', :js do
find('.new-project-item-select-button').trigger('click') find('.new-project-item-select-button').click
page.within('.select2-results') do page.within('.select2-results') do
expect(page).to have_content(project.name_with_namespace) expect(page).to have_content(project.name_with_namespace)
...@@ -80,7 +80,7 @@ RSpec.describe 'Dashboard Issues' do ...@@ -80,7 +80,7 @@ RSpec.describe 'Dashboard Issues' do
end end
it 'shows the new issue page', :js do it 'shows the new issue page', :js do
find('.new-project-item-select-button').trigger('click') find('.new-project-item-select-button').click
wait_for_requests wait_for_requests
...@@ -93,7 +93,7 @@ RSpec.describe 'Dashboard Issues' do ...@@ -93,7 +93,7 @@ RSpec.describe 'Dashboard Issues' do
find('#select2-drop-mask', visible: false) find('#select2-drop-mask', visible: false)
execute_script("$('#select2-drop-mask').remove();") execute_script("$('#select2-drop-mask').remove();")
find('.new-project-item-link').trigger('click') find('.new-project-item-link').click
expect(page).to have_current_path("#{project_path}/issues/new") expect(page).to have_current_path("#{project_path}/issues/new")
......
...@@ -25,7 +25,7 @@ feature 'Dashboard Merge Requests' do ...@@ -25,7 +25,7 @@ feature 'Dashboard Merge Requests' do
end end
it 'shows projects only with merge requests feature enabled', :js do it 'shows projects only with merge requests feature enabled', :js do
find('.new-project-item-select-button').trigger('click') find('.new-project-item-select-button').click
page.within('.select2-results') do page.within('.select2-results') do
expect(page).to have_content(project.name_with_namespace) expect(page).to have_content(project.name_with_namespace)
......
...@@ -252,7 +252,7 @@ feature 'Dashboard Todos' do ...@@ -252,7 +252,7 @@ feature 'Dashboard Todos' do
describe 'mark all as done', :js do describe 'mark all as done', :js do
before do before do
visit dashboard_todos_path visit dashboard_todos_path
find('.js-todos-mark-all').trigger('click') find('.js-todos-mark-all').click
end end
it 'shows "All done" message!' do it 'shows "All done" message!' do
...@@ -309,9 +309,9 @@ feature 'Dashboard Todos' do ...@@ -309,9 +309,9 @@ feature 'Dashboard Todos' do
end end
def mark_all_and_undo def mark_all_and_undo
find('.js-todos-mark-all').trigger('click') find('.js-todos-mark-all').click
wait_for_requests wait_for_requests
find('.js-todos-undo-all').trigger('click') find('.js-todos-undo-all').click
wait_for_requests wait_for_requests
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe 'Discussion Comments Merge Request', :js do describe 'Discussion Comments Commit', :js do
include RepoHelpers include RepoHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
......
require 'spec_helper' require 'spec_helper'
describe 'Discussion Comments Issue', :js do describe 'Discussion Comments Snippet', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:snippet) { create(:project_snippet, :private, project: project, author: user) } let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
......
...@@ -6,7 +6,7 @@ feature 'Group elastic search', :js do ...@@ -6,7 +6,7 @@ feature 'Group elastic search', :js do
let(:project) { create(:project, :repository, namespace: group) } let(:project) { create(:project, :repository, namespace: group) }
def choose_group(group) def choose_group(group)
find('.js-search-group-dropdown').trigger('click') find('.js-search-group-dropdown').click
wait_for_requests wait_for_requests
page.within '.search-holder' do page.within '.search-holder' do
...@@ -41,7 +41,7 @@ feature 'Group elastic search', :js do ...@@ -41,7 +41,7 @@ feature 'Group elastic search', :js do
choose_group group choose_group group
fill_in 'search', with: 'chosen' fill_in 'search', with: 'chosen'
find('.btn-search').trigger('click') find('.btn-search').click
select_filter('Issues') select_filter('Issues')
expect(page).to have_content('chosen issue title') expect(page).to have_content('chosen issue title')
...@@ -60,7 +60,7 @@ feature 'Group elastic search', :js do ...@@ -60,7 +60,7 @@ feature 'Group elastic search', :js do
choose_group group choose_group group
fill_in 'search', with: 'def' fill_in 'search', with: 'def'
find('.btn-search').trigger('click') find('.btn-search').click
select_filter('Code') select_filter('Code')
...@@ -83,7 +83,7 @@ feature 'Group elastic search', :js do ...@@ -83,7 +83,7 @@ feature 'Group elastic search', :js do
choose_group group choose_group group
fill_in "search", with: "term" fill_in "search", with: "term"
find('.btn-search').trigger('click') find('.btn-search').click
select_filter("Wiki") select_filter("Wiki")
...@@ -104,7 +104,7 @@ feature 'Group elastic search', :js do ...@@ -104,7 +104,7 @@ feature 'Group elastic search', :js do
choose_group group choose_group group
fill_in 'search', with: 'add' fill_in 'search', with: 'add'
find('.btn-search').trigger('click') find('.btn-search').click
select_filter('Commits') select_filter('Commits')
......
...@@ -65,9 +65,9 @@ feature 'Top Plus Menu', :js do ...@@ -65,9 +65,9 @@ feature 'Top Plus Menu', :js do
visit project_path(project) visit project_path(project)
page.within '.header-content' do page.within '.header-content' do
find('.header-new-dropdown-toggle').trigger('click') find('.header-new-dropdown-toggle').click
expect(page).to have_selector('.header-new.dropdown.open', count: 1) expect(page).to have_selector('.header-new.dropdown.open', count: 1)
find('.header-new-project-snippet a').trigger('click') find('.header-new-project-snippet a').click
end end
expect(page).to have_content('New Snippet') expect(page).to have_content('New Snippet')
...@@ -87,9 +87,9 @@ feature 'Top Plus Menu', :js do ...@@ -87,9 +87,9 @@ feature 'Top Plus Menu', :js do
visit group_path(group) visit group_path(group)
page.within '.header-content' do page.within '.header-content' do
find('.header-new-dropdown-toggle').trigger('click') find('.header-new-dropdown-toggle').click
expect(page).to have_selector('.header-new.dropdown.open', count: 1) expect(page).to have_selector('.header-new.dropdown.open', count: 1)
find('.header-new-group-project a').trigger('click') find('.header-new-group-project a').click
end end
expect(page).to have_content('Project path') expect(page).to have_content('Project path')
...@@ -155,7 +155,7 @@ feature 'Top Plus Menu', :js do ...@@ -155,7 +155,7 @@ feature 'Top Plus Menu', :js do
def click_topmenuitem(item_name) def click_topmenuitem(item_name)
page.within '.header-content' do page.within '.header-content' do
find('.header-new-dropdown-toggle').trigger('click') find('.header-new-dropdown-toggle').click
expect(page).to have_selector('.header-new.dropdown.open', count: 1) expect(page).to have_selector('.header-new.dropdown.open', count: 1)
click_link item_name click_link item_name
end end
......
...@@ -14,7 +14,7 @@ feature 'Groups > Contribution Analytics', :js do ...@@ -14,7 +14,7 @@ feature 'Groups > Contribution Analytics', :js do
it 'displays Contribution Analytics' do it 'displays Contribution Analytics' do
visit group_path(group) visit group_path(group)
find('a', text: 'Contribution Analytics').trigger('click') find('a', text: 'Contribution Analytics').click
expect(page).to have_content "Contribution analytics for issues, merge requests and push" expect(page).to have_content "Contribution analytics for issues, merge requests and push"
end end
......
...@@ -17,9 +17,11 @@ feature 'Groups > Audit Events', :js do ...@@ -17,9 +17,11 @@ feature 'Groups > Audit Events', :js do
end end
it 'returns 404' do it 'returns 404' do
visit group_audit_events_path(group) reqs = inspect_requests do
visit group_audit_events_path(group)
end
expect(page.status_code).to eq(404) expect(reqs.first.status_code).to eq(404)
end end
it 'does not have Audit Events button in head nav bar' do it 'does not have Audit Events button in head nav bar' do
...@@ -46,8 +48,7 @@ feature 'Groups > Audit Events', :js do ...@@ -46,8 +48,7 @@ feature 'Groups > Audit Events', :js do
click_link 'Master' click_link 'Master'
end end
# This is to avoid a Capybara::Poltergeist::MouseEventFailed error find(:link, text: 'Settings').click
find(:link, text: 'Settings').trigger('click')
click_link 'Audit Events' click_link 'Audit Events'
......
...@@ -65,7 +65,7 @@ feature 'Group' do ...@@ -65,7 +65,7 @@ feature 'Group' do
end end
it 'updates the team URL on graph path update', :js do it 'updates the team URL on graph path update', :js do
out_span = find('span[data-bind-out="create_chat_team"]') out_span = find('span[data-bind-out="create_chat_team"]', visible: false)
expect(out_span.text).to be_empty expect(out_span.text).to be_empty
......
...@@ -405,7 +405,7 @@ feature 'Issues > Labels bulk assignment' do ...@@ -405,7 +405,7 @@ feature 'Issues > Labels bulk assignment' do
end end
def update_issues def update_issues
find('.update-selected-issues').trigger('click') find('.update-selected-issues').click
wait_for_requests wait_for_requests
end end
......
...@@ -43,15 +43,16 @@ describe 'Dropdown assignee', :js do ...@@ -43,15 +43,16 @@ describe 'Dropdown assignee', :js do
end end
it 'should show loading indicator when opened' do it 'should show loading indicator when opened' do
filtered_search.set('assignee:') slow_requests do
filtered_search.set('assignee:')
expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true) expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true)
end
end end
it 'should hide loading indicator when loaded' do it 'should hide loading indicator when loaded' do
filtered_search.set('assignee:') filtered_search.set('assignee:')
expect(find(js_dropdown_assignee)).to have_css('.filter-dropdown-loading')
expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading') expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading')
end end
......
...@@ -51,9 +51,11 @@ describe 'Dropdown author', :js do ...@@ -51,9 +51,11 @@ describe 'Dropdown author', :js do
end end
it 'should show loading indicator when opened' do it 'should show loading indicator when opened' do
filtered_search.set('author:') slow_requests do
filtered_search.set('author:')
expect(page).to have_css('#js-dropdown-author .filter-dropdown-loading', visible: true) expect(page).to have_css('#js-dropdown-author .filter-dropdown-loading', visible: true)
end
end end
it 'should hide loading indicator when loaded' do it 'should hide loading indicator when loaded' do
......
...@@ -70,9 +70,11 @@ describe 'Dropdown emoji', :js do ...@@ -70,9 +70,11 @@ describe 'Dropdown emoji', :js do
end end
it 'should show loading indicator when opened' do it 'should show loading indicator when opened' do
filtered_search.set('my-reaction:') slow_requests do
filtered_search.set('my-reaction:')
expect(page).to have_css('#js-dropdown-my-reaction .filter-dropdown-loading', visible: true) expect(page).to have_css('#js-dropdown-my-reaction .filter-dropdown-loading', visible: true)
end
end end
it 'should hide loading indicator when loaded' do it 'should hide loading indicator when loaded' do
......
...@@ -66,9 +66,11 @@ describe 'Dropdown label', :js do ...@@ -66,9 +66,11 @@ describe 'Dropdown label', :js do
end end
it 'shows loading indicator when opened and hides it when loaded' do it 'shows loading indicator when opened and hides it when loaded' do
filtered_search.set('label:') slow_requests do
filtered_search.set('label:')
expect(find(js_dropdown_label)).to have_css('.filter-dropdown-loading') expect(page).to have_css("#{js_dropdown_label} .filter-dropdown-loading", visible: true)
end
expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading') expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading')
end end
......
...@@ -50,15 +50,16 @@ describe 'Dropdown milestone', :js do ...@@ -50,15 +50,16 @@ describe 'Dropdown milestone', :js do
end end
it 'should show loading indicator when opened' do it 'should show loading indicator when opened' do
filtered_search.set('milestone:') slow_requests do
filtered_search.set('milestone:')
expect(page).to have_css('#js-dropdown-milestone .filter-dropdown-loading', visible: true) expect(page).to have_css('#js-dropdown-milestone .filter-dropdown-loading', visible: true)
end
end end
it 'should hide loading indicator when loaded' do it 'should hide loading indicator when loaded' do
filtered_search.set('milestone:') filtered_search.set('milestone:')
expect(find(js_dropdown_milestone)).to have_css('.filter-dropdown-loading')
expect(find(js_dropdown_milestone)).not_to have_css('.filter-dropdown-loading') expect(find(js_dropdown_milestone)).not_to have_css('.filter-dropdown-loading')
end end
......
...@@ -156,7 +156,7 @@ describe 'Filter issues', :js do ...@@ -156,7 +156,7 @@ describe 'Filter issues', :js do
input_filtered_search('label:none') input_filtered_search('label:none')
expect_tokens([label_token('none', false)]) expect_tokens([label_token('none', false)])
expect_issues_list_count(8) expect_issues_list_count(4)
expect_filtered_search_input_empty expect_filtered_search_input_empty
end end
...@@ -520,7 +520,7 @@ describe 'Filter issues', :js do ...@@ -520,7 +520,7 @@ describe 'Filter issues', :js do
it 'updates atom feed link for group issues' do it 'updates atom feed link for group issues' do
visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id)
link = find('.nav-controls a', text: 'Subscribe') link = find('.nav-controls a', text: 'Subscribe', visible: false)
params = CGI.parse(URI.parse(link[:href]).query) params = CGI.parse(URI.parse(link[:href]).query)
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
......
...@@ -76,7 +76,8 @@ describe 'Recent searches', :js do ...@@ -76,7 +76,8 @@ describe 'Recent searches', :js do
set_recent_searches(project_1_local_storage_key, '["foo", "bar"]') set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
visit project_issues_path(project_1) visit project_issues_path(project_1)
all('.filtered-search-history-dropdown-item', visible: false, count: 2)[0].trigger('click') find('.filtered-search-history-dropdown-toggle-button').click
all('.filtered-search-history-dropdown-item', count: 2)[0].click
wait_for_filtered_search('foo') wait_for_filtered_search('foo')
expect(find('.filtered-search').value.strip).to eq('foo') expect(find('.filtered-search').value.strip).to eq('foo')
...@@ -86,10 +87,11 @@ describe 'Recent searches', :js do ...@@ -86,10 +87,11 @@ describe 'Recent searches', :js do
set_recent_searches(project_1_local_storage_key, '["foo"]') set_recent_searches(project_1_local_storage_key, '["foo"]')
visit project_issues_path(project_1) visit project_issues_path(project_1)
all('.filtered-search-history-dropdown-item', visible: false, count: 1) find('.filtered-search-history-dropdown-toggle-button').click
all('.filtered-search-history-dropdown-item', count: 1)
find('.filtered-search-history-clear-button', visible: false).trigger('click') find('.filtered-search-history-clear-button').click
items_after = all('.filtered-search-history-dropdown-item', visible: false, count: 0) items_after = all('.filtered-search-history-dropdown-item', count: 0)
expect(items_after.count).to eq(0) expect(items_after.count).to eq(0)
end end
......
...@@ -2,7 +2,6 @@ require 'rails_helper' ...@@ -2,7 +2,6 @@ require 'rails_helper'
describe 'Visual tokens', :js do describe 'Visual tokens', :js do
include FilteredSearchHelpers include FilteredSearchHelpers
include WaitForRequests
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:user) { create(:user, name: 'administrator', username: 'root') } let!(:user) { create(:user, name: 'administrator', username: 'root') }
...@@ -28,7 +27,7 @@ describe 'Visual tokens', :js do ...@@ -28,7 +27,7 @@ describe 'Visual tokens', :js do
sign_in(user) sign_in(user)
create(:issue, project: project) create(:issue, project: project)
page.driver.set_cookie('sidebar_collapsed', 'true') set_cookie('sidebar_collapsed', 'true')
visit project_issues_path(project) visit project_issues_path(project)
end end
......
...@@ -17,9 +17,9 @@ feature 'GFM autocomplete', :js do ...@@ -17,9 +17,9 @@ feature 'GFM autocomplete', :js do
it 'updates issue descripton with GFM reference' do it 'updates issue descripton with GFM reference' do
find('.issuable-edit').click find('.issuable-edit').click
find('#issue-description').native.send_keys("@#{user.name[0...3]}") simulate_input('#issue-description', "@#{user.name[0...3]}")
find('.atwho-view .cur').trigger('click') find('.atwho-view .cur').click
click_button 'Save changes' click_button 'Save changes'
...@@ -28,7 +28,6 @@ feature 'GFM autocomplete', :js do ...@@ -28,7 +28,6 @@ feature 'GFM autocomplete', :js do
it 'opens autocomplete menu when field starts with text' do it 'opens autocomplete menu when field starts with text' do
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
find('#note-body').native.send_keys('')
find('#note-body').native.send_keys('@') find('#note-body').native.send_keys('@')
end end
...@@ -46,7 +45,6 @@ feature 'GFM autocomplete', :js do ...@@ -46,7 +45,6 @@ feature 'GFM autocomplete', :js do
it 'doesnt select the first item for non-assignee dropdowns' do it 'doesnt select the first item for non-assignee dropdowns' do
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
find('#note-body').native.send_keys('')
find('#note-body').native.send_keys(':') find('#note-body').native.send_keys(':')
end end
...@@ -86,7 +84,6 @@ feature 'GFM autocomplete', :js do ...@@ -86,7 +84,6 @@ feature 'GFM autocomplete', :js do
it 'selects the first item for assignee dropdowns' do it 'selects the first item for assignee dropdowns' do
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
find('#note-body').native.send_keys('')
find('#note-body').native.send_keys('@') find('#note-body').native.send_keys('@')
end end
...@@ -100,7 +97,7 @@ feature 'GFM autocomplete', :js do ...@@ -100,7 +97,7 @@ feature 'GFM autocomplete', :js do
it 'includes items for assignee dropdowns with non-ASCII characters in name' do it 'includes items for assignee dropdowns with non-ASCII characters in name' do
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
find('#note-body').native.send_keys('') find('#note-body').native.send_keys('')
find('#note-body').native.send_keys("@#{user.name[0...8]}") simulate_input('#note-body', "@#{user.name[0...8]}")
end end
expect(page).to have_selector('.atwho-container') expect(page).to have_selector('.atwho-container')
...@@ -112,7 +109,6 @@ feature 'GFM autocomplete', :js do ...@@ -112,7 +109,6 @@ feature 'GFM autocomplete', :js do
it 'selects the first item for non-assignee dropdowns if a query is entered' do it 'selects the first item for non-assignee dropdowns if a query is entered' do
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
find('#note-body').native.send_keys('')
find('#note-body').native.send_keys(':1') find('#note-body').native.send_keys(':1')
end end
...@@ -127,9 +123,8 @@ feature 'GFM autocomplete', :js do ...@@ -127,9 +123,8 @@ feature 'GFM autocomplete', :js do
it 'wraps the result in double quotes' do it 'wraps the result in double quotes' do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('') find('#note-body').native.send_keys('')
note.native.send_keys("~#{label.title[0]}") simulate_input('#note-body', "~#{label.title[0]}")
note.click
end end
label_item = find('.atwho-view li', text: label.title) label_item = find('.atwho-view li', text: label.title)
...@@ -152,16 +147,13 @@ feature 'GFM autocomplete', :js do ...@@ -152,16 +147,13 @@ feature 'GFM autocomplete', :js do
it "does not show dropdown when preceded with a special character" do it "does not show dropdown when preceded with a special character" do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys("@") note.native.send_keys("@")
note.click
end end
expect(page).to have_selector('.atwho-container') expect(page).to have_selector('.atwho-container')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys("@") note.native.send_keys("@")
note.click
end end
expect(page).to have_selector('.atwho-container', visible: false) expect(page).to have_selector('.atwho-container', visible: false)
...@@ -170,9 +162,7 @@ feature 'GFM autocomplete', :js do ...@@ -170,9 +162,7 @@ feature 'GFM autocomplete', :js do
it "does not throw an error if no labels exist" do it "does not throw an error if no labels exist" do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys('~') note.native.send_keys('~')
note.click
end end
expect(page).to have_selector('.atwho-container', visible: false) expect(page).to have_selector('.atwho-container', visible: false)
...@@ -181,9 +171,7 @@ feature 'GFM autocomplete', :js do ...@@ -181,9 +171,7 @@ feature 'GFM autocomplete', :js do
it 'doesn\'t wrap for assignee values' do it 'doesn\'t wrap for assignee values' do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys("@#{user.username[0]}") note.native.send_keys("@#{user.username[0]}")
note.click
end end
user_item = find('.atwho-view li', text: user.username) user_item = find('.atwho-view li', text: user.username)
...@@ -194,9 +182,7 @@ feature 'GFM autocomplete', :js do ...@@ -194,9 +182,7 @@ feature 'GFM autocomplete', :js do
it 'doesn\'t wrap for emoji values' do it 'doesn\'t wrap for emoji values' do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('') note.native.send_keys(":cartwheel_")
note.native.send_keys(":cartwheel")
note.click
end end
emoji_item = find('.atwho-view li', text: 'cartwheel_tone1') emoji_item = find('.atwho-view li', text: 'cartwheel_tone1')
...@@ -223,12 +209,11 @@ feature 'GFM autocomplete', :js do ...@@ -223,12 +209,11 @@ feature 'GFM autocomplete', :js do
it 'triggers autocomplete after selecting a quick action' do it 'triggers autocomplete after selecting a quick action' do
note = find('#note-body') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys('/as') note.native.send_keys('/as')
note.click
end end
find('.atwho-view li', text: '/assign').native.send_keys(:tab) find('.atwho-view li', text: '/assign')
note.native.send_keys(:tab)
user_item = find('.atwho-view li', text: user.username) user_item = find('.atwho-view li', text: user.username)
expect(user_item).to have_content(user.username) expect(user_item).to have_content(user.username)
......
...@@ -130,8 +130,8 @@ feature 'Issue Sidebar' do ...@@ -130,8 +130,8 @@ feature 'Issue Sidebar' do
it 'adds new label' do it 'adds new label' do
page.within('.block.labels') do page.within('.block.labels') do
fill_in 'new_label_name', with: 'wontfix' fill_in 'new_label_name', with: 'wontfix'
page.find('.suggest-colors a', match: :first).trigger('click') page.find('.suggest-colors a', match: :first).click
page.find('button', text: 'Create').trigger('click') page.find('button', text: 'Create').click
page.within('.dropdown-page-one') do page.within('.dropdown-page-one') do
expect(page).to have_content 'wontfix' expect(page).to have_content 'wontfix'
...@@ -142,8 +142,8 @@ feature 'Issue Sidebar' do ...@@ -142,8 +142,8 @@ feature 'Issue Sidebar' do
it 'shows error message if label title is taken' do it 'shows error message if label title is taken' do
page.within('.block.labels') do page.within('.block.labels') do
fill_in 'new_label_name', with: label.title fill_in 'new_label_name', with: label.title
page.find('.suggest-colors a', match: :first).trigger('click') page.find('.suggest-colors a', match: :first).click
page.find('button', text: 'Create').trigger('click') page.find('button', text: 'Create').click
page.within('.dropdown-page-two') do page.within('.dropdown-page-two') do
expect(page).to have_content 'Title has already been taken' expect(page).to have_content 'Title has already been taken'
...@@ -199,7 +199,7 @@ feature 'Issue Sidebar' do ...@@ -199,7 +199,7 @@ feature 'Issue Sidebar' do
end end
def open_issue_sidebar def open_issue_sidebar
find('aside.right-sidebar.right-sidebar-collapsed .js-sidebar-toggle').trigger('click') find('aside.right-sidebar.right-sidebar-collapsed .js-sidebar-toggle').click
find('aside.right-sidebar.right-sidebar-expanded') find('aside.right-sidebar.right-sidebar-expanded')
end end
end end
...@@ -38,7 +38,7 @@ feature 'issue move to another project' do ...@@ -38,7 +38,7 @@ feature 'issue move to another project' do
end end
scenario 'moving issue to another project', :js do scenario 'moving issue to another project', :js do
find('.js-move-issue').trigger('click') find('.js-move-issue').click
wait_for_requests wait_for_requests
all('.js-move-issue-dropdown-item')[0].click all('.js-move-issue-dropdown-item')[0].click
find('.js-move-issue-confirmation-button').click find('.js-move-issue-confirmation-button').click
...@@ -52,7 +52,7 @@ feature 'issue move to another project' do ...@@ -52,7 +52,7 @@ feature 'issue move to another project' do
scenario 'searching project dropdown', :js do scenario 'searching project dropdown', :js do
new_project_search.team << [user, :reporter] new_project_search.team << [user, :reporter]
find('.js-move-issue').trigger('click') find('.js-move-issue').click
wait_for_requests wait_for_requests
page.within '.js-sidebar-move-issue-block' do page.within '.js-sidebar-move-issue-block' do
...@@ -69,7 +69,7 @@ feature 'issue move to another project' do ...@@ -69,7 +69,7 @@ feature 'issue move to another project' do
background { another_project.team << [user, :guest] } background { another_project.team << [user, :guest] }
scenario 'browsing projects in projects select' do scenario 'browsing projects in projects select' do
find('.js-move-issue').trigger('click') find('.js-move-issue').click
wait_for_requests wait_for_requests
page.within '.js-sidebar-move-issue-block' do page.within '.js-sidebar-move-issue-block' do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment