diff options
246 files changed, 2146 insertions, 1421 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38fb743b0c9..fed5971233d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -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 key: "ruby-235-with-yarn" @@ -23,7 +23,6 @@ variables: SIMPLECOV: "true" GIT_DEPTH: "20" GIT_SUBMODULE_STRATEGY: "none" - PHANTOMJS_VERSION: "2.1.1" GET_SOURCES_ATTEMPTS: "3" 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 @@ -455,7 +454,7 @@ db:migrate:reset-mysql: variables: SETUP_DB: "false" script: - - git fetch origin v9.3.0 + - git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0 - git checkout -f FETCH_HEAD - bundle install $BUNDLE_INSTALL_FLAGS - cp config/gitlab.yml.example config/gitlab.yml @@ -551,7 +550,6 @@ karma: <<: *dedicated-runner <<: *except-docs <<: *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 variables: BABEL_ENV: "coverage" diff --git a/.scss-lint.yml b/.scss-lint.yml index d2c972fa9c4..16a168b7c60 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -112,7 +112,7 @@ linters: # Reports when you define the same selector twice in a single sheet. MergeableSelector: - enabled: false + enabled: true # Functions, mixins, variables, and placeholders should be declared # with all lowercase letters and hyphens instead of underscores. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bca9944bb1..2f13eca2caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.1.1 (2017-10-31) + +- [FIXED] Auto Devops kubernetes default namespace is now correctly built out of gitlab project group-name. !14642 (Mircea Danila Dumitrescu) +- [FIXED] Forbid the usage of `Redis#keys`. !14889 +- [FIXED] Make the circuitbreaker more robust by adding higher thresholds, and multiple access attempts. !14933 +- [FIXED] Only cache last push event for existing projects when pushing to a fork. !14989 +- [FIXED] Fix bug preventing secondary emails from being confirmed. !15010 +- [FIXED] Fix broken wiki pages that link to a wiki file. !15019 +- [FIXED] Don't rename paths that were freed up when upgrading. !15029 +- [FIXED] Fix bitbucket login. !15051 +- [FIXED] Update gitaly in Gitlab 10.1 to 0.43.1 for temp file cleanup. !15055 +- [FIXED] Use the correct visibility attribute for projects in system hooks. !15065 +- [FIXED] Normalize LDAP DN when looking up identity. +- [FIXED] Adds callback functions for initial request in clusters page. +- [FIXED] Fix missing Import/Export issue assignees. +- [FIXED] Allow boards as top level route. +- [FIXED] Fix widget of locked merge requests not being presented. +- [FIXED] Fix editing issue description in mobile view. +- [FIXED] Fix deletion of container registry or images returning an error. +- [FIXED] Fix the writing of invalid environment refs. +- [CHANGED] Store circuitbreaker settings in the database instead of config. !14842 +- [CHANGED] Update default disabled merge request widget message to reflect a general failure. !14960 +- [PERFORMANCE] Stop merge requests with thousands of commits from timing out. !15063 + ## 10.1.0 (2017-10-22) - [SECURITY] Use a timeout on certain git operations. !14872 @@ -194,6 +218,24 @@ entry. - creation of keys moved to services. !13331 (haseebeqx) - 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) - [SECURITY] Move project repositories between namespaces when renaming users. @@ -324,9 +324,9 @@ group :development, :test do # Generate Fake data gem 'ffaker', '~> 2.4' - gem 'capybara', '~> 2.15.0' + gem 'capybara', '~> 2.15' gem 'capybara-screenshot', '~> 1.0.0' - gem 'poltergeist', '~> 1.9.0' + gem 'selenium-webdriver', '~> 3.5' gem 'spring', '~> 2.0.0' gem 'spring-commands-rspec', '~> 1.0.4' diff --git a/Gemfile.lock b/Gemfile.lock index 8ccf18818a6..ae145ca5f69 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,12 +113,13 @@ GEM mime-types (>= 1.16) cause (0.1) charlock_holmes (0.7.5) + childprocess (0.7.0) + ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) chronic_duration (0.10.6) numerizer (~> 0.1.1) chunky_png (1.3.5) citrus (3.0.2) - cliver (0.3.2) coderay (1.1.1) coercible (1.0.0) descendants_tracker (~> 0.0.1) @@ -604,11 +605,6 @@ GEM pg (0.18.4) po_to_json (1.0.1) 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) powerpack (0.1.1) premailer (1.10.4) @@ -818,6 +814,9 @@ GEM activesupport (>= 3.1) select2-rails (3.5.9.3) thor (~> 0.14) + selenium-webdriver (3.5.0) + childprocess (~> 0.5) + rubyzip (~> 1.0) sentry-raven (2.5.3) faraday (>= 0.7.6, < 1.0) settingslogic (2.0.9) @@ -949,9 +948,6 @@ GEM hashdiff webpack-rails (0.9.10) railties (>= 3.2.0) - websocket-driver (0.6.3) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) wikicloth (0.8.1) builder expression_parser @@ -988,7 +984,7 @@ DEPENDENCIES browser (~> 2.2) bullet (~> 5.5.0) bundler-audit (~> 0.5.0) - capybara (~> 2.15.0) + capybara (~> 2.15) capybara-screenshot (~> 1.0.0) carrierwave (~> 1.2) charlock_holmes (~> 0.7.5) @@ -1104,7 +1100,6 @@ DEPENDENCIES peek-redis (~> 1.2.0) peek-sidekiq (~> 1.0.3) pg (~> 0.18.2) - poltergeist (~> 1.9.0) premailer-rails (~> 1.9.7) prometheus-client-mmap (~> 0.7.0.beta18) pry-byebug (~> 3.4.1) @@ -1150,6 +1145,7 @@ DEPENDENCIES scss_lint (~> 0.54.0) seed-fu (~> 2.3.5) select2-rails (~> 3.5.9) + selenium-webdriver (~> 3.5) sentry-raven (~> 2.5.3) settingslogic (~> 2.0.9) sham_rack (~> 1.3.6) diff --git a/app/assets/javascripts/droplab/plugins/filter.js b/app/assets/javascripts/droplab/plugins/filter.js index d6a1aadd49c..404d707cf7a 100644 --- a/app/assets/javascripts/droplab/plugins/filter.js +++ b/app/assets/javascripts/droplab/plugins/filter.js @@ -79,8 +79,6 @@ const Filter = { this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown); this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown); - - this.debounceKeydown({ detail: { hook: this.hook } }); }, destroy: function destroy() { diff --git a/app/assets/javascripts/test_utils/index.js b/app/assets/javascripts/test_utils/index.js index 8875590f0f2..a55a338eea8 100644 --- a/app/assets/javascripts/test_utils/index.js +++ b/app/assets/javascripts/test_utils/index.js @@ -1,6 +1,8 @@ import 'core-js/es6/map'; import 'core-js/es6/set'; import simulateDrag from './simulate_drag'; +import simulateInput from './simulate_input'; // Export to global space for rspec to use window.simulateDrag = simulateDrag; +window.simulateInput = simulateInput; diff --git a/app/assets/javascripts/test_utils/simulate_input.js b/app/assets/javascripts/test_utils/simulate_input.js new file mode 100644 index 00000000000..90c1b7cb57e --- /dev/null +++ b/app/assets/javascripts/test_utils/simulate_input.js @@ -0,0 +1,23 @@ +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; +} diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 8819a0c20f4..def986180fc 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -40,6 +40,10 @@ &.top-block { border-top: none; + + .container-fluid { + background-color: inherit; + } } &.middle-block { @@ -98,10 +102,6 @@ background-color: $white-light; border-top: none; } - - &.top-block .container-fluid { - background-color: inherit; - } } .sub-header-block { diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index e0e46dd73af..1bd94c0acba 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -12,15 +12,15 @@ border-left: 3px solid $border-color; color: $text-color; background: $gray-light; -} -.bs-callout h4 { - margin-top: 0; - margin-bottom: 5px; -} + h4 { + margin-top: 0; + margin-bottom: 5px; + } -.bs-callout p:last-child { - margin-bottom: 0; + p:last-child { + margin-bottom: 0; + } } /* Variations */ diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index ed84b17af6a..ea3007f5e08 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -53,6 +53,14 @@ hr { .str-truncated { @include str-truncated; + + &-60 { + @include str-truncated(60%); + } + + &-100 { + @include str-truncated(100%); + } } .block-truncated { @@ -78,10 +86,17 @@ hr { font-size: 14px; } -table a code { - position: relative; - top: -2px; - margin-right: 3px; +table { + a code { + position: relative; + top: -2px; + margin-right: 3px; + } + + td.permission-x { + background: $table-permission-x-bg !important; + text-align: center; + } } .loading { @@ -266,13 +281,6 @@ img.emoji { margin-bottom: 10px; } -table { - td.permission-x { - background: $table-permission-x-bg !important; - text-align: center; - } -} - .btn-sign-in { text-shadow: none; @@ -338,10 +346,11 @@ table { .dropzone .dz-preview .dz-progress { border-color: $border-color !important; -} -.dropzone .dz-preview .dz-progress .dz-upload { - background: $gl-success !important; + .dz-upload { + background: $gl-success !important; + } + } .dz-message { @@ -402,16 +411,6 @@ table { border-radius: $border-radius-default; } -.str-truncated { - &-60 { - @include str-truncated(60%); - } - - &-100 { - @include str-truncated(100%); - } -} - .tooltip { .tooltip-inner { word-wrap: break-word; diff --git a/app/assets/stylesheets/framework/contextual-sidebar.scss b/app/assets/stylesheets/framework/contextual-sidebar.scss index fa5d3833f3e..320f458630a 100644 --- a/app/assets/stylesheets/framework/contextual-sidebar.scss +++ b/app/assets/stylesheets/framework/contextual-sidebar.scss @@ -141,15 +141,15 @@ svg { fill: $gl-text-color-secondary; } - } - .nav-item-name { - flex: 1; - } + .nav-item-name { + flex: 1; + } - li.active { - > a { - font-weight: $gl-font-weight-bold; + &.active { + > a { + font-weight: $gl-font-weight-bold; + } } } @@ -484,10 +484,7 @@ height: calc(100vh - #{$header-height}); @media (min-width: $screen-sm-min) { - height: 475px; // Needed for PhantomJS - // scss-lint:disable DuplicateProperty height: calc(100vh - 180px); - // scss-lint:enable DuplicateProperty } } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 1aa53b8f8cf..08c603edd23 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -727,11 +727,11 @@ .pika-single.animate-picker.is-bound { @include set-visible; -} -.pika-single.animate-picker.is-bound.is-hidden { - @include set-invisible; - overflow: hidden; + &.is-hidden { + @include set-invisible; + overflow: hidden; + } } @mixin dropdown-item-hover { @@ -938,9 +938,7 @@ header.header-content .dropdown-menu.projects-dropdown-menu { border-right: 0; } } -} -.projects-dropdown-container { .projects-list-frequent-container, .projects-list-search-container, { padding: 8px 0; @@ -951,11 +949,6 @@ header.header-content .dropdown-menu.projects-dropdown-menu { .projects-list-frequent-container li.section-empty, .projects-list-search-container li.section-empty { padding: 0 15px; - } - - .section-header, - .projects-list-frequent-container li.section-empty, - .projects-list-search-container li.section-empty { color: $gl-text-color-secondary; font-size: $gl-font-size; } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 5833ef939e9..6382551fcc9 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -165,22 +165,36 @@ &:last-child { border-right: none; } - } - td.blame-commit { - padding: 5px 10px; - min-width: 400px; - max-width: 400px; - background: $gray-light; - border-left: 3px solid; + &.blame-commit { + padding: 5px 10px; + min-width: 400px; + max-width: 400px; + background: $gray-light; + border-left: 3px solid; + + .commit-row-title { + display: flex; + } + + .item-title { + flex: 1; + margin-right: 0.5em; + } + } + + &.line-numbers { + float: none; + border-left: 1px solid $blame-line-numbers-border; - .commit-row-title { - display: flex; + i { + float: none; + margin-right: 0; + } } - .item-title { - flex: 1; - margin-right: 0.5em; + &.lines { + padding: 0; } } @@ -195,20 +209,6 @@ border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%); } } - - td.line-numbers { - float: none; - border-left: 1px solid $blame-line-numbers-border; - - i { - float: none; - margin-right: 0; - } - } - - td.lines { - padding: 0; - } } &.logs { diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 0d80a85d521..a7333925f80 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -268,12 +268,6 @@ .filtered-search-box-input-container { flex: 1; 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; } @@ -469,10 +463,10 @@ word-break: break-all; } } -} -.filter-dropdown-item.droplab-item-active .btn { - @extend %filter-dropdown-item-btn-hover; + &.droplab-item-active .btn { + @extend %filter-dropdown-item-btn-hover; + } } .filter-dropdown-loading { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 62ba74ff582..5d777f0d468 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -352,7 +352,77 @@ .header-user .dropdown-menu-nav, .header-new .dropdown-menu-nav { - margin-top: $dropdown-vertical-offset; + margin-top: 4px; +} + +.search { + margin: 4px 8px 0; + + form { + height: 32px; + border: 0; + border-radius: $border-radius-default; + transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s; + + &:hover { + box-shadow: none; + } + } + + .search-input { + color: $white-light; + background: none; + transition: color ease-in-out 0.15s; + } + + .search-input::placeholder { + transition: color ease-in-out 0.15s; + } + + .location-badge { + font-size: 12px; + margin: -4px 4px -4px -4px; + line-height: 25px; + padding: 4px 8px; + border-radius: 2px 0 0 2px; + height: 32px; + transition: border-color ease-in-out 0.15s; + } + + &.search-active { + form { + background-color: rgba($indigo-200, .3); + box-shadow: none; + + .search-input { + color: $gl-text-color; + transition: color ease-in-out 0.15s; + } + + .search-input::placeholder { + color: $gl-text-color-tertiary; + } + + .search-input-wrap { + .search-icon, + .clear-icon { + color: $gl-text-color-tertiary; + transition: color ease-in-out 0.15s; + } + } + } + + .location-badge { + background-color: $nav-badge-bg; + border-color: $border-color; + } + + .search-input-wrap { + .clear-icon { + color: $white-light; + } + } + } } .breadcrumbs { diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 69d19ea2962..cb324ccc440 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -30,10 +30,10 @@ body { .container { padding-top: 0; z-index: 5; -} -.container .content { - margin: 0; + .content { + margin: 0; + } } .navless-container { @@ -82,26 +82,26 @@ body { transition: background-color 0.15s, border-color 0.15s; background-color: $orange-500; border-color: $orange-500; - } - .alert-warning + .alert-warning { - background-color: $orange-600; - border-color: $orange-600; - } + &:only-of-type { + background-color: $orange-500; + border-color: $orange-500; + } - .alert-warning + .alert-warning + .alert-warning { - background-color: $orange-700; - border-color: $orange-700; - } + + .alert-warning { + background-color: $orange-600; + border-color: $orange-600; - .alert-warning + .alert-warning + .alert-warning + .alert-warning { - background-color: $orange-800; - border-color: $orange-800; - } + + .alert-warning { + background-color: $orange-700; + border-color: $orange-700; - .alert-warning:only-of-type { - background-color: $orange-500; - border-color: $orange-500; + + .alert-warning { + background-color: $orange-800; + border-color: $orange-800; + } + } + } } } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index d43f998cb82..511608c618c 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -299,40 +299,40 @@ ul.indent-list { } } -.group-list-tree .avatar-container.content-loading { - position: relative; +.group-list-tree { + .avatar-container.content-loading { + position: relative; - > a, - > a .avatar { - height: 100%; - border-radius: 50%; - } + > a, + > a .avatar { + height: 100%; + border-radius: 50%; + } - > a { - padding: 2px; - } + > a { + padding: 2px; - > a .avatar { - border: 2px solid $white-normal; + .avatar { + border: 2px solid $white-normal; - &.identicon { - line-height: 30px; + &.identicon { + line-height: 30px; + } + } } - } - &::after { - content: ""; - position: absolute; - height: 100%; - width: 100%; - background-color: transparent; - border: 2px outset $kdb-border; - border-radius: 50%; - animation: spin-avatar 3s infinite linear; + &::after { + content: ""; + position: absolute; + height: 100%; + width: 100%; + background-color: transparent; + border: 2px outset $kdb-border; + border-radius: 50%; + animation: spin-avatar 3s infinite linear; + } } -} -.group-list-tree { .folder-toggle-wrap { float: left; line-height: $list-text-height; diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index e3920b5d3d9..0a5a16c09b0 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -173,21 +173,8 @@ ul > li { white-space: nowrap; } -} - -@media(max-width: $screen-xs-max) { - .atwho-view-ul { - width: 350px; - } - - .atwho-view ul li { - overflow: hidden; - text-overflow: ellipsis; - } -} -// TODO: fallback to global style -.atwho-view { + // TODO: fallback to global style .atwho-view-ul { padding: 8px 1px; @@ -220,3 +207,14 @@ } } } + +@media(max-width: $screen-xs-max) { + .atwho-view-ul { + width: 350px; + } + + .atwho-view ul li { + overflow: hidden; + text-overflow: ellipsis; + } +} diff --git a/app/assets/stylesheets/framework/responsive_tables.scss b/app/assets/stylesheets/framework/responsive_tables.scss index 7adb2f113bb..8b7afdbe1a5 100644 --- a/app/assets/stylesheets/framework/responsive_tables.scss +++ b/app/assets/stylesheets/framework/responsive_tables.scss @@ -25,7 +25,10 @@ margin: 0; padding: $gl-padding 0; border: none; - border-bottom: 1px solid $white-normal; + + &:not(:last-child) { + border-bottom: 1px solid $white-normal; + } } } diff --git a/app/assets/stylesheets/framework/secondary-navigation-elements.scss b/app/assets/stylesheets/framework/secondary-navigation-elements.scss index 3fd2549b143..9e1f77e5726 100644 --- a/app/assets/stylesheets/framework/secondary-navigation-elements.scss +++ b/app/assets/stylesheets/framework/secondary-navigation-elements.scss @@ -340,11 +340,64 @@ } } -.project-item-select-holder.btn-group { - display: flex; - max-width: 350px; - overflow: hidden; - float: right; +.page-with-layout-nav { + .right-sidebar { + top: ($header-height + 1) * 2; + } + + &.page-with-sub-nav { + .right-sidebar { + top: ($header-height + 1) * 3; + + &.affix { + top: $header-height; + } + } + } +} + +.with-performance-bar .page-with-layout-nav { + .right-sidebar { + top: ($header-height + 1) * 2 + $performance-bar-height; + } + + &.page-with-sub-nav { + .right-sidebar { + top: ($header-height + 1) * 3 + $performance-bar-height; + + &.affix { + top: $header-height + $performance-bar-height; + } + } + } +} + +@media (max-width: $screen-xs-max) { + .top-area { + flex-flow: row wrap; + + .nav-controls { + $controls-margin: $btn-xs-side-margin - 2px; + flex: 0 0 100%; + + &.controls-flex { + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: center; + padding: 0 0 $gl-padding-top; + } + + .controls-item, + .controls-item-full, + .controls-item:last-child { + flex: 1 1 35%; + display: block; + width: 100%; + margin: $controls-margin; + } + } + } .new-project-item-link { white-space: nowrap; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 621eec4f158..aa35cd9bea4 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -60,22 +60,12 @@ border-radius: $border-radius-base; border: 1px solid $dropdown-border-color; min-width: 175px; - color: $gl-text-color; - z-index: 999; + color: $gl-grayish-blue; } -.select2-drop-mask { - z-index: 998; -} - -.select2-drop.select2-drop-above.select2-drop-active { - border-top: 1px solid $dropdown-border-color; - margin-top: -6px; -} - -.select2-results li.select2-result-with-children > .select2-result-label { - font-weight: $gl-font-weight-bold; - color: $gl-text-color; +.select2-results .select2-result-label, +.select2-more-results { + padding: 10px 15px; } .select2-container-active { @@ -144,58 +134,46 @@ .select2-drop-auto-width & { padding: 15px 15px 5px; } -} -.select2-search input { - padding: 2px 25px 2px 5px; - background: $white-light image-url('select2.png'); - background-repeat: no-repeat; - background-position: right 0 bottom 6px; - border: 1px solid $input-border; - border-radius: $border-radius-default; - transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; - - &:focus { - border-color: $input-border-focus; + input { + padding: 2px 25px 2px 5px; + background: $white-light image-url('select2.png'); + background-repeat: no-repeat; + background-position: right 0 bottom 6px; + border: 1px solid $input-border; + border-radius: $border-radius-default; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + + &:focus { + border-color: $input-border-focus; + } + + &.select2-active { + background-color: $white-light; + background-image: image-url('select2-spinner.gif') !important; + background-repeat: no-repeat; + background-position: right 5px center !important; + background-size: 16px 16px !important; + } } } -.select2-search input.select2-active { - background-color: $white-light; - background-image: image-url('select2-spinner.gif') !important; - background-repeat: no-repeat; - background-position: right 5px center !important; - background-size: 16px 16px !important; +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-ajax-error, +.select2-results .select2-selection-limit { + background: $gray-light; + display: list-item; + padding: 10px 15px; } .select2-results { margin: 0; - padding: #{$gl-padding / 2} 0; - - .select2-no-results, - .select2-searching, - .select2-ajax-error, - .select2-selection-limit { - background: transparent; - padding: #{$gl-padding / 2} $gl-padding; - } - - .select2-result-label, - .select2-more-results { - padding: #{$gl-padding / 2} $gl-padding; - } + padding: 10px 0; - .select2-highlighted { - background: transparent; + li.select2-result-with-children > .select2-result-label { + font-weight: $gl-font-weight-bold; color: $gl-text-color; - - .select2-result-label { - background: $dropdown-item-hover-bg; - } - } - - .select2-result { - padding: 0 1px; } } @@ -212,6 +190,8 @@ } .select2-highlighted { + background: $gl-link-color !important; + .group-result { .group-path { color: $white-light; diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 65b140cd7f8..c3d8f0c61a2 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -217,13 +217,31 @@ $white-gc-bg: #eaf2f5; .cp { color: $white-cp; font-weight: $gl-font-weight-bold; } .c1 { color: $white-c1; font-style: italic; } .cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; } - .gd { color: $white-gd; background-color: $white-gd-bg; } - .gd .x { color: $white-gd-x; background-color: $white-gd-x-bg; } + + .gd { + color: $white-gd; + background-color: $white-gd-bg; + + .x { + color: $white-gd-x; + background-color: $white-gd-x-bg; + } + } + .ge { font-style: italic; } .gr { color: $white-gr; } .gh { color: $white-gh; } - .gi { color: $white-gi; background-color: $white-gi-bg; } - .gi .x { color: $white-gi-x; background-color: $white-gi-x-bg; } + + .gi { + color: $white-gi; + background-color: $white-gi-bg; + + .x { + color: $white-gi-x; + background-color: $white-gi-x-bg; + } + } + .go { color: $white-go; } .gp { color: $white-gp; } .gs { font-weight: $gl-font-weight-bold; } diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss index fbe538ad1d7..658ac26fca9 100644 --- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss +++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss @@ -158,13 +158,31 @@ span.highlight_word { .cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; } .c1 { color: $highlighted-c1; font-style: italic; } .cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; } -.gd { color: $highlighted-gd; background-color: $highlighted-gd-bg; } -.gd .x { color: $highlighted-gd; background-color: $highlighted-gd-x-bg; } + +.gd { + color: $highlighted-gd; + background-color: $highlighted-gd-bg; + + .x { + color: $highlighted-gd; + background-color: $highlighted-gd-x-bg; + } +} + .ge { font-style: italic; } .gr { color: $highlighted-gr; } .gh { color: $highlighted-gh; } -.gi { color: $highlighted-gi; background-color: $highlighted-gi-bg; } -.gi .x { color: $highlighted-gi; background-color: $highlighted-gi-x-bg; } + +.gi { + color: $highlighted-gi; + background-color: $highlighted-gi-bg; + + .x { + color: $highlighted-gi; + background-color: $highlighted-gi-x-bg; + } +} + .go { color: $highlighted-go; } .gp { color: $highlighted-gp; } .gs { font-weight: $gl-font-weight-bold; } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 278ec16bcd9..3683afa07de 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -86,10 +86,7 @@ } @media (min-width: $screen-md-min) { - height: 475px; // Needed for PhantomJS - // scss-lint:disable DuplicateProperty height: calc(100vh - 160px); - // scss-lint:enable DuplicateProperty min-height: 475px; } } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index e87ffe4f374..46978be8ba0 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -68,18 +68,18 @@ &.affix { top: $header-height; - } - // with sidebar - &.affix.sidebar-expanded { - right: 306px; - left: 16px; - } + // with sidebar + &.sidebar-expanded { + right: 306px; + left: 16px; + } - // without sidebar - &.affix.sidebar-collapsed { - right: 16px; - left: 16px; + // without sidebar + &.sidebar-collapsed { + right: 16px; + left: 16px; + } } &.affix-top { diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 2a92673d9fa..82d9be29201 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -22,6 +22,11 @@ } } } + + svg { + width: 136px; + height: 136px; + } } .col-headers { @@ -155,11 +160,6 @@ } } - .landing svg { - width: 136px; - height: 136px; - } - .fa-spinner { font-size: 28px; position: relative; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 09f831dcb29..faa3d1fb4d5 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -380,6 +380,10 @@ } } } + + .line_content { + white-space: pre-wrap; + } } .file-content .diff-file { @@ -387,10 +391,6 @@ border: none; } -.diff-file .line_content { - white-space: pre-wrap; -} - .diff-wrap-lines .line_content { white-space: pre-wrap; } diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 6c1d32bed2f..b5b0f3d9dfa 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -255,23 +255,6 @@ width: 100%; padding: 0; padding-bottom: 100%; -} - -.prometheus-svg-container > svg { - position: absolute; - height: 100%; - width: 100%; - left: 0; - top: 0; - - text { - fill: $gl-text-color; - stroke-width: 0; - } - - .text-metric-bold { - font-weight: $gl-font-weight-bold; - } .label-axis-text { fill: $black; @@ -286,42 +269,51 @@ font-size: 12px; } - .legend-axis-text { - fill: $black; - } + > svg { + position: absolute; + height: 100%; + width: 100%; + left: 0; + top: 0; - .tick { - > line { - stroke: $gray-darker; + .label-axis-text, + .text-metric-usage { + fill: $black; + font-weight: $gl-font-weight-normal; + font-size: 12px; } - > text { - font-size: 12px; + .legend-axis-text { + fill: $black; } - } - .text-metric-title { - font-size: 12px; - } + .tick > text { + font-size: 12px; + } - .y-label-text, - .x-label-text { - fill: $gray-darkest; - } + .text-metric-title { + font-size: 12px; + } - .axis-tick { - stroke: $gray-darker; - } + .y-label-text, + .x-label-text { + fill: $gray-darkest; + } - @media (max-width: $screen-sm-max) { - .label-axis-text, - .text-metric-usage, - .legend-axis-text { - font-size: 8px; + .axis-tick { + stroke: $gray-darker; } - .tick > text { - font-size: 8px; + @media (max-width: $screen-sm-max) { + .label-axis-text, + .text-metric-usage, + .legend-axis-text { + font-size: 8px; + } + + .tick > text { + font-size: 8px; + } } } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 88600a0e6d3..7059a4cfe85 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -127,7 +127,16 @@ } .right-sidebar { - a:not(.btn-retry), + position: absolute; + top: $header-height; + bottom: 0; + right: 0; + transition: width .3s; + background: $gray-light; + z-index: 200; + overflow: hidden; + + a, .btn-link { color: inherit; } @@ -228,17 +237,6 @@ .btn-clipboard:hover { color: $gl-text-color; } -} - -.right-sidebar { - position: absolute; - top: $header-height; - bottom: 0; - right: 0; - transition: width $right-sidebar-transition-duration; - background: $gray-light; - z-index: 200; - overflow: hidden; .issuable-sidebar { width: calc(100% + 100px); diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index cf5f933a762..92d49bd864a 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -109,6 +109,30 @@ border-top-right-radius: $border-radius-default; border-top-left-radius: $border-radius-default; + // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long). + // These styles prevent this from breaking the layout, and only applied when providers are configured. + &.custom-provider-tabs { + flex-wrap: wrap; + + li { + min-width: 85px; + flex-basis: auto; + + // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen. + // We are making somewhat of an assumption about the configuration here: that users do not have more than + // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any + // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border + // above one of the bottom row elements. If you know a better way, please implement it! + &:nth-child(n+5) { + border-top: 1px solid $border-color; + } + } + + a { + font-size: 16px; + } + } + li { flex: 1; text-align: center; @@ -154,32 +178,6 @@ } } - // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long). - // These styles prevent this from breaking the layout, and only applied when providers are configured. - - .new-session-tabs.custom-provider-tabs { - flex-wrap: wrap; - - li { - min-width: 85px; - flex-basis: auto; - - // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen. - // We are making somewhat of an assumption about the configuration here: that users do not have more than - // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any - // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border - // above one of the bottom row elements. If you know a better way, please implement it! - &:nth-child(n+5) { - border-top: 1px solid $border-color; - } - } - - a { - font-size: 16px; - } - } - - .form-control { &:active, &:focus { @@ -231,35 +229,35 @@ margin: 0; padding: 0; height: 100%; -} -// Fixes footer container to bottom of viewport -.devise-layout-html body { - // offset height of fixed header + 1 to avoid scroll - height: calc(100% - 51px); - margin: 0; - padding: 0; + // Fixes footer container to bottom of viewport + body { + // offset height of fixed header + 1 to avoid scroll + height: calc(100% - 51px); + margin: 0; + padding: 0; - .page-wrap { - min-height: 100%; - position: relative; - } + .page-wrap { + min-height: 100%; + position: relative; + } - .footer-container, - hr.footer-fixed { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 40px; - background: $white-light; - } + .footer-container, + hr.footer-fixed { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 40px; + background: $white-light; + } - .navless-container { - padding: 65px 15px; // height of footer + bottom padding of email confirmation link + .navless-container { + padding: 65px 15px; // height of footer + bottom padding of email confirmation link - @media (max-width: $screen-xs-max) { - padding: 0 15px 65px; + @media (max-width: $screen-xs-max) { + padding: 0 15px 65px; + } } } } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 692acf74a58..18c48405ecd 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -49,9 +49,17 @@ width: auto; } } + + &.existing-title { + @media (min-width: $screen-sm-min) { + float: left; + } + } } .member-form-control { + @include new-style-dropdown; + @media (max-width: $screen-xs-max) { padding-bottom: 5px; margin-left: 0; @@ -64,12 +72,6 @@ line-height: 43px; } -.member.existing-title { - @media (min-width: $screen-sm-min) { - float: left; - } -} - .member-search-form { @include new-style-dropdown; @@ -281,7 +283,3 @@ } } } - -.member-form-control { - @include new-style-dropdown; -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 645fc1f3ebb..6e485ebad1b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -156,6 +156,10 @@ &.media > *:first-child { margin-right: 10px; } + + .approve-btn { + margin-right: 5px; + } } .mr-widget-pipeline-graph { @@ -191,6 +195,10 @@ overflow: hidden; word-break: break-all; + &.media > *:first-child { + margin-right: 10px; + } + &.label-truncated { position: relative; display: inline-block; @@ -208,14 +216,7 @@ background-color: $gray-light; } } - } - .mr-widget-help { - padding: 10px 16px 10px 48px; - font-style: italic; - } - - .mr-widget-body { h4 { float: left; font-weight: $gl-font-weight-bold; @@ -238,6 +239,10 @@ margin-right: 7px; } + .approve-btn { + margin-right: 5px; + } + label { font-weight: $gl-font-weight-normal; } @@ -337,6 +342,22 @@ } } + .mini-pipeline-graph-dropdown-menu .mini-pipeline-graph-dropdown-item { + display: flex; + align-items: center; + + .ci-status-text, + .ci-status-icon { + top: 0; + margin-right: 10px; + } + } + + .mr-widget-help { + padding: 10px 16px 10px 48px; + font-style: italic; + } + .ci-coverage { float: right; } @@ -351,12 +372,6 @@ } } -.mr-state-widget .mr-widget-body { - .approve-btn { - margin-right: 5px; - } -} - .mr-widget-body-controls { flex-wrap: wrap; } @@ -470,16 +485,16 @@ padding-bottom: 0; } } -} -.mr-info-list.mr-memory-usage { - p { - float: left; - } + &.mr-memory-usage { + p { + float: left; + } - .memory-graph-container { - float: left; - margin-left: 5px; + .memory-graph-container { + float: left; + margin-left: 5px; + } } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 32039936be7..ae8fa45a2d7 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -66,6 +66,15 @@ height: 6px; margin: 0; } + + .sidebar-collapsed-icon { + clear: both; + padding: 15px 5px 5px; + + .progress { + margin: 5px 0; + } + } } .collapsed-milestone-date { @@ -93,17 +102,6 @@ margin-right: 0; } - .milestone-progress { - .sidebar-collapsed-icon { - clear: both; - padding: 15px 5px 5px; - - .progress { - margin: 5px 0; - } - } - } - .right-sidebar-collapsed & { .reference { border-top: 1px solid $border-gray-normal; @@ -156,18 +154,16 @@ .status-box { margin-top: 0; - } - - .milestone-buttons { - margin-left: auto; - } - - .status-box { order: 1; } .milestone-buttons { + margin-left: auto; order: 2; + + .verbose { + display: none; + } } .header-text-content { @@ -175,10 +171,6 @@ width: 100%; } - .milestone-buttons .verbose { - display: none; - } - @media (min-width: $screen-xs-min) { .milestone-buttons .verbose { display: inline; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index f0cad30f4f3..5127307c5e7 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -111,24 +111,9 @@ margin: auto; align-items: center; - .icon { - margin-right: $issuable-warning-icon-margin; - } -} - -.disabled-comment .issuable-note-warning { - border: none; - border-radius: $label-border-radius; - padding-top: $gl-vert-padding; - padding-bottom: $gl-vert-padding; - - .icon svg { - position: relative; - top: 2px; - margin-right: $btn-xs-side-margin; - width: $gl-font-size; - height: $gl-font-size; - fill: $orange-600; + + .md-area { + border-top-left-radius: 0; + border-top-right-radius: 0; } } @@ -155,11 +140,6 @@ } } -.issuable-note-warning + .md-area { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - .discussion-form { background-color: $white-light; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 312917bd13a..ca363c6eac4 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -312,57 +312,72 @@ ul.notes { } } -.diff-file .notes_holder { - font-family: $regular_font; +.diff-file { + .is-over { + .add-diff-note { + display: inline-block; + } + } - td { - border: 1px solid $white-normal; - border-left: none; + // Merge request notes in diffs + // Diff is inline + .notes_content .note-header .note-headline-light { + display: inline-block; + position: relative; + } - &.notes_line { - vertical-align: middle; - text-align: center; - padding: 10px 0; - background: $gray-light; - color: $text-color; - } + .notes_holder { + font-family: $regular_font; - &.notes_line2 { - text-align: center; - padding: 10px 0; - border-left: 1px solid $note-line2-border !important; - } + td { + border: 1px solid $white-normal; + border-left: none; - &.notes_content { - background-color: $gray-light; - border-width: 1px 0; - padding: 0; - vertical-align: top; - white-space: normal; + &.notes_line { + vertical-align: middle; + text-align: center; + padding: 10px 0; + background: $gray-light; + color: $text-color; + } - &.parallel { - border-width: 1px; + &.notes_line2 { + text-align: center; + padding: 10px 0; + border-left: 1px solid $note-line2-border !important; } - .discussion-notes { - &:not(:first-child) { - border-top: 1px solid $white-normal; - margin-top: 20px; + &.notes_content { + background-color: $gray-light; + border-width: 1px 0; + padding: 0; + vertical-align: top; + white-space: normal; + + &.parallel { + border-width: 1px; } - &:not(:last-child) { - border-bottom: 1px solid $white-normal; - margin-bottom: 20px; + .discussion-notes { + &:not(:first-child) { + border-top: 1px solid $white-normal; + margin-top: 20px; + } + + &:not(:last-child) { + border-bottom: 1px solid $white-normal; + margin-bottom: 20px; + } } - } - .notes { - background-color: $white-light; - } + .notes { + background-color: $white-light; + } - a code { - top: 0; - margin-right: 0; + a code { + top: 0; + margin-right: 0; + } } } } @@ -457,8 +472,9 @@ ul.notes { margin-left: 10px; color: $gray-darkest; - .btn-group > .discussion-next-btn { - margin-left: -1px; + @include notes-media('max', $screen-md-max) { + float: none; + margin-left: 0; } } @@ -469,8 +485,6 @@ ul.notes { flex-shrink: 0; display: inline-flex; align-items: center; - // For PhantomJS that does not support flex - float: right; margin-left: 10px; color: $gray-darkest; @@ -481,7 +495,6 @@ ul.notes { } .more-actions { - float: right; // phantomjs fallback display: flex; align-items: flex-end; @@ -502,13 +515,6 @@ ul.notes { min-width: 180px; } -.discussion-actions { - @include notes-media('max', $screen-md-max) { - float: none; - margin-left: 0; - } -} - .note-actions-item { margin-left: 12px; display: flex; @@ -665,14 +671,6 @@ ul.notes { } } -.diff-file { - .is-over { - .add-diff-note { - display: inline-block; - } - } -} - .disabled-comment { background-color: $gray-light; border-radius: $border-radius-base; @@ -714,20 +712,20 @@ ul.notes { svg path { fill: $gray-darkest; } - } - .btn.discussion-create-issue-btn { - margin-left: -4px; - border-radius: 0; - border-right: 0; + &.discussion-create-issue-btn { + margin-left: -4px; + border-radius: 0; + border-right: 0; - a { - padding: 0; - line-height: 0; + a { + padding: 0; + line-height: 0; - &:hover { - text-decoration: none; - border: 0; + &:hover { + text-decoration: none; + border: 0; + } } } } @@ -801,12 +799,3 @@ ul.notes { .line-resolve-text { vertical-align: middle; } - -// Merge request notes in diffs -.diff-file { - // Diff is inline - .notes_content .note-header .note-headline-light { - display: inline-block; - position: relative; - } -} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 6604b471560..2a8cbc61af7 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -175,6 +175,25 @@ } } + /** + * Play button with icon in dropdowns + */ + .no-btn { + border: none; + background: none; + outline: none; + width: 100%; + text-align: left; + + .icon-play { + position: relative; + top: 2px; + margin-right: 5px; + height: 13px; + width: 12px; + } + } + .duration, .finished-at { color: $gl-text-color-secondary; @@ -450,48 +469,48 @@ @extend .build-content:hover; } - // Action Icons in big pipeline-graph nodes - .ci-action-icon-container.ci-action-icon-wrapper { - height: 30px; - width: 30px; - background: $white-light; - border: 1px solid $border-color; - border-radius: 100%; - display: block; + .ci-action-icon-container { + position: absolute; + right: 5px; + top: 5px; - &:hover { - background-color: $stage-hover-bg; - border: 1px solid $dropdown-toggle-active-border-color; - } + // Action Icons in big pipeline-graph nodes + &.ci-action-icon-wrapper { + height: 30px; + width: 30px; + background: $white-light; + border: 1px solid $border-color; + border-radius: 100%; + display: block; - svg { - fill: $gl-text-color-secondary; - position: relative; - left: 5px; - top: 2px; - width: 18px; - height: 18px; - } + &:hover { + background-color: $stage-hover-bg; + border: 1px solid $dropdown-toggle-active-border-color; + + svg { + fill: $gl-text-color; + } + } - &.play { svg { - width: #{$ci-action-icon-size - 8}; - height: #{$ci-action-icon-size - 8}; - left: 8px; + fill: $gl-text-color-secondary; + position: relative; + left: 5px; + top: 2px; + width: 18px; + height: 18px; } - } - &:hover svg { - fill: $gl-text-color; + &.play { + svg { + width: #{$ci-action-icon-size - 8}; + height: #{$ci-action-icon-size - 8}; + left: 8px; + } + } } } - .ci-action-icon-container { - position: absolute; - right: 5px; - top: 5px; - } - .ci-status-icon svg { height: 20px; width: 20px; @@ -735,6 +754,28 @@ button.mini-pipeline-graph-dropdown-toggle { left: -3px; position: relative; top: -2px; + + &.icon-action-stop, + &.icon-action-cancel { + width: 12px; + height: 12px; + top: 1px; + left: -1px; + } + + &.icon-action-play { + width: 11px; + height: 11px; + top: 1px; + left: 1px; + } + + &.icon-action-retry { + width: 16px; + height: 16px; + top: 0; + left: -3px; + } } &:hover svg, @@ -751,27 +792,6 @@ button.mini-pipeline-graph-dropdown-toggle { } } - svg.icon-action-stop, - svg.icon-action-cancel { - width: 12px; - height: 12px; - top: 1px; - left: -1px; - } - - svg.icon-action-play { - width: 11px; - height: 11px; - top: 1px; - left: 1px; - } - - svg.icon-action-retry { - width: 16px; - height: 16px; - top: 0; - left: -3px; - } } @@ -840,13 +860,10 @@ button.mini-pipeline-graph-dropdown-toggle { left: 100%; top: -10px; box-shadow: 0 1px 5px $black-transparent; -} - -/** - * Top arrow in the dropdown in the big pipeline graph - */ -.big-pipeline-graph-dropdown-menu { + /** + * Top arrow in the dropdown in the big pipeline graph + */ &::before, &::after { content: ''; @@ -908,22 +925,23 @@ button.mini-pipeline-graph-dropdown-toggle { margin-top: 1px; border-bottom-color: $white-light; } -} -/** - * Center dropdown menu in mini graph - */ -.mini-pipeline-graph-dropdown-menu.dropdown-menu { - transform: translate(-80%, 0); - min-width: 150px; + /** + * Center dropdown menu in mini graph + */ + &.dropdown-menu { + transform: translate(-80%, 0); + min-width: 150px; - @media(min-width: $screen-md-min) { - transform: translate(-50%, 0); - right: auto; - left: 50%; - min-width: 240px; + @media(min-width: $screen-md-min) { + transform: translate(-50%, 0); + right: auto; + left: 50%; + min-width: 240px; + } } } + /** * Terminal */ @@ -947,25 +965,6 @@ button.mini-pipeline-graph-dropdown-toggle { } } -/** - * Play button with icon in dropdowns - */ -.ci-table .no-btn { - border: none; - background: none; - outline: none; - width: 100%; - text-align: left; - - .icon-play { - position: relative; - top: 2px; - margin-right: 5px; - height: 13px; - width: 12px; - } -} - .ci-header-container { min-height: 55px; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index bd385db9692..b0c3474e3d5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -88,7 +88,8 @@ transition: background 2s ease-out; &:disabled { - opacity: 0.75; + opacity: 0.5; + pointer-events: none; } .highlight-changes & { @@ -778,35 +779,35 @@ a.deploy-project-label { .nav { padding-top: 12px; padding-bottom: 12px; - } - .nav > li { - display: inline-block; + > li { + display: inline-block; - &:not(:last-child) { - margin-right: $gl-padding; - } + &:not(:last-child) { + margin-right: $gl-padding; + } - &.right { - vertical-align: top; - margin-top: 0; + &.right { + vertical-align: top; + margin-top: 0; - @media (min-width: $screen-lg-min) { - float: right; + @media (min-width: $screen-lg-min) { + float: right; + } } - } - } - .nav > li > a { - padding: 0; - background-color: transparent; - font-size: 14px; - line-height: 29px; - color: $notes-light-color; + > a { + padding: 0; + background-color: transparent; + font-size: 14px; + line-height: 29px; + color: $notes-light-color; - &:hover, - &:focus { - color: $gl-text-color; + &:hover, + &:focus { + color: $gl-text-color; + } + } } } @@ -1160,13 +1161,6 @@ pre.light-well { } } -.project-repo-select { - &.disabled { - opacity: 0.5; - pointer-events: none; - } -} - .variables-table { table-layout: fixed; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index db0a04a5eb3..eed711b1b66 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -78,6 +78,10 @@ input[type="checkbox"]:hover { } .search-input-wrap { + // Fallback if flexbox is not supported + display: inline-block; + width: 100%; + .search-icon, .clear-icon { position: absolute; diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 968a94c68cf..8b9b47a41bc 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -241,11 +241,11 @@ margin-left: 5px; background: $badge-bg; } - } - /* Ensure we don't add border if there's only single li */ - li + li { - border-top: 1px solid $border-color; + /* Ensure we don't add border if there's only single li */ + + li { + border-top: 1px solid $border-color; + } } } } diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss index bfe065dbbaf..2bf0bedb1f5 100644 --- a/app/assets/stylesheets/pages/sherlock.scss +++ b/app/assets/stylesheets/pages/sherlock.scss @@ -5,10 +5,10 @@ table .sherlock-code { .sherlock-code { pre { word-wrap: normal; - } - pre code { - white-space: pre; + code { + white-space: pre; + } } } @@ -21,13 +21,13 @@ table .sherlock-code { text-align: right; padding: 0 10px !important; } + + .slow { + color: $red-500; + font-weight: $gl-font-weight-bold; + } } .sherlock-file-sample pre { padding-top: 28px !important; } - -.sherlock-line-samples-table .slow { - color: $red-500; - font-weight: $gl-font-weight-bold; -} diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss index dfa4d033fb8..cede147d559 100644 --- a/app/assets/stylesheets/pages/stat_graph.scss +++ b/app/assets/stylesheets/pages/stat_graph.scss @@ -40,16 +40,16 @@ @media (max-width: $screen-xs-max) { width: 100%; } - } - .person .spark { - display: block; - background: $stat-graph-common-bg; - width: 100%; - } + .spark { + display: block; + background: $stat-graph-common-bg; + width: 100%; + } - .person .area-contributor { - fill: $stat-graph-orange-fill; + .area-contributor { + fill: $stat-graph-orange-fill; + } } } diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index b7d4e7bf582..e150f96f3fa 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -161,10 +161,10 @@ ul.wiki-pages-list.content-list { list-style: none; margin-left: 0; padding-left: 15px; - } - ul li { - padding: 5px 0; + li { + padding: 5px 0; + } } } diff --git a/app/assets/stylesheets/test.scss b/app/assets/stylesheets/test.scss index 06733b7f1a9..e65b49c36f3 100644 --- a/app/assets/stylesheets/test.scss +++ b/app/assets/stylesheets/test.scss @@ -4,11 +4,6 @@ -ms-transition: none !important; -webkit-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; -moz-animation: none !important; -o-animation: none !important; diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index 2b6afaa6233..738afd612f0 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -94,10 +94,9 @@ module LfsRequest @storage_project ||= begin result = project - loop do - break unless result.forked? - result = result.forked_from_project - end + # TODO: Make this go to the fork_network root immeadiatly + # dependant on the discussion in: https://gitlab.com/gitlab-org/gitlab-ce/issues/39769 + result = result.fork_source while result.forked? result end diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index 1126f706393..fb9c942d302 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -4,6 +4,7 @@ module NotesActions included do before_action :set_polling_interval_header, only: [:index] + before_action :noteable, only: :index before_action :authorize_admin_note!, only: [:update, :destroy] before_action :note_project, only: [:create] end @@ -188,7 +189,7 @@ module NotesActions end def noteable - @noteable ||= notes_finder.target + @noteable ||= notes_finder.target || render_404 end def last_fetched_at diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d085c1a0e57..f48d47953e4 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -110,7 +110,15 @@ module ProjectsHelper def remove_fork_project_message(project) _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") % - { forked_from_project: @project.forked_from_project.name_with_namespace } + { forked_from_project: fork_source_name(project) } + end + + def fork_source_name(project) + if @project.fork_source + @project.fork_source.full_name + else + @project.fork_network&.deleted_root_project_name + end end def project_nav_tabs @@ -140,8 +148,8 @@ module ProjectsHelper def can_change_visibility_level?(project, current_user) return false unless can?(current_user, :change_visibility_level, project) - if project.forked? - project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE + if project.fork_source + project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE else true end diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb index 218e37a5312..7f1728e8c77 100644 --- a/app/models/fork_network.rb +++ b/app/models/fork_network.rb @@ -12,4 +12,8 @@ class ForkNetwork < ActiveRecord::Base def find_forks_in(other_projects) projects.where(id: other_projects) end + + def merge_requests + MergeRequest.where(target_project: projects) + end end diff --git a/app/models/group.rb b/app/models/group.rb index 4e8023cdb7f..c660de7fcb6 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -42,6 +42,7 @@ class Group < Namespace after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement + after_update :path_changed_hook, if: :path_changed? class << self def supports_nested_groups? @@ -295,6 +296,12 @@ class Group < Namespace list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten end + def full_path_was + return path_was unless has_parent? + + "#{parent.full_path}/#{path_was}" + end + private def update_two_factor_requirement @@ -303,6 +310,10 @@ class Group < Namespace users.find_each(&:update_two_factor_requirement) end + def path_changed_hook + system_hook_service.execute_hooks_for(self, :rename) + end + def visibility_level_allowed_by_parent return if visibility_level_allowed_by_parent? diff --git a/app/models/project.rb b/app/models/project.rb index a42e2553935..f387025c161 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1043,6 +1043,10 @@ class Project < ActiveRecord::Base !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) end + def fork_source + forked_from_project || fork_network&.root_project + end + def personal? !group end @@ -1491,7 +1495,8 @@ class Project < ActiveRecord::Base { key: 'CI_PROJECT_PATH', value: full_path, public: true }, { key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true }, - { key: 'CI_PROJECT_URL', value: web_url, public: true } + { key: 'CI_PROJECT_URL', value: web_url, public: true }, + { key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level), public: true } ] end diff --git a/app/models/user.rb b/app/models/user.rb index 6c9349ed9dd..bcda4564595 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -168,6 +168,7 @@ class User < ActiveRecord::Base before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) } before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? } after_save :ensure_namespace_correct + after_update :username_changed_hook, if: :username_changed? after_destroy :post_destroy_hook after_commit :update_emails_with_primary_email, on: :update, if: -> { previous_changes.key?('email') } after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') } @@ -871,6 +872,10 @@ class User < ActiveRecord::Base end end + def username_changed_hook + system_hook_service.execute_hooks_for(self, :rename) + end + def post_destroy_hook log_info("User \"#{name}\" (#{email}) was removed") system_hook_service.execute_hooks_for(self, :destroy) diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb index 2b82e5732e4..c499f384426 100644 --- a/app/services/projects/unlink_fork_service.rb +++ b/app/services/projects/unlink_fork_service.rb @@ -3,18 +3,24 @@ module Projects def execute return unless @project.forked? - @project.forked_from_project.lfs_objects.find_each do |lfs_object| - lfs_object.projects << @project + if fork_source = @project.fork_source + fork_source.lfs_objects.find_each do |lfs_object| + lfs_object.projects << @project + end + + refresh_forks_count(fork_source) end - merge_requests = @project.forked_from_project.merge_requests.opened.from_project(@project) + merge_requests = @project.fork_network + .merge_requests + .opened + .where.not(target_project: @project) + .from_project(@project) merge_requests.each do |mr| ::MergeRequests::CloseService.new(@project, @current_user).execute(mr) end - refresh_forks_count(@project.forked_from_project) - @project.fork_network_member.destroy @project.forked_project_link.destroy end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 5d275967821..911cc919bb8 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -35,24 +35,22 @@ class SystemHooksService data[:old_path_with_namespace] = model.old_path_with_namespace end when User - data.merge!({ - name: model.name, - email: model.email, - user_id: model.id, - username: model.username - }) + data.merge!(user_data(model)) + + if event == :rename + data[:old_username] = model.username_was + end when ProjectMember data.merge!(project_member_data(model)) when Group - owner = model.owner + data.merge!(group_data(model)) - data.merge!( - name: model.name, - path: model.path, - group_id: model.id, - owner_name: owner.respond_to?(:name) ? owner.name : nil, - owner_email: owner.respond_to?(:email) ? owner.email : nil - ) + if event == :rename + data.merge!( + old_path: model.path_was, + old_full_path: model.full_path_was + ) + end when GroupMember data.merge!(group_member_data(model)) end @@ -104,6 +102,19 @@ class SystemHooksService } end + def group_data(model) + owner = model.owner + + { + name: model.name, + path: model.path, + full_path: model.full_path, + group_id: model.id, + owner_name: owner.try(:name), + owner_email: owner.try(:email) + } + end + def group_member_data(model) { group_name: model.group.name, @@ -116,4 +127,13 @@ class SystemHooksService group_access: model.human_access } end + + def user_data(model) + { + name: model.name, + email: model.email, + user_id: model.id, + username: model.username + } + end end diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 619b632918e..1d644dda177 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,6 +1,5 @@ - empty_repo = @project.empty_repo? - fork_network = @project.fork_network -- forked_from_project = @project.forked_from_project || fork_network&.root_project .project-home-panel.text-center{ class: ("empty-project" if empty_repo) } .limit-container-width{ class: container_class } .avatar-container.s70.project-avatar @@ -16,13 +15,13 @@ - if @project.forked? %p - - if forked_from_project + - if @project.fork_source #{ s_('ForkedFromProjectPath|Forked from') } - = link_to project_path(forked_from_project) do - = forked_from_project.full_name + = link_to project_path(@project.fork_source) do + = fork_source_name(@project) - else - deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)') - = deleted_message % { project_name: fork_network.deleted_root_project_name } + = deleted_message % { project_name: fork_source_name(@project) } .project-repo-buttons .count-buttons diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index de1f9fc5d32..799b07046e5 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -65,7 +65,7 @@ = expanded ? 'Collapse' : 'Expand' %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 %label.append-bottom-10{ for: 'cluster-name' } @@ -75,11 +75,11 @@ %span.input-group-addon.clipboard-addon = clipboard_button(text: @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 %h4= _('Advanced settings') %button.btn.js-settings-toggle = expanded ? 'Collapse' : 'Expand' %p= s_('ClusterIntegration|Manage Cluster integration on your GitLab project') - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = render 'advanced_settings' diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 5703ef1d4bb..5ebeae5c35f 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -173,7 +173,10 @@ %p This will remove the fork relationship to source project = succeed "." do - = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) + - if @project.fork_source + = link_to(fork_source_name(@project), project_path(@project.fork_source)) + - else + = fork_source_name(@project) = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| %p %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. diff --git a/changelogs/unreleased/37473-expose-project-visibility-as-ci-variable.yml b/changelogs/unreleased/37473-expose-project-visibility-as-ci-variable.yml new file mode 100644 index 00000000000..f6906a3b0e0 --- /dev/null +++ b/changelogs/unreleased/37473-expose-project-visibility-as-ci-variable.yml @@ -0,0 +1,5 @@ +--- +title: Expose project visibility as CI variable - CI_PROJECT_VISIBILITY +merge_request: 15193 +author: +type: added diff --git a/changelogs/unreleased/39054-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml b/changelogs/unreleased/39054-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml deleted file mode 100644 index 47bf30ecb5a..00000000000 --- a/changelogs/unreleased/39054-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Stop merge requests with thousands of commits from timing out -merge_request: 15063 -author: -type: performance diff --git a/changelogs/unreleased/39188-change-default-disabled-merge-message.yml b/changelogs/unreleased/39188-change-default-disabled-merge-message.yml deleted file mode 100644 index 7de65f5c3f6..00000000000 --- a/changelogs/unreleased/39188-change-default-disabled-merge-message.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update default disabled merge request widget message to reflect a general failure -merge_request: 14960 -author: -type: changed diff --git a/changelogs/unreleased/39366-email-confirmation-fails.yml b/changelogs/unreleased/39366-email-confirmation-fails.yml deleted file mode 100644 index a5568670c70..00000000000 --- a/changelogs/unreleased/39366-email-confirmation-fails.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Fix bug preventing secondary emails from being confirmed' -merge_request: 15010 -author: -type: fixed diff --git a/changelogs/unreleased/39441-bring-edit-form-back.yml b/changelogs/unreleased/39441-bring-edit-form-back.yml deleted file mode 100644 index 025417e4da9..00000000000 --- a/changelogs/unreleased/39441-bring-edit-form-back.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix editing issue description in mobile view -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/39495-fix-bitbucket-login.yml b/changelogs/unreleased/39495-fix-bitbucket-login.yml deleted file mode 100644 index b48d557108b..00000000000 --- a/changelogs/unreleased/39495-fix-bitbucket-login.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix bitbucket login -merge_request: 15051 -author: -type: fixed diff --git a/changelogs/unreleased/39639-clusters-poll.yml b/changelogs/unreleased/39639-clusters-poll.yml deleted file mode 100644 index f0a82f58b19..00000000000 --- a/changelogs/unreleased/39639-clusters-poll.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds callback functions for initial request in clusters page -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/39776-remove-responsive-table-bottom-border.yml b/changelogs/unreleased/39776-remove-responsive-table-bottom-border.yml new file mode 100644 index 00000000000..52b6a267ced --- /dev/null +++ b/changelogs/unreleased/39776-remove-responsive-table-bottom-border.yml @@ -0,0 +1,5 @@ +--- +title: Fix double border UI bug on pipelines/environments table and pagination +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/bvl-circuitbreaker-backoff.yml b/changelogs/unreleased/bvl-circuitbreaker-backoff.yml deleted file mode 100644 index 5cb90e7c085..00000000000 --- a/changelogs/unreleased/bvl-circuitbreaker-backoff.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Make the circuitbreaker more robust by adding higher thresholds, and multiple - access attempts. -merge_request: 14933 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-circuitbreaker-improvements.yml b/changelogs/unreleased/bvl-circuitbreaker-improvements.yml deleted file mode 100644 index 15cbd5592e9..00000000000 --- a/changelogs/unreleased/bvl-circuitbreaker-improvements.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Store circuitbreaker settings in the database instead of config -merge_request: 14842 -author: -type: changed diff --git a/changelogs/unreleased/bvl-do-not-use-redis-keys.yml b/changelogs/unreleased/bvl-do-not-use-redis-keys.yml deleted file mode 100644 index f703aad2065..00000000000 --- a/changelogs/unreleased/bvl-do-not-use-redis-keys.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Forbid the usage of `Redis#keys` -merge_request: 14889 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-dont-rename-free-names.yml b/changelogs/unreleased/bvl-dont-rename-free-names.yml deleted file mode 100644 index 60a4ec8afbe..00000000000 --- a/changelogs/unreleased/bvl-dont-rename-free-names.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Don't rename paths that were freed up when upgrading -merge_request: 15029 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-fix-push-event-service-for-forks.yml b/changelogs/unreleased/bvl-fix-push-event-service-for-forks.yml deleted file mode 100644 index 2a7d80270ac..00000000000 --- a/changelogs/unreleased/bvl-fix-push-event-service-for-forks.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Only cache last push event for existing projects when pushing to a fork -merge_request: 14989 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-fix-system-hook-project-visibility.yml b/changelogs/unreleased/bvl-fix-system-hook-project-visibility.yml deleted file mode 100644 index a17ed51c9b8..00000000000 --- a/changelogs/unreleased/bvl-fix-system-hook-project-visibility.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use the correct visibility attribute for projects in system hooks -merge_request: 15065 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-unlink-fixes.yml b/changelogs/unreleased/bvl-unlink-fixes.yml new file mode 100644 index 00000000000..685d78f479d --- /dev/null +++ b/changelogs/unreleased/bvl-unlink-fixes.yml @@ -0,0 +1,5 @@ +--- +title: Fix issues with forked projects of which the source was deleted +merge_request: 15150 +author: +type: fixed diff --git a/changelogs/unreleased/dm-ldap-identity-normalize-dn.yml b/changelogs/unreleased/dm-ldap-identity-normalize-dn.yml deleted file mode 100644 index 7ab25f79143..00000000000 --- a/changelogs/unreleased/dm-ldap-identity-normalize-dn.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Normalize LDAP DN when looking up identity -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/enable-scss-lint-mergeable-selector.yml b/changelogs/unreleased/enable-scss-lint-mergeable-selector.yml new file mode 100644 index 00000000000..5f6e0cafe88 --- /dev/null +++ b/changelogs/unreleased/enable-scss-lint-mergeable-selector.yml @@ -0,0 +1,4 @@ +--- +title: Enable MergeableSelector in scss-lint +merge_request: 12810 +author: Takuya Noguchi diff --git a/changelogs/unreleased/fix-add-path-attr-to-wiki-file.yml b/changelogs/unreleased/fix-add-path-attr-to-wiki-file.yml deleted file mode 100644 index 0847b5f6733..00000000000 --- a/changelogs/unreleased/fix-add-path-attr-to-wiki-file.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix broken wiki pages that link to a wiki file -merge_request: 15019 -author: -type: fixed diff --git a/changelogs/unreleased/fix-import-issue-assignees.yml b/changelogs/unreleased/fix-import-issue-assignees.yml deleted file mode 100644 index 063b6afaf08..00000000000 --- a/changelogs/unreleased/fix-import-issue-assignees.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix missing Import/Export issue assignees -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix_global_board_routes_39073.yml b/changelogs/unreleased/fix_global_board_routes_39073.yml deleted file mode 100644 index cc9ae8592db..00000000000 --- a/changelogs/unreleased/fix_global_board_routes_39073.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow boards as top level route -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml b/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml new file mode 100644 index 00000000000..a1685497331 --- /dev/null +++ b/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml @@ -0,0 +1,5 @@ +--- +title: Fix a migration that adds merge_requests_ff_only_enabled column to MR table +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/issue_39176.yml b/changelogs/unreleased/issue_39176.yml new file mode 100644 index 00000000000..6255b51c094 --- /dev/null +++ b/changelogs/unreleased/issue_39176.yml @@ -0,0 +1,5 @@ +--- +title: Render 404 when polling commit notes without having permissions +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/make-merge-jid-handling-less-stateful.yml b/changelogs/unreleased/make-merge-jid-handling-less-stateful.yml deleted file mode 100644 index fe945e822fd..00000000000 --- a/changelogs/unreleased/make-merge-jid-handling-less-stateful.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix widget of locked merge requests not being presented -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/mr-14642.yml b/changelogs/unreleased/mr-14642.yml deleted file mode 100644 index 048cc79e323..00000000000 --- a/changelogs/unreleased/mr-14642.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Auto Devops kubernetes default namespace is now correctly built out of gitlab - project group-name -merge_request: 14642 -author: Mircea Danila Dumitrescu -type: fixed diff --git a/changelogs/unreleased/sh-fix-container-registry-destroy.yml b/changelogs/unreleased/sh-fix-container-registry-destroy.yml deleted file mode 100644 index 21a463da62a..00000000000 --- a/changelogs/unreleased/sh-fix-container-registry-destroy.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix deletion of container registry or images returning an error -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-environment-write-ref.yml b/changelogs/unreleased/sh-fix-environment-write-ref.yml deleted file mode 100644 index 8f291843ebe..00000000000 --- a/changelogs/unreleased/sh-fix-environment-write-ref.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix the writing of invalid environment refs -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/winh-namespace-rename-hooks.yml b/changelogs/unreleased/winh-namespace-rename-hooks.yml new file mode 100644 index 00000000000..f5090b03b74 --- /dev/null +++ b/changelogs/unreleased/winh-namespace-rename-hooks.yml @@ -0,0 +1,5 @@ +--- +title: Add system hooks user_rename and group_rename +merge_request: 15123 +author: +type: changed diff --git a/config/environments/test.rb b/config/environments/test.rb index 1edb6fd39b8..d09e51e766a 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,6 +1,7 @@ Rails.application.configure do # 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::RequestInspectorMiddleware') # Settings specified here will take precedence over those in config/application.rb diff --git a/db/migrate/20150827121444_add_fast_forward_option_to_project.rb b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb index 6f22641077d..35df121519e 100644 --- a/db/migrate/20150827121444_add_fast_forward_option_to_project.rb +++ b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb @@ -8,7 +8,11 @@ class AddFastForwardOptionToProject < ActiveRecord::Migration disable_ddl_transaction! def up - add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false) + # We put condition here because of a mistake we made a couple of years ago + # see https://gitlab.com/gitlab-org/gitlab-ce/issues/39382#note_45716103 + unless column_exists?(:projects, :merge_requests_ff_only_enabled) + add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false) + end end def down diff --git a/db/migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb b/db/migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb deleted file mode 100644 index 6ff98899bcb..00000000000 --- a/db/migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb +++ /dev/null @@ -1,86 +0,0 @@ -class MigrateGcpClustersToNewClustersArchitectures < ActiveRecord::Migration - DOWNTIME = false - - def up - gcp_clusters = ActiveRecord::Base.connection.select_all('SELECT * from gcp_clusters;') - - rows_for_clusters = Array.new - rows_for_cluster_projects = Array.new - rows_for_cluster_providers_gcp = Array.new - rows_for_cluster_platforms_kubernetes = Array.new - - gcp_clusters.each do |gcp_cluster| - rows_for_clusters << params_for_clusters(gcp_cluster) - rows_for_cluster_projects << params_for_cluster_projects(gcp_cluster) - rows_for_cluster_providers_gcp << params_for_cluster_providers_gcp(gcp_cluster) - rows_for_cluster_platforms_kubernetes << params_for_cluster_platforms_kubernetes(gcp_cluster) - end - - Gitlab::Database.bulk_insert('clusters', rows_for_clusters) - Gitlab::Database.bulk_insert('cluster_projects', rows_for_cluster_projects) - Gitlab::Database.bulk_insert('cluster_providers_gcp', rows_for_cluster_providers_gcp) - Gitlab::Database.bulk_insert('cluster_platforms_kubernetes', rows_for_cluster_platforms_kubernetes) - end - - def down - execute('DELETE FROM clusters') - end - - private - - def params_for_clusters(gcp_cluster) - { - id: gcp_cluster['id'], - user_id: gcp_cluster['user_id'], - enabled: gcp_cluster['enabled'], - name: gcp_cluster['gcp_cluster_name'], - provider_type: Clusters::Cluster.provider_types[:gcp], - platform_type: Clusters::Cluster.platform_types[:kubernetes], - created_at: gcp_cluster['created_at'], - updated_at: gcp_cluster['updated_at'] - } - end - - def params_for_cluster_projects(gcp_cluster) - { - cluster_id: gcp_cluster['id'], - project_id: gcp_cluster['project_id'], - created_at: gcp_cluster['created_at'], - updated_at: gcp_cluster['updated_at'] - } - end - - def params_for_cluster_providers_gcp(gcp_cluster) - { - cluster_id: gcp_cluster['id'], - status: gcp_cluster['status'], - status_reason: gcp_cluster['status_reason'], - gcp_project_id: gcp_cluster['gcp_project_id'], - zone: gcp_cluster['gcp_cluster_zone'], - num_nodes: gcp_cluster['gcp_cluster_size'], - machine_type: gcp_cluster['gcp_machine_type'], - operation_id: gcp_cluster['gcp_operation_id'], - endpoint: gcp_cluster['endpoint'], - encrypted_access_token: gcp_cluster['encrypted_gcp_token'], - encrypted_access_token_iv: gcp_cluster['encrypted_gcp_token_iv'], - created_at: gcp_cluster['created_at'], - updated_at: gcp_cluster['updated_at'] - } - end - - def params_for_cluster_platforms_kubernetes(gcp_cluster) - { - cluster_id: gcp_cluster['id'], - api_url: 'https://' + gcp_cluster['endpoint'], - ca_cert: gcp_cluster['ca_cert'], - namespace: gcp_cluster['project_namespace'], - username: gcp_cluster['username'], - encrypted_password: gcp_cluster['encrypted_password'], - encrypted_password_iv: gcp_cluster['encrypted_password_iv'], - encrypted_token: gcp_cluster['encrypted_kubernetes_token'], - encrypted_token_iv: gcp_cluster['encrypted_kubernetes_token_iv'], - created_at: gcp_cluster['created_at'], - updated_at: gcp_cluster['updated_at'] - } - end -end diff --git a/db/post_migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb b/db/post_migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb new file mode 100644 index 00000000000..4758c694563 --- /dev/null +++ b/db/post_migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb @@ -0,0 +1,99 @@ +class MigrateGcpClustersToNewClustersArchitectures < ActiveRecord::Migration + DOWNTIME = false + + class GcpCluster < ActiveRecord::Base + self.table_name = 'gcp_clusters' + + belongs_to :project, class_name: 'Project' + + include EachBatch + end + + class Cluster < ActiveRecord::Base + self.table_name = 'clusters' + + has_many :cluster_projects, class_name: 'ClustersProject' + has_many :projects, through: :cluster_projects, class_name: 'Project' + has_one :provider_gcp, class_name: 'ProvidersGcp' + has_one :platform_kubernetes, class_name: 'PlatformsKubernetes' + + accepts_nested_attributes_for :provider_gcp + accepts_nested_attributes_for :platform_kubernetes + + enum platform_type: { + kubernetes: 1 + } + + enum provider_type: { + user: 0, + gcp: 1 + } + end + + class Project < ActiveRecord::Base + self.table_name = 'projects' + + has_one :cluster_project, class_name: 'ClustersProject' + has_one :cluster, through: :cluster_project, class_name: 'Cluster' + end + + class ClustersProject < ActiveRecord::Base + self.table_name = 'cluster_projects' + + belongs_to :cluster, class_name: 'Cluster' + belongs_to :project, class_name: 'Project' + end + + class ProvidersGcp < ActiveRecord::Base + self.table_name = 'cluster_providers_gcp' + end + + class PlatformsKubernetes < ActiveRecord::Base + self.table_name = 'cluster_platforms_kubernetes' + end + + def up + GcpCluster.all.find_each(batch_size: 1) do |gcp_cluster| + Cluster.create( + enabled: gcp_cluster.enabled, + user_id: gcp_cluster.user_id, + name: gcp_cluster.gcp_cluster_name, + provider_type: Cluster.provider_types[:gcp], + platform_type: Cluster.platform_types[:kubernetes], + projects: [gcp_cluster.project], + provider_gcp_attributes: { + status: gcp_cluster.status, + status_reason: gcp_cluster.status_reason, + gcp_project_id: gcp_cluster.gcp_project_id, + zone: gcp_cluster.gcp_cluster_zone, + num_nodes: gcp_cluster.gcp_cluster_size, + machine_type: gcp_cluster.gcp_machine_type, + operation_id: gcp_cluster.gcp_operation_id, + endpoint: gcp_cluster.endpoint, + encrypted_access_token: gcp_cluster.encrypted_gcp_token, + encrypted_access_token_iv: gcp_cluster.encrypted_gcp_token_iv + }, + platform_kubernetes_attributes: { + cluster_id: gcp_cluster.id, + api_url: api_url(gcp_cluster.endpoint), + ca_cert: gcp_cluster.ca_cert, + namespace: gcp_cluster.project_namespace, + username: gcp_cluster.username, + encrypted_password: gcp_cluster.encrypted_password, + encrypted_password_iv: gcp_cluster.encrypted_password_iv, + encrypted_token: gcp_cluster.encrypted_kubernetes_token, + encrypted_token_iv: gcp_cluster.encrypted_kubernetes_token_iv + } ) + end + end + + def down + execute('DELETE FROM clusters') + end + + private + + def api_url(endpoint) + endpoint ? 'https://' + endpoint : nil + end +end diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 535ed351366..a9e6bda9916 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -66,6 +66,7 @@ future GitLab releases.** | **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name | | **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. | | **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project | +| **CI_PROJECT_VISIBILITY** | 10.3 | all | The project visibility (internal, private, public) | | **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry | | **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project | | **CI_REGISTRY_PASSWORD** | 9.0 | all | The password to use to push containers to the GitLab Container Registry | diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index a45a4eb9e49..f2a9b1d769b 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -1,6 +1,24 @@ # System hooks -Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `project_update`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. +Your GitLab instance can perform HTTP POST requests on the following events: + +- `project_create` +- `project_destroy` +- `project_rename` +- `project_transfer` +- `project_update` +- `user_add_to_team` +- `user_remove_from_team` +- `user_create` +- `user_destroy` +- `user_rename` +- `key_create` +- `key_destroy` +- `group_create` +- `group_destroy` +- `group_rename` +- `user_add_to_group` +- `user_remove_from_group` The triggers for most of these are self-explanatory, but `project_update` and `project_rename` deserve some clarification: `project_update` is fired any time an attribute of a project is changed (name, description, tags, etc.) *unless* the `path` attribute is also changed. In that case, a `project_rename` is triggered instead (so that, for instance, if all you care about is the repo URL, you can just listen for `project_rename`). @@ -72,6 +90,9 @@ X-Gitlab-Event: System Hook } ``` +Note that `project_rename` is not triggered if the namespace changes. +Please refer to `group_rename` and `user_rename` for that case. + **Project transferred:** ```json @@ -175,6 +196,21 @@ X-Gitlab-Event: System Hook } ``` +**User renamed:** + +```json +{ + "event_name": "user_rename", + "created_at": "2017-11-01T11:21:04Z", + "updated_at": "2017-11-01T14:04:47Z", + "name": "new-name", + "email": "best-email@example.tld", + "user_id": 58, + "username": "new-exciting-name", + "old_username": "old-boring-name" +} +``` + **Key added** ```json @@ -209,13 +245,15 @@ X-Gitlab-Event: System Hook "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_create", "name": "StoreCloud", - "owner_email": "johnsmith@gmail.com", - "owner_name": "John Smith", + "owner_email": null, + "owner_name": null, "path": "storecloud", "group_id": 78 } ``` +`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675. + **Group removed:** ```json @@ -224,13 +262,35 @@ X-Gitlab-Event: System Hook "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_destroy", "name": "StoreCloud", - "owner_email": "johnsmith@gmail.com", - "owner_name": "John Smith", + "owner_email": null, + "owner_name": null, "path": "storecloud", "group_id": 78 } ``` +`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675. + +**Group renamed:** + +```json +{ + "event_name": "group_rename", + "created_at": "2017-10-30T15:09:00Z", + "updated_at": "2017-11-01T10:23:52Z", + "name": "Better Name", + "path": "better-name", + "full_path": "parent-group/better-name", + "group_id": 64, + "owner_name": null, + "owner_email": null, + "old_path": "old-name", + "old_full_path": "parent-group/old-name" +} +``` + +`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675. + **New Group Member:** ```json diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 042cde3f01e..1cfdabac248 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -144,6 +144,12 @@ has a `.gitlab-ci.yml` or not: All you need to do is remove your existing `.gitlab-ci.yml`, and you can even do that in a branch to test Auto DevOps before committing to `master`. +NOTE: **Note:** +If you are a GitLab Administrator, you can enable Auto DevOps instance wide +in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that, +all the projects that haven't explicitly set an option will have Auto DevOps +enabled by default. + ## Stages of Auto DevOps The following sections describe the stages of Auto DevOps. Read them carefully diff --git a/features/steps/profile/notifications.rb b/features/steps/profile/notifications.rb index 7e339443b75..f8eb0f01de8 100644 --- a/features/steps/profile/notifications.rb +++ b/features/steps/profile/notifications.rb @@ -11,7 +11,7 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps end step 'I select Mention setting from dropdown' do - first(:link, "On mention").trigger('click') + first(:link, "On mention").click end step 'I should see Notification saved message' do diff --git a/features/steps/project/commits/branches.rb b/features/steps/project/commits/branches.rb index ccaf3237815..c3ae33d2aa9 100644 --- a/features/steps/project/commits/branches.rb +++ b/features/steps/project/commits/branches.rb @@ -40,6 +40,7 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps step 'I submit new branch form with invalid name' do fill_in 'branch_name', with: '1.0 stable' + page.find("body").click # defocus the branch_name input select_branch('master') click_button 'Create branch' end @@ -70,17 +71,16 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps step "I click branch 'improve/awesome' delete link" do page.within '.js-branch-improve\/awesome' do - find('.btn-remove').click - sleep 0.05 + accept_alert { find('.btn-remove').click } end end 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 def select_branch(branch_name) - click_button 'master' + find('.git-revision-dropdown-toggle').click page.within '#new-branch-form .dropdown-menu' do click_link branch_name diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index dac18c537ac..196e0fff63a 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -16,7 +16,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I delete all labels' do page.within '.labels' do page.all('.remove-row').each do - first('.remove-row').click + accept_confirm { first('.remove-row').click } end end end diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index 16a2d4a6f93..33a24e8913a 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -3,6 +3,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps include SharedProject include SharedPaths include SharedMarkdown + include CapybaraHelpers step 'I should see milestone "v2.2"' do milestone = @project.milestones.find_by(title: "v2.2") @@ -65,7 +66,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps end step 'I click link to remove milestone' do - click_link 'Delete' + confirm_modal_if_present { click_link 'Delete' } end step 'I should see no milestones' do diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index c872bd6f861..aa32528a7ca 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -215,7 +215,7 @@ module SharedDiffNote end step 'I click side-by-side diff button' do - find('#parallel-diff-btn').trigger('click') + find('#parallel-diff-btn').click end step 'I see side-by-side diff button' do @@ -227,12 +227,11 @@ module SharedDiffNote end def click_diff_line(code) - find(".line_holder[id='#{code}'] td:nth-of-type(1)").trigger 'mouseover' - find(".line_holder[id='#{code}'] button").trigger 'click' + find(".line_holder[id='#{code}'] button").click end 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 button[data-line-code='#{code}']").trigger 'click' + find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').hover + find(".line_holder.parallel button[data-line-code='#{code}']").click end end diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index 0cd7b506a95..95f0cd2156e 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -14,7 +14,7 @@ module SharedNote find('.more-actions').click find('.more-actions .dropdown-menu li', match: :first) - find(".js-note-delete").click + accept_confirm { find(".js-note-delete").click } end end diff --git a/features/support/capybara.rb b/features/support/capybara.rb index f4691647d4b..3c4db8b9601 100644 --- a/features/support/capybara.rb +++ b/features/support/capybara.rb @@ -1,22 +1,21 @@ -require 'capybara/poltergeist' require 'capybara-screenshot/spinach' # Give CI some extra time timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 -Capybara.javascript_driver = :poltergeist -Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new( - app, - js_errors: true, - timeout: timeout, - window_size: [1366, 768], - url_whitelist: %w[localhost 127.0.0.1], - url_blacklist: %w[.mp4 .png .gif .avi .bmp .jpg .jpeg], - phantomjs_options: [ - '--load-images=yes' - ] +Capybara.javascript_driver = :chrome +Capybara.register_driver :chrome do |app| + extra_args = [] + extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i + + capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( + chromeOptions: { + 'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args + } ) + + Capybara::Selenium::Driver + .new(app, browser: :chrome, desired_capabilities: capabilities) end Capybara.default_max_wait_time = timeout @@ -24,6 +23,10 @@ Capybara.ignore_hidden_elements = false # Keep only the screenshots generated from the last failing test suite 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 TestEnv.eager_load_driver_server diff --git a/features/support/capybara_helpers.rb b/features/support/capybara_helpers.rb new file mode 100644 index 00000000000..647f8d087c3 --- /dev/null +++ b/features/support/capybara_helpers.rb @@ -0,0 +1,10 @@ +module CapybaraHelpers + def confirm_modal_if_present + if Capybara.current_driver == Capybara.javascript_driver + accept_confirm { yield } + return + end + + yield + end +end diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index aba3e0df382..c2cbd3c16a1 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -46,14 +46,14 @@ module Gitlab # Returns the current real time in a given precision. # - # Returns the time as a Float. + # Returns the time as a Fixnum. def self.real_time(precision = :millisecond) Process.clock_gettime(Process::CLOCK_REALTIME, precision) end # Returns the current monotonic clock time in a given precision. # - # Returns the time as a Float. + # Returns the time as a Fixnum. def self.monotonic_time(precision = :millisecond) Process.clock_gettime(Process::CLOCK_MONOTONIC, precision) end diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb index 69e117f1da9..f2825db59ae 100644 --- a/lib/gitlab/performance_bar/peek_query_tracker.rb +++ b/lib/gitlab/performance_bar/peek_query_tracker.rb @@ -36,7 +36,7 @@ module Gitlab end def track_query(raw_query, bindings, start, finish) - duration = finish - start + duration = (finish - start) * 1000.0 query_info = { duration: duration.round(3), sql: raw_query } PEEK_DB_CLIENT.query_details << query_info diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb index 3489fb251b6..400a552bf99 100644 --- a/lib/gitlab/sherlock/transaction.rb +++ b/lib/gitlab/sherlock/transaction.rb @@ -89,7 +89,9 @@ module Gitlab ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data| next unless same_thread? - track_query(data[:sql].strip, data[:binds], start, finish) + unless data.fetch(:cached, data[:name] == 'CACHE') + track_query(data[:sql].strip, data[:binds], start, finish) + end end end diff --git a/lib/gitlab/testing/request_blocker_middleware.rb b/lib/gitlab/testing/request_blocker_middleware.rb index aa67fa08577..4a8e3c2eee0 100644 --- a/lib/gitlab/testing/request_blocker_middleware.rb +++ b/lib/gitlab/testing/request_blocker_middleware.rb @@ -7,6 +7,7 @@ module Gitlab class RequestBlockerMiddleware @@num_active_requests = Concurrent::AtomicFixnum.new(0) @@block_requests = Concurrent::AtomicBoolean.new(false) + @@slow_requests = Concurrent::AtomicBoolean.new(false) # Returns the number of requests the server is currently processing. def self.num_active_requests @@ -19,9 +20,15 @@ module Gitlab @@block_requests.value = true 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. def self.allow_requests! @@block_requests.value = false + @@slow_requests.value = false end def initialize(app) @@ -33,6 +40,7 @@ module Gitlab if block_requests? block_request(env) else + sleep 0.2 if slow_requests? @app.call(env) end ensure @@ -45,6 +53,10 @@ module Gitlab @@block_requests.true? end + def slow_requests? + @@slow_requests.true? + end + def block_request(env) [503, {}, []] end diff --git a/lib/gitlab/testing/request_inspector_middleware.rb b/lib/gitlab/testing/request_inspector_middleware.rb new file mode 100644 index 00000000000..e387667480d --- /dev/null +++ b/lib/gitlab/testing/request_inspector_middleware.rb @@ -0,0 +1,71 @@ +# 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 diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb new file mode 100644 index 00000000000..33b23db302a --- /dev/null +++ b/spec/controllers/concerns/lfs_request_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe LfsRequest do + include ProjectForksHelper + + controller(Projects::GitHttpClientController) do + # `described_class` is not available in this context + include LfsRequest # rubocop:disable RSpec/DescribedClass + + def show + storage_project + + render nothing: true + end + + def project + @project ||= Project.find(params[:id]) + end + + def download_request? + true + end + + def ci? + false + end + end + + let(:project) { create(:project, :public) } + + before do + stub_lfs_setting(enabled: true) + end + + describe '#storage_project' do + it 'assigns the project as storage project' do + get :show, id: project.id + + expect(assigns(:storage_project)).to eq(project) + end + + it 'assigns the source of a forked project' do + forked_project = fork_project(project) + + get :show, id: forked_project.id + + expect(assigns(:storage_project)).to eq(project) + end + end +end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 1184c55e540..43afcc14957 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -105,6 +105,19 @@ describe Projects::NotesController do expect(note_json[:discussion_html]).to be_nil expect(note_json[:diff_discussion_html]).to be_nil end + + context 'when user cannot read commit' do + before do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(user, :download_code, project).and_return(false) + end + + it 'renders 404' do + get :index, params + + expect(response).to have_gitlab_http_status(404) + end + end end end diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb index 86adaddfcb9..5d4e3e5cf5d 100644 --- a/spec/factories/clusters/platforms/kubernetes.rb +++ b/spec/factories/clusters/platforms/kubernetes.rb @@ -2,6 +2,8 @@ FactoryGirl.define do factory :platform_kubernetes, class: Clusters::Platforms::Kubernetes do cluster namespace nil + api_url 'https://kubernetes.example.com' + token 'a' * 40 trait :configured do api_url 'https://kubernetes.example.com' diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb index 6a97378391b..2abdd3c9ef2 100644 --- a/spec/features/admin/admin_disables_two_factor_spec.rb +++ b/spec/features/admin/admin_disables_two_factor_spec.rb @@ -7,7 +7,7 @@ feature 'Admin disables 2FA for a user' do edit_user(user) page.within('.two-factor-status') do - click_link 'Disable' + accept_confirm { click_link 'Disable' } end page.within('.two-factor-status') do diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 771fb5253da..a5f22848031 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -152,7 +152,7 @@ feature 'Admin Groups' do expect(page).to have_content('Developer') 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) diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 2e65fcc5231..eec44549a03 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -62,14 +62,14 @@ describe 'Admin::Hooks', :js do it 'from hooks list page' do 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 it 'from hook edit page' do visit admin_hooks_path 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 diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb index a5834056a1d..de406d7d966 100644 --- a/spec/features/admin/admin_labels_spec.rb +++ b/spec/features/admin/admin_labels_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'admin issues labels' do it 'deletes all labels', :js do page.within '.labels' do page.all('.btn-remove').each do |remove| - remove.click + accept_confirm { remove.click } wait_for_requests end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 85561511101..1218ea52227 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -73,7 +73,7 @@ feature 'Admin updates settings' do context 'sign-in restrictions', :js 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') end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 388d30828a7..e16eae219a4 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -24,7 +24,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do fill_in "Name", with: name # Set date to 1st of next month - find_field("Expires at").trigger('focus') + find_field("Expires at").click find(".pika-next").click click_on "1" @@ -60,7 +60,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do it "allows revocation of an active impersonation token" do 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(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.") diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index f9f4bd6f5b9..b47f9055d29 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -290,7 +290,7 @@ describe "Admin::Users" do it 'allows group membership to be revoked', :js do page.within(first('.group_member')) do - find('.btn-remove').click + accept_confirm { find('.btn-remove').click } end wait_for_requests @@ -319,7 +319,7 @@ describe "Admin::Users" do 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) end diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 42f5b5eb8dc..f1ac73ff819 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -37,7 +37,7 @@ feature 'Admin uses repository checks' do 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.') end diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb index c480b5b7e34..e4cfcea45a5 100644 --- a/spec/features/boards/add_issues_modal_spec.rb +++ b/spec/features/boards/add_issues_modal_spec.rb @@ -101,7 +101,7 @@ describe 'Issue Boards add issue modal', :js do click_button 'Cancel' end - first('.board-delete').click + accept_confirm { first('.board-delete').click } click_button('Add issues') diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index ebe6939df4c..e8d779f5772 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe 'Issue Boards', :js do include DragTo + include MobileHelpers let(:group) { create(:group, :nested) } let(:project) { create(:project, :public, namespace: group) } @@ -13,7 +14,7 @@ describe 'Issue Boards', :js do project.team << [user, :master] project.team << [user2, :master] - page.driver.set_cookie('sidebar_collapsed', 'true') + set_cookie('sidebar_collapsed', 'true') sign_in(user) end @@ -135,7 +136,7 @@ describe 'Issue Boards', :js do it 'allows user to delete board' do page.within(find('.board:nth-child(2)')) do - find('.board-delete').click + accept_confirm { find('.board-delete').click } end wait_for_requests @@ -150,7 +151,7 @@ describe 'Issue Boards', :js do find('.dropdown-menu-close').click page.within(find('.board:nth-child(2)')) do - find('.board-delete').click + accept_confirm { find('.board-delete').click } end wait_for_requests @@ -379,7 +380,7 @@ describe 'Issue Boards', :js do end it 'filters by milestone' do - set_filter("milestone", "\"#{milestone.title}\"") + set_filter("milestone", "\"#{milestone.title}") click_filter_link(milestone.title) submit_filter @@ -400,7 +401,7 @@ describe 'Issue Boards', :js do end it 'filters by label with space after reload' do - set_filter("label", "\"#{accepting.title}\"") + set_filter("label", "\"#{accepting.title}") click_filter_link(accepting.title) submit_filter @@ -521,7 +522,7 @@ describe 'Issue Boards', :js do end 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') end end @@ -563,6 +564,9 @@ describe 'Issue Boards', :js do end 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, scrollable: '#board-app', list_from_index: list_from_index, diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 4965f803883..9137ab82ff4 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -51,7 +51,7 @@ describe 'Issue Boards', :js do 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') end @@ -171,7 +171,7 @@ describe 'Issue Boards', :js do end page.within(find('.board:nth-child(2)')) do - find('.card:nth-child(2)').trigger('click') + find('.card:nth-child(2)').click end page.within('.assignee') do diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 4fc6956d111..a9530becb65 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -63,8 +63,8 @@ feature 'Contributions Calendar', :js do Event.create(note_comment_params) end - def selected_day_activities - find('.user-calendar-activities').text + def selected_day_activities(visible: true) + find('.user-calendar-activities', visible: visible).text end before do @@ -112,7 +112,7 @@ feature 'Contributions Calendar', :js do end 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 diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb index d5e9de20e59..bef2aa9e0e5 100644 --- a/spec/features/container_registry_spec.rb +++ b/spec/features/container_registry_spec.rb @@ -47,7 +47,7 @@ describe "Container Registry", :js do scenario 'user removes a specific tag from container repository' do visit_container_registry - find('.js-toggle-repo').trigger('click') + find('.js-toggle-repo').click wait_for_requests expect_any_instance_of(ContainerRegistry::Tag) diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index 1213f8c32eb..1c7932e7964 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Dashboard Group' do it 'creates new group', :js do visit dashboard_groups_path - find('.btn-new').trigger('click') + find('.btn-new').click new_path = 'Samurai' new_description = 'Tokugawa Shogunate' diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb index c6873d1923c..d92c002b4e7 100644 --- a/spec/features/dashboard/groups_list_spec.rb +++ b/spec/features/dashboard/groups_list_spec.rb @@ -138,7 +138,7 @@ feature 'Dashboard Groups page', :js do expect(page).not_to have_selector("#group-#{group.id}") # Go to next page - find(".gl-pagination .page:not(.active) a").trigger('click') + find(".gl-pagination .page:not(.active) a").click wait_for_requests diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb index a8919976c31..5b4c00b3c7e 100644 --- a/spec/features/dashboard/issues_spec.rb +++ b/spec/features/dashboard/issues_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'Dashboard Issues' do end 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 expect(find('li[data-user-id="null"] a.is-active')).to be_visible @@ -71,7 +71,7 @@ RSpec.describe 'Dashboard Issues' do describe 'new issue dropdown' 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 expect(page).to have_content(project.name_with_namespace) @@ -80,7 +80,7 @@ RSpec.describe 'Dashboard Issues' do end 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 @@ -93,7 +93,7 @@ RSpec.describe 'Dashboard Issues' do find('#select2-drop-mask', visible: false) 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") diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index f01ba442e58..991d360ccaf 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -25,7 +25,7 @@ feature 'Dashboard Merge Requests' do end 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 expect(page).to have_content(project.name_with_namespace) diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index 39ac68af493..6f916078b1a 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -252,7 +252,7 @@ feature 'Dashboard Todos' do describe 'mark all as done', :js do before do visit dashboard_todos_path - find('.js-todos-mark-all').trigger('click') + find('.js-todos-mark-all').click end it 'shows "All done" message!' do @@ -309,9 +309,9 @@ feature 'Dashboard Todos' do end def mark_all_and_undo - find('.js-todos-mark-all').trigger('click') + find('.js-todos-mark-all').click wait_for_requests - find('.js-todos-undo-all').trigger('click') + find('.js-todos-undo-all').click wait_for_requests end end diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb index 0375d0bf8ff..69d35cdbc72 100644 --- a/spec/features/discussion_comments/commit_spec.rb +++ b/spec/features/discussion_comments/commit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Merge Request', :js do +describe 'Discussion Comments Commit', :js do include RepoHelpers let(:user) { create(:user) } diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb index 1e6389d9a13..4a236c4639b 100644 --- a/spec/features/discussion_comments/snippets_spec.rb +++ b/spec/features/discussion_comments/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Issue', :js do +describe 'Discussion Comments Snippet', :js do let(:user) { create(:user) } let(:project) { create(:project) } let(:snippet) { create(:project_snippet, :private, project: project, author: user) } diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb index c5ec495a418..8d5233d0c0f 100644 --- a/spec/features/explore/new_menu_spec.rb +++ b/spec/features/explore/new_menu_spec.rb @@ -65,9 +65,9 @@ feature 'Top Plus Menu', :js do visit project_path(project) 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) - find('.header-new-project-snippet a').trigger('click') + find('.header-new-project-snippet a').click end expect(page).to have_content('New Snippet') @@ -87,9 +87,9 @@ feature 'Top Plus Menu', :js do visit group_path(group) 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) - find('.header-new-group-project a').trigger('click') + find('.header-new-group-project a').click end expect(page).to have_content('Project path') @@ -155,7 +155,7 @@ feature 'Top Plus Menu', :js do def click_topmenuitem(item_name) 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) click_link item_name end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index cc8906fa969..c1f3d94bc20 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -65,7 +65,7 @@ feature 'Group' do end 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 diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 3223eb20b55..fa4d3a55c62 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -405,7 +405,7 @@ feature 'Issues > Labels bulk assignment' do end def update_issues - find('.update-selected-issues').trigger('click') + find('.update-selected-issues').click wait_for_requests end diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index 1c4649d0ba9..2e4a25ee15d 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -43,15 +43,16 @@ describe 'Dropdown assignee', :js do end 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 it 'should hide loading indicator when loaded' do 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') end diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb index 5e20fb48768..2fb5e7cdba4 100644 --- a/spec/features/issues/filtered_search/dropdown_author_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb @@ -51,9 +51,11 @@ describe 'Dropdown author', :js do end 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 it 'should hide loading indicator when loaded' do diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb index 3012c77f2b9..8db435634fd 100644 --- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb @@ -70,9 +70,11 @@ describe 'Dropdown emoji', :js do end 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 it 'should hide loading indicator when loaded' do diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb index cbc4f8d4c50..18cdb199c70 100644 --- a/spec/features/issues/filtered_search/dropdown_label_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb @@ -66,9 +66,11 @@ describe 'Dropdown label', :js do end 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') end diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb index f6c2e952bea..031eb06723a 100644 --- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb @@ -50,15 +50,16 @@ describe 'Dropdown milestone', :js do end 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 it 'should hide loading indicator when loaded' do 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') end diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 2974016c6a7..b3c50964810 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -139,7 +139,7 @@ describe 'Filter issues', :js do input_filtered_search('label:none') expect_tokens([label_token('none', false)]) - expect_issues_list_count(8) + expect_issues_list_count(4) expect_filtered_search_input_empty end diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb index 4fff056cb70..f355cec3ba9 100644 --- a/spec/features/issues/filtered_search/recent_searches_spec.rb +++ b/spec/features/issues/filtered_search/recent_searches_spec.rb @@ -76,7 +76,8 @@ describe 'Recent searches', :js do set_recent_searches(project_1_local_storage_key, '["foo", "bar"]') 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') expect(find('.filtered-search').value.strip).to eq('foo') @@ -86,10 +87,11 @@ describe 'Recent searches', :js do set_recent_searches(project_1_local_storage_key, '["foo"]') 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') - items_after = all('.filtered-search-history-dropdown-item', visible: false, count: 0) + find('.filtered-search-history-clear-button').click + items_after = all('.filtered-search-history-dropdown-item', count: 0) expect(items_after.count).to eq(0) end diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb index 920f5546eef..0ae70c855db 100644 --- a/spec/features/issues/filtered_search/visual_tokens_spec.rb +++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb @@ -2,7 +2,6 @@ require 'rails_helper' describe 'Visual tokens', :js do include FilteredSearchHelpers - include WaitForRequests let!(:project) { create(:project) } let!(:user) { create(:user, name: 'administrator', username: 'root') } @@ -28,7 +27,7 @@ describe 'Visual tokens', :js do sign_in(user) create(:issue, project: project) - page.driver.set_cookie('sidebar_collapsed', 'true') + set_cookie('sidebar_collapsed', 'true') visit project_issues_path(project) end diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 15041ff04ea..b8a66245153 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -17,9 +17,9 @@ feature 'GFM autocomplete', :js do it 'updates issue descripton with GFM reference' do 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' @@ -28,7 +28,6 @@ feature 'GFM autocomplete', :js do it 'opens autocomplete menu when field starts with text' do page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') find('#note-body').native.send_keys('@') end @@ -46,7 +45,6 @@ feature 'GFM autocomplete', :js do it 'doesnt select the first item for non-assignee dropdowns' do page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') find('#note-body').native.send_keys(':') end @@ -86,7 +84,6 @@ feature 'GFM autocomplete', :js do it 'selects the first item for assignee dropdowns' do page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') find('#note-body').native.send_keys('@') end @@ -100,7 +97,7 @@ feature 'GFM autocomplete', :js do it 'includes items for assignee dropdowns with non-ASCII characters in name' do page.within '.timeline-content-form' do 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 expect(page).to have_selector('.atwho-container') @@ -112,7 +109,6 @@ feature 'GFM autocomplete', :js do it 'selects the first item for non-assignee dropdowns if a query is entered' do page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') find('#note-body').native.send_keys(':1') end @@ -127,9 +123,8 @@ feature 'GFM autocomplete', :js do it 'wraps the result in double quotes' do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') - note.native.send_keys("~#{label.title[0]}") - note.click + find('#note-body').native.send_keys('') + simulate_input('#note-body', "~#{label.title[0]}") end label_item = find('.atwho-view li', text: label.title) @@ -152,16 +147,13 @@ feature 'GFM autocomplete', :js do it "does not show dropdown when preceded with a special character" do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') note.native.send_keys("@") - note.click end expect(page).to have_selector('.atwho-container') page.within '.timeline-content-form' do note.native.send_keys("@") - note.click end expect(page).to have_selector('.atwho-container', visible: false) @@ -170,9 +162,7 @@ feature 'GFM autocomplete', :js do it "does not throw an error if no labels exist" do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') note.native.send_keys('~') - note.click end expect(page).to have_selector('.atwho-container', visible: false) @@ -181,9 +171,7 @@ feature 'GFM autocomplete', :js do it 'doesn\'t wrap for assignee values' do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') note.native.send_keys("@#{user.username[0]}") - note.click end user_item = find('.atwho-view li', text: user.username) @@ -194,9 +182,7 @@ feature 'GFM autocomplete', :js do it 'doesn\'t wrap for emoji values' do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') - note.native.send_keys(":cartwheel") - note.click + note.native.send_keys(":cartwheel_") end emoji_item = find('.atwho-view li', text: 'cartwheel_tone1') @@ -223,12 +209,11 @@ feature 'GFM autocomplete', :js do it 'triggers autocomplete after selecting a quick action' do note = find('#note-body') page.within '.timeline-content-form' do - note.native.send_keys('') note.native.send_keys('/as') - note.click 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) expect(user_item).to have_content(user.username) diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index bc9c3d825c1..a9de52bd8d5 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -130,8 +130,8 @@ feature 'Issue Sidebar' do it 'adds new label' do page.within('.block.labels') do fill_in 'new_label_name', with: 'wontfix' - page.find('.suggest-colors a', match: :first).trigger('click') - page.find('button', text: 'Create').trigger('click') + page.find('.suggest-colors a', match: :first).click + page.find('button', text: 'Create').click page.within('.dropdown-page-one') do expect(page).to have_content 'wontfix' @@ -142,8 +142,8 @@ feature 'Issue Sidebar' do it 'shows error message if label title is taken' do page.within('.block.labels') do fill_in 'new_label_name', with: label.title - page.find('.suggest-colors a', match: :first).trigger('click') - page.find('button', text: 'Create').trigger('click') + page.find('.suggest-colors a', match: :first).click + page.find('button', text: 'Create').click page.within('.dropdown-page-two') do expect(page).to have_content 'Title has already been taken' @@ -170,7 +170,7 @@ feature 'Issue Sidebar' do end 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') end end diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 6d7b1b1cd8f..17035b5501c 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -38,7 +38,7 @@ feature 'issue move to another project' do end scenario 'moving issue to another project', :js do - find('.js-move-issue').trigger('click') + find('.js-move-issue').click wait_for_requests all('.js-move-issue-dropdown-item')[0].click find('.js-move-issue-confirmation-button').click @@ -52,7 +52,7 @@ feature 'issue move to another project' do scenario 'searching project dropdown', :js do new_project_search.team << [user, :reporter] - find('.js-move-issue').trigger('click') + find('.js-move-issue').click wait_for_requests page.within '.js-sidebar-move-issue-block' do @@ -69,7 +69,7 @@ feature 'issue move to another project' do background { another_project.team << [user, :guest] } scenario 'browsing projects in projects select' do - find('.js-move-issue').trigger('click') + find('.js-move-issue').click wait_for_requests page.within '.js-sidebar-move-issue-block' do diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 1f57c110c11..bcc6e9bab0f 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -118,7 +118,7 @@ feature 'Multiple issue updating from issues#index', :js do end def click_update_issues_button - find('.update-selected-issues').trigger('click') + find('.update-selected-issues').click wait_for_requests end end diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index 9f5e25ff2cb..c4c06ed514b 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -226,7 +226,7 @@ feature 'Issues > User uses quick actions', :js do end it 'applies the commands to both issues and moves the issue' do - write_note("/label ~#{bug.title} ~#{wontfix.title}\n/milestone %\"#{milestone.title}\"\n/move #{target_project.full_path}") + write_note("/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"\n\n/move #{target_project.full_path}") expect(page).to have_content 'Commands applied' expect(issue.reload).to be_closed @@ -245,7 +245,7 @@ feature 'Issues > User uses quick actions', :js do end it 'moves the issue and applies the commands to both issues' do - write_note("/move #{target_project.full_path}\n/label ~#{bug.title} ~#{wontfix.title}\n/milestone %\"#{milestone.title}\"") + write_note("/move #{target_project.full_path}\n\n/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"") expect(page).to have_content 'Commands applied' expect(issue.reload).to be_closed diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 9b94452fb0d..b9af77f918a 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -367,7 +367,7 @@ describe 'Issues' do it 'changes incoming email address token', :js do find('.issue-email-modal-btn').click previous_token = find('input#issue_email').value - find('.incoming-email-token-reset').trigger('click') + find('.incoming-email-token-reset').click wait_for_requests @@ -585,9 +585,11 @@ describe 'Issues' do end it "cancels a file upload correctly" do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) + slow_requests do + dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - click_button 'Cancel' + click_button 'Cancel' + end expect(page).to have_button('Attach a file') expect(page).not_to have_button('Cancel') diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb index ba976bc7216..4e2963c116d 100644 --- a/spec/features/merge_requests/conflicts_spec.rb +++ b/spec/features/merge_requests/conflicts_spec.rb @@ -23,11 +23,11 @@ feature 'Merge request conflict resolution', :js do within find('.files-wrapper .diff-file', text: 'files/ruby/regex.rb') do all('button', text: 'Use ours').each do |button| - button.trigger('click') + button.send_keys(:return) end end - click_button 'Commit conflict resolution' + find_button('Commit conflict resolution').send_keys(:return) expect(page).to have_content('All merge conflicts were resolved') merge_request.reload_diff @@ -71,7 +71,7 @@ feature 'Merge request conflict resolution', :js do execute_script('ace.edit($(".files-wrapper .diff-file pre")[1]).setValue("Gregor Samsa woke from troubled dreams");') end - click_button 'Commit conflict resolution' + find_button('Commit conflict resolution').send_keys(:return) expect(page).to have_content('All merge conflicts were resolved') merge_request.reload_diff diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb index 9aa0672feae..9e816cf041b 100644 --- a/spec/features/merge_requests/diff_notes_avatars_spec.rb +++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb @@ -22,7 +22,7 @@ feature 'Diff note avatars', :js do project.team << [user, :master] sign_in user - page.driver.set_cookie('sidebar_collapsed', 'true') + set_cookie('sidebar_collapsed', 'true') end context 'discussion tab' do @@ -56,7 +56,7 @@ feature 'Diff note avatars', :js do end it 'does not render avatar after commenting' do - first('.diff-line-num').trigger('mouseover') + first('.diff-line-num').click find('.js-add-diff-note-button').click page.within('.js-discussion-note-form') do @@ -85,7 +85,7 @@ feature 'Diff note avatars', :js do it 'shows note avatar' do page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').click + find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 1) end @@ -93,7 +93,7 @@ feature 'Diff note avatars', :js do it 'shows comment on note avatar' do page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').click + find('.diff-notes-collapse').send_keys(:return) expect(first('img.js-diff-comment-avatar')["data-original-title"]).to eq("#{note.author.name}: #{note.note.truncate(17)}") end @@ -101,7 +101,7 @@ feature 'Diff note avatars', :js do it 'toggles comments when clicking avatar' do page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').click + find('.diff-notes-collapse').send_keys(:return) end expect(page).to have_selector('.notes_holder', visible: false) @@ -117,7 +117,7 @@ feature 'Diff note avatars', :js do open_more_actions_dropdown(note) page.within find(".note-row-#{note.id}") do - find('.js-note-delete').click + accept_confirm { find('.js-note-delete').click } end wait_for_requests @@ -139,7 +139,7 @@ feature 'Diff note avatars', :js do end page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').trigger('click') + find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 2) end @@ -152,14 +152,14 @@ feature 'Diff note avatars', :js do page.within '.js-discussion-note-form' do find('.js-note-text').native.send_keys('Test') - find('.js-comment-button').trigger('click') + find('.js-comment-button').click wait_for_requests end end page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').trigger('click') + find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 3) expect(find('.diff-comments-more-count')).to have_content '+1' @@ -177,7 +177,7 @@ feature 'Diff note avatars', :js do it 'shows extra comment count' do page.within find_line(position.line_code(project.repository)) do - find('.diff-notes-collapse').click + find('.diff-notes-collapse').send_keys(:return) expect(find('.diff-comments-more-count')).to have_content '+1' end diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index 3db0729cafb..15d380b1bf4 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -192,7 +192,7 @@ feature 'Diff notes resolve', :js do page.find('.discussion-next-btn').click end - expect(page.evaluate_script("$('body').scrollTop()")).to be > 0 + expect(page.evaluate_script("window.pageYOffset")).to be > 0 end it 'hides jump to next button when all resolved' do @@ -241,10 +241,8 @@ feature 'Diff notes resolve', :js do end it 'resolves discussion' do - page.all('.note').each do |note| - note.all('.line-resolve-btn').each do |button| - button.click - end + page.all('.note .line-resolve-btn').each do |button| + button.click end expect(page).to have_content('Resolved by') @@ -305,10 +303,10 @@ feature 'Diff notes resolve', :js do end page.within '.line-resolve-all-container' do - page.find('.discussion-next-btn').trigger('click') + page.find('.discussion-next-btn').click end - expect(page.evaluate_script("$('body').scrollTop()")).to be > 0 + expect(page.evaluate_script("window.pageYOffset")).to be > 0 end it 'updates updated text after resolving note' do diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb index 2adca58620f..1bf77296ae6 100644 --- a/spec/features/merge_requests/diffs_spec.rb +++ b/spec/features/merge_requests/diffs_spec.rb @@ -7,14 +7,12 @@ feature 'Diffs URL', :js do let(:merge_request) { create(:merge_request, source_project: project) } context 'when visit with */* as accept header' do - before do - page.driver.add_header('Accept', '*/*') - end - it 'renders the notes' do create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master' - visit diffs_project_merge_request_path(project, merge_request) + inspect_requests(inject_headers: { 'Accept' => '*/*' }) do + visit diffs_project_merge_request_path(project, merge_request) + end # Load notes and diff through AJAX expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master') @@ -90,7 +88,7 @@ feature 'Diffs URL', :js do visit diffs_project_merge_request_path(project, merge_request) # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax - find("[id=\"#{changelog_id}\"] .js-edit-blob").trigger('click') + find("[id=\"#{changelog_id}\"] .js-edit-blob").click expect(page).to have_selector('.js-fork-suggestion-button', count: 1) expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1) diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb index 758fc9b139d..1dcc1e139a0 100644 --- a/spec/features/merge_requests/form_spec.rb +++ b/spec/features/merge_requests/form_spec.rb @@ -43,7 +43,7 @@ describe 'New/edit merge request', :js do expect(page).to have_content user2.name end - find('a', text: 'Assign to me').trigger('click') + find('a', text: 'Assign to me').click expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s) page.within '.js-assignee-search' do expect(page).to have_content user.name diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb index 0ae43e226b9..bac56270362 100644 --- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb +++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb @@ -83,7 +83,7 @@ feature 'Mini Pipeline Graph', :js do end before do - toggle.trigger('click') + toggle.click wait_for_requests end @@ -92,7 +92,7 @@ feature 'Mini Pipeline Graph', :js do end it 'should close when toggle is clicked again' do - toggle.trigger('click') + toggle.click expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu') end diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb index 1a41fd36a4f..c5498563b39 100644 --- a/spec/features/merge_requests/update_merge_requests_spec.rb +++ b/spec/features/merge_requests/update_merge_requests_spec.rb @@ -127,7 +127,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do end def click_update_merge_requests_button - find('.update-selected-issues').trigger('click') + find('.update-selected-issues').click wait_for_requests end end diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_requests/user_posts_diff_notes_spec.rb index 7a773fb2baa..d44eb23d7f4 100644 --- a/spec/features/merge_requests/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_requests/user_posts_diff_notes_spec.rb @@ -8,7 +8,7 @@ feature 'Merge requests > User posts diff notes', :js do let(:project) { merge_request.source_project } before do - page.driver.set_cookie('sidebar_collapsed', 'true') + set_cookie('sidebar_collapsed', 'true') project.add_developer(user) sign_in(user) @@ -103,7 +103,10 @@ feature 'Merge requests > User posts diff notes', :js do it 'allows commenting' do should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]')) - first('.js-note-delete', visible: false).trigger('click') + accept_confirm do + first('button.more-actions-toggle').click + first('.js-note-delete').click + end should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]')) end @@ -236,7 +239,7 @@ feature 'Merge requests > User posts diff notes', :js do def should_allow_dismissing_a_comment(line_holder, diff_side = nil) write_comment_on_line(line_holder, diff_side) - find('.js-close-discussion-note-form').trigger('click') + find('.js-close-discussion-note-form').click assert_comment_dismissal(line_holder) end diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb index d7cda73ab40..f4c75a2f265 100644 --- a/spec/features/merge_requests/user_posts_notes_spec.rb +++ b/spec/features/merge_requests/user_posts_notes_spec.rb @@ -141,7 +141,7 @@ describe 'Merge requests > User posts notes', :js do end it 'removes the attachment div and resets the edit form' do - find('.js-note-attachment-delete').click + accept_confirm { find('.js-note-attachment-delete').click } is_expected.not_to have_css('.note-attachment') is_expected.not_to have_css('.current-note-edit-form') wait_for_requests diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_requests/versions_spec.rb index 50f7d721ff3..29f95039af8 100644 --- a/spec/features/merge_requests/versions_spec.rb +++ b/spec/features/merge_requests/versions_spec.rb @@ -67,8 +67,8 @@ feature 'Merge Request versions', :js do line_code = '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44_2_2' page.within(diff_file_selector) do - find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").trigger 'mouseover' - find(".line_holder[id='#{line_code}'] button").trigger 'click' + find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").hover + find(".line_holder[id='#{line_code}'] button").click page.within("form[data-line-code='#{line_code}']") do fill_in "note[note]", with: "Typo, please fix" @@ -137,8 +137,8 @@ feature 'Merge Request versions', :js do line_code = '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44_4_4' page.within(diff_file_selector) do - find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").trigger 'mouseover' - find(".line_holder[id='#{line_code}'] button").trigger 'click' + find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").hover + find(".line_holder[id='#{line_code}'] button").click page.within("form[data-line-code='#{line_code}']") do fill_in "note[note]", with: "Typo, please fix" diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb index 5658c2c5122..72a52c979b3 100644 --- a/spec/features/merge_requests/widget_deployments_spec.rb +++ b/spec/features/merge_requests/widget_deployments_spec.rb @@ -42,7 +42,7 @@ feature 'Widget Deployments Header', :js do end scenario 'does start build when stop button clicked' do - click_button('Stop environment') + accept_confirm { click_button('Stop environment') } expect(page).to have_content('close_app') end diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 0166ab8be99..c60883911f7 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -65,7 +65,7 @@ describe 'Profile account page', :js do within('.rss-token-reset') do previous_token = find("#rss_token").value - click_link('reset it') + accept_confirm { click_link('reset it') } expect(find('#rss_token').value).not_to eq(previous_token) end @@ -84,7 +84,7 @@ describe 'Profile account page', :js do within('.incoming-email-token-reset') do previous_token = find('#incoming_email_token').value - click_link('reset it') + accept_confirm { click_link('reset it') } expect(find('#incoming_email_token').value).not_to eq(previous_token) end diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb index 8cb240077eb..d1edeef8da4 100644 --- a/spec/features/profiles/oauth_applications_spec.rb +++ b/spec/features/profiles/oauth_applications_spec.rb @@ -14,7 +14,7 @@ describe 'Profile > Applications' do page.within('.oauth-applications') do expect(page).to have_content('Your applications (1)') - click_button 'Destroy' + accept_confirm { click_button 'Destroy' } end expect(page).to have_content('The application was deleted successfully') @@ -28,7 +28,7 @@ describe 'Profile > Applications' do page.within('.oauth-authorized-applications') do expect(page).to have_content('Authorized applications (1)') - click_button 'Revoke' + accept_confirm { click_button 'Revoke' } end expect(page).to have_content('The application was revoked access.') diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index a572160dae9..8461cd0027c 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -34,7 +34,7 @@ describe 'Profile > Personal Access Tokens', :js do fill_in "Name", with: name # Set date to 1st of next month - find_field("Expires at").trigger('focus') + find_field("Expires at").click find(".pika-next").click click_on "1" @@ -78,7 +78,7 @@ describe 'Profile > Personal Access Tokens', :js do it "allows revocation of an active token" do visit profile_personal_access_tokens_path - click_on "Revoke" + accept_confirm { click_on "Revoke" } expect(page).to have_selector(".settings-message") expect(no_personal_access_tokens_message).to have_text("This user has no active Personal Access Tokens.") @@ -100,7 +100,7 @@ describe 'Profile > Personal Access Tokens', :js do errors = ActiveModel::Errors.new(PersonalAccessToken.new).tap { |e| e.add(:name, "cannot be nil") } allow_any_instance_of(PersonalAccessToken).to receive(:errors).and_return(errors) - click_on "Revoke" + accept_confirm { click_on "Revoke" } expect(active_personal_access_tokens).to have_text(personal_access_token.name) expect(page).to have_content("Could not revoke") end diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb index 923ca8b1c80..df89918f17a 100644 --- a/spec/features/profiles/user_visits_notifications_tab_spec.rb +++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb @@ -13,7 +13,7 @@ feature 'User visits the notifications tab', :js do it 'changes the project notifications setting' do expect(page).to have_content('Notifications') - first('#notifications-button').trigger('click') + first('#notifications-button').click click_link('On mention') expect(page).to have_content('On mention') diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb index 924ee0e4174..90d6841af0e 100644 --- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb +++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb @@ -53,7 +53,7 @@ describe 'User visits the profile preferences page' do expect(page).to have_content("You don't have starred projects yet") expect(page.current_path).to eq starred_dashboard_projects_path - find('.shortcuts-activity').trigger('click') + find('.shortcuts-activity').click expect(page).not_to have_content("You don't have starred projects yet") expect(page.current_path).to eq dashboard_projects_path diff --git a/spec/features/projects/artifacts/download_spec.rb b/spec/features/projects/artifacts/download_spec.rb index f1bdb2812c6..6f76c14910b 100644 --- a/spec/features/projects/artifacts/download_spec.rb +++ b/spec/features/projects/artifacts/download_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Download artifact', :js do +feature 'Download artifact' do let(:project) { create(:project, :public) } let(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) } let(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) } diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb index b2be10a7e0c..df1d17bdcb7 100644 --- a/spec/features/projects/artifacts/file_spec.rb +++ b/spec/features/projects/artifacts/file_spec.rb @@ -39,7 +39,6 @@ feature 'Artifact file', :js do context 'JPG file' do before do - page.driver.browser.url_blacklist = [] visit_file('rails_sample.jpg') wait_for_requests diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index 941d34dd660..7a77df83034 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -67,7 +67,7 @@ describe 'Branches' do expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) - find('.js-branch-fix .btn-remove').trigger(:click) + accept_confirm { find('.js-branch-fix .btn-remove').click } expect(page).not_to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 0) diff --git a/spec/features/projects/commit/diff_notes_spec.rb b/spec/features/projects/commit/diff_notes_spec.rb index f0fe4e00acc..4dbfc6f6edf 100644 --- a/spec/features/projects/commit/diff_notes_spec.rb +++ b/spec/features/projects/commit/diff_notes_spec.rb @@ -20,8 +20,8 @@ feature 'Commit diff', :js do it "adds comment to diff" do diff_line_num = first('.diff-line-num.new') - diff_line_num.trigger('mouseover') - diff_line_num.find('.js-add-diff-note-button').trigger('click') + diff_line_num.hover + diff_line_num.find('.js-add-diff-note-button').click page.within(first('.diff-viewer')) do find('.js-note-text').set 'test comment' diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 6f6d562c7b6..807a2189cc4 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -18,7 +18,7 @@ feature 'Mini Pipeline Graph in Commit View', :js do expect(page).to have_selector('.mr-widget-pipeline-graph') - first('.mini-pipeline-graph-dropdown-toggle').trigger('click') + first('.mini-pipeline-graph-dropdown-toggle').click wait_for_requests diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb index 2d1a9b931b5..e445758cb5e 100644 --- a/spec/features/projects/deploy_keys_spec.rb +++ b/spec/features/projects/deploy_keys_spec.rb @@ -20,7 +20,7 @@ describe 'Project deploy keys', :js do page.within(find('.deploy-keys')) do expect(page).to have_selector('.deploy-keys li', count: 1) - click_on 'Remove' + accept_confirm { find(:button, text: 'Remove').send_keys(:return) } expect(page).not_to have_selector('.fa-spinner', count: 0) expect(page).to have_selector('.deploy-keys li', count: 0) diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index 0fe1eb4c293..5fc3ba54f65 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -193,12 +193,14 @@ feature 'Environment' do create(:environment, project: project, name: 'staging-1.0/review', state: :available) - - visit folder_project_environments_path(project, id: 'staging-1.0') end it 'renders a correct environment folder' do - expect(page).to have_gitlab_http_status(:ok) + reqs = inspect_requests do + visit folder_project_environments_path(project, id: 'staging-1.0') + end + + expect(reqs.first.status_code).to eq(200) expect(page).to have_content('Environments / staging-1.0') end end diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 610f566c0cf..b4eb5795470 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -151,7 +151,7 @@ feature 'Environments page', :js do find('.js-dropdown-play-icon-container').click expect(page).to have_content(action.name.humanize) - expect { find('.js-manual-action-link').trigger('click') } + expect { find('.js-manual-action-link').click } .not_to change { Ci::Pipeline.count } end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index e5282b42a4f..951456763dc 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -22,7 +22,7 @@ describe 'Edit Project Settings' do # disable by clicking toggle toggle_feature_off("project[project_feature_attributes][#{tool_name}_access_level]") page.within('.sharing-permissions') do - click_button 'Save changes' + find('input[value="Save changes"]').click end wait_for_requests expect(page).not_to have_selector(".shortcuts-#{shortcut_name}") @@ -30,7 +30,7 @@ describe 'Edit Project Settings' do # re-enable by clicking toggle again toggle_feature_on("project[project_feature_attributes][#{tool_name}_access_level]") page.within('.sharing-permissions') do - click_button 'Save changes' + find('input[value="Save changes"]').click end wait_for_requests expect(page).to have_selector(".shortcuts-#{shortcut_name}") diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb index 25f7e18ac5c..3ab43b3c656 100644 --- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb +++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb @@ -7,18 +7,18 @@ feature 'User uses soft wrap whilst editing file', :js do project.team << [user, :master] sign_in user visit project_new_blob_path(project, 'master', file_name: 'test_file-name') - editor = find('.file-editor.code') - editor.click - editor.send_keys 'Touch water with paw then recoil in horror chase dog then - run away chase the pig around the house eat owner\'s food, and knock - dish off table head butt cant eat out of my own dish. Cat is love, cat - is life rub face on everything poop on grasses so meow. Playing with - balls of wool flee in terror at cucumber discovered on floor run in - circles tuxedo cats always looking dapper, but attack dog, run away - and pretend to be victim so all of a sudden cat goes crazy, yet chase - laser. Make muffins sit in window and stare ooo, a bird! yum lick yarn - hanging out of own butt jump off balcony, onto stranger\'s head yet - chase laser. Purr for no reason stare at ceiling hola te quiero.'.squish + page.within('.file-editor.code') do + find('.ace_text-input', visible: false).send_keys 'Touch water with paw then recoil in horror chase dog then + run away chase the pig around the house eat owner\'s food, and knock + dish off table head butt cant eat out of my own dish. Cat is love, cat + is life rub face on everything poop on grasses so meow. Playing with + balls of wool flee in terror at cucumber discovered on floor run in + circles tuxedo cats always looking dapper, but attack dog, run away + and pretend to be victim so all of a sudden cat goes crazy, yet chase + laser. Make muffins sit in window and stare ooo, a bird! yum lick yarn + hanging out of own butt jump off balcony, onto stranger\'s head yet + chase laser. Purr for no reason stare at ceiling hola te quiero.'.squish + end end let(:toggle_button) { find('.soft-wrap-toggle') } @@ -36,6 +36,6 @@ feature 'User uses soft wrap whilst editing file', :js do end def get_content_width - find('.ace_content')[:style].slice!(/width: \d+/).slice!(/\d+/) + find('.ace_content')[:style].slice!(/width: \d+/).slice!(/\d+/).to_i end end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 05776c50f9d..461aa39d0ad 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -41,7 +41,7 @@ feature 'Import/Export - project export integration test', :js do expect(page).to have_content('Export project') - click_link 'Export project' + find(:link, 'Export project').send_keys(:return) visit edit_project_path(project) diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 026aa03f7cf..af125e1b9d3 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -94,6 +94,6 @@ feature 'Import/Export - project import integration test', :js do end def click_import_project_tab - find('#import-project-tab').trigger('click') + find('#import-project-tab').click end end diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index b6a7c3cdcdb..e76bc6f1220 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -52,7 +52,7 @@ feature 'Import/Export - Namespace export file cleanup', :js do expect(page).to have_content('Export project') - click_link 'Export project' + find(:link, 'Export project').send_keys(:return) visit edit_project_path(project) diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 21c9acc7ac0..5d9208ebadd 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -21,12 +21,12 @@ describe 'User browses a job', :js do expect(page).to have_content("Job ##{build.id}") expect(page).to have_css('#build-trace') - click_link('Erase') + accept_confirm { click_link('Erase') } + expect(page).to have_no_css('.artifacts') expect(build).not_to have_trace expect(build.artifacts_file.exists?).to be_falsy expect(build.artifacts_metadata.exists?).to be_falsy - expect(page).to have_no_css('.artifacts') page.within('.erased') do expect(page).to have_content('Job has been erased') diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index b095c3e6f7b..c2a0d2395a9 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -380,7 +380,6 @@ feature 'Jobs' do end it 'loads the page and shows all needed controls' do - expect(page.status_code).to eq(200) expect(page).to have_content 'Retry' end end @@ -392,11 +391,10 @@ feature 'Jobs' do job.run! visit project_job_path(project, job) find('.js-cancel-job').click() - find('.js-retry-button').trigger('click') + find('.js-retry-button').click end it 'shows the right status and buttons', :js do - expect(page).to have_gitlab_http_status(200) page.within('aside.right-sidebar') do expect(page).to have_content 'Cancel' end @@ -443,28 +441,30 @@ feature 'Jobs' do context 'access source' do context 'job from project' do before do - Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } job.run! - visit project_job_path(project, job) - find('.js-raw-link-controller').click() end it 'sends the right headers' do - expect(page.status_code).to eq(200) - expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(job.trace.send(:current_path)) + requests = inspect_requests(inject_headers: { 'X-Sendfile-Type' => 'X-Sendfile' }) do + visit raw_project_job_path(project, job) + end + + expect(requests.first.status_code).to eq(200) + expect(requests.first.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(requests.first.response_headers['X-Sendfile']).to eq(job.trace.send(:current_path)) end end context 'job from other project' do before do - Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } job2.run! - visit raw_project_job_path(project, job2) end it 'sends the right headers' do - expect(page.status_code).to eq(404) + requests = inspect_requests(inject_headers: { 'X-Sendfile-Type' => 'X-Sendfile' }) do + visit raw_project_job_path(project, job2) + end + expect(requests.first.status_code).to eq(404) end end end @@ -473,8 +473,6 @@ feature 'Jobs' do let(:existing_file) { Tempfile.new('existing-trace-file').path } before do - Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } - job.run! end @@ -483,16 +481,14 @@ feature 'Jobs' do allow_any_instance_of(Gitlab::Ci::Trace) .to receive(:paths) .and_return([existing_file]) - - visit project_job_path(project, job) - - find('.js-raw-link-controller').click end it 'sends the right headers' do - expect(page.status_code).to eq(200) - expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(existing_file) + requests = inspect_requests(inject_headers: { 'X-Sendfile-Type' => 'X-Sendfile' }) do + visit raw_project_job_path(project, job) + end + expect(requests.first.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(requests.first.response_headers['X-Sendfile']).to eq(existing_file) end end diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb index b1053982eee..7f067aadec6 100644 --- a/spec/features/projects/members/groups_with_access_list_spec.rb +++ b/spec/features/projects/members/groups_with_access_list_spec.rb @@ -31,6 +31,7 @@ feature 'Projects > Members > Groups with access list', :js do tomorrow = Date.today + 3 fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F") + find('body').click wait_for_requests page.within(find('li.group_member')) do @@ -40,7 +41,7 @@ feature 'Projects > Members > Groups with access list', :js do scenario 'deletes group link' do page.within(first('.group_member')) do - find('.btn-remove').click + accept_confirm { find('.btn-remove').click } end wait_for_requests diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 5f7b4ee0e77..0f88f4cb1e8 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -20,7 +20,7 @@ feature 'Projects > Members > Master adds member with expiration date', :js do page.within '.users-project-form' do select2(new_member.id, from: '#user_ids', multiple: true) - fill_in 'expires_at', with: date.to_s(:medium) + fill_in 'expires_at', with: date.to_s(:medium) + "\n" click_on 'Add to project' end @@ -37,7 +37,7 @@ feature 'Projects > Members > Master adds member with expiration date', :js do visit project_project_members_path(project) page.within "#project_member_#{new_member.project_members.first.id}" do - find('.js-access-expiration-date').set date.to_s(:medium) + find('.js-access-expiration-date').set date.to_s(:medium) + "\n" wait_for_requests expect(page).to have_content('Expires in 3 days') end diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb index 63b5df5a8f5..3198798306c 100644 --- a/spec/features/projects/members/share_with_group_spec.rb +++ b/spec/features/projects/members/share_with_group_spec.rb @@ -41,7 +41,7 @@ feature 'Project > Members > Share with Group', :js do select2 group_to_share_with.id, from: '#link_group_id' page.find('body').click - find('.btn-create').trigger('click') + find('.btn-create').click page.within('.project-members-groups') do expect(page).to have_content(group_to_share_with.name) @@ -123,7 +123,7 @@ feature 'Project > Members > Share with Group', :js do fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d') page.find('body').click - find('.btn-create').trigger('click') + find('.btn-create').click end scenario 'the group link shows the expiration time with a warning class' do diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 0fbe1ddb2a5..4eb36156812 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -60,7 +60,7 @@ feature 'Projects > Members > User requests access', :js do expect(project.requesters.exists?(user_id: user)).to be_truthy - click_link 'Withdraw Access Request' + accept_confirm { click_link 'Withdraw Access Request' } expect(project.requesters.exists?(user_id: user)).to be_falsey expect(page).to have_content 'Your access request to the project has been withdrawn.' diff --git a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb index f34302f25f8..e3f90a78cb5 100644 --- a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb +++ b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb @@ -31,7 +31,7 @@ describe 'User comments on a diff', :js do page.within('.files > div:nth-child(3)') do expect(page).to have_content('Line is wrong') - find('.js-toggle-diff-comments').trigger('click') + find('.js-toggle-diff-comments').click expect(page).not_to have_content('Line is wrong') end @@ -64,7 +64,7 @@ describe 'User comments on a diff', :js do # Hide the comment. page.within('.files > div:nth-child(3)') do - find('.js-toggle-diff-comments').trigger('click') + find('.js-toggle-diff-comments').click expect(page).not_to have_content('Line is wrong') end @@ -77,7 +77,7 @@ describe 'User comments on a diff', :js do # Show the comment. page.within('.files > div:nth-child(3)') do - find('.js-toggle-diff-comments').trigger('click') + find('.js-toggle-diff-comments').click end # Now both the comments should be shown. @@ -90,6 +90,7 @@ describe 'User comments on a diff', :js do end # Check the same comments in the side-by-side view. + execute_script("window.scrollTo(0,0);") click_link('Side-by-side') wait_for_requests @@ -153,11 +154,11 @@ describe 'User comments on a diff', :js do find('.more-actions').click find('.more-actions .dropdown-menu li', match: :first) - find('.js-note-delete').click + accept_confirm { find('.js-note-delete').click } end page.within('.merge-request-tabs') do - find('.notes-tab').trigger('click') + find('.notes-tab').click end wait_for_requests diff --git a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb index f6e3997383f..3d19a2923b9 100644 --- a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb +++ b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe 'User edits a merge request', :js do + include Select2Helper + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:user) { create(:user) } @@ -15,8 +17,7 @@ describe 'User edits a merge request', :js do it 'changes the target branch' do expect(page).to have_content('Target branch') - first('.target_branch').click - select('merge-test', from: 'merge_request_target_branch', visible: false) + select2('merge-test', from: '#merge_request_target_branch') click_button('Save changes') expect(page).to have_content("Request to merge #{merge_request.source_branch} into merge-test") diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 8e11cb94350..6f097ad16c7 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -15,7 +15,7 @@ feature 'New project' do expect(page).to have_content('Project path') expect(page).to have_content('Project name') - find('#import-project-tab').trigger('click') + find('#import-project-tab').click expect(page).to have_link('GitHub') expect(page).to have_link('Bitbucket') @@ -137,7 +137,7 @@ feature 'New project' do context 'Import project options', :js do before do visit new_project_path - find('#import-project-tab').trigger('click') + find('#import-project-tab').click end context 'from git repository url' do diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 24b335a7068..fa2f7a1fd78 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -54,7 +54,7 @@ feature 'Pipeline Schedules', :js do end it 'deletes the pipeline' do - click_link 'Delete' + accept_confirm { click_link 'Delete' } expect(page).not_to have_css(".pipeline-schedule-table-row") end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 931811e0ee6..b8fa1a54c24 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -73,7 +73,7 @@ describe 'Pipeline', :js do end it 'should be possible to cancel the running build' do - find('#ci-badge-deploy .ci-action-icon-container').trigger('click') + find('#ci-badge-deploy .ci-action-icon-container').click expect(page).not_to have_content('Cancel running') end @@ -92,7 +92,7 @@ describe 'Pipeline', :js do end it 'should be possible to retry the success job' do - find('#ci-badge-build .ci-action-icon-container').trigger('click') + find('#ci-badge-build .ci-action-icon-container').click expect(page).not_to have_content('Retry job') end @@ -111,7 +111,7 @@ describe 'Pipeline', :js do end it 'should be possible to retry the failed build' do - find('#ci-badge-test .ci-action-icon-container').trigger('click') + find('#ci-badge-test .ci-action-icon-container').click expect(page).not_to have_content('Retry job') end @@ -130,7 +130,7 @@ describe 'Pipeline', :js do end it 'should be possible to play the manual job' do - find('#ci-badge-manual-build .ci-action-icon-container').trigger('click') + find('#ci-badge-manual-build .ci-action-icon-container').click expect(page).not_to have_content('Play job') end @@ -165,7 +165,7 @@ describe 'Pipeline', :js do context 'when retrying' do before do - find('.js-retry-button').trigger('click') + find('.js-retry-button').click end it { expect(page).not_to have_content('Retry') } @@ -231,7 +231,7 @@ describe 'Pipeline', :js do context 'when retrying' do before do - find('.js-retry-button').trigger('click') + find('.js-retry-button').click end it { expect(page).not_to have_content('Retry') } diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index ae888fd4343..fc689bbb486 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -103,7 +103,7 @@ describe 'Pipelines', :js do context 'when canceling' do before do - find('.js-pipelines-cancel-button').click + accept_confirm { find('.js-pipelines-cancel-button').click } wait_for_requests end @@ -232,7 +232,7 @@ describe 'Pipelines', :js do context 'when canceling' do before do - find('.js-pipelines-cancel-button').trigger('click') + accept_alert { find('.js-pipelines-cancel-button').click } end it 'indicates that pipeline was canceled' do @@ -345,14 +345,14 @@ describe 'Pipelines', :js do context 'when clicking a stage badge' do it 'should open a dropdown' do - find('.js-builds-dropdown-button').trigger('click') + find('.js-builds-dropdown-button').click expect(page).to have_link build.name end it 'should be possible to cancel pending build' do - find('.js-builds-dropdown-button').trigger('click') - find('a.js-ci-action-icon').trigger('click') + find('.js-builds-dropdown-button').click + find('a.js-ci-action-icon').click expect(page).to have_content('canceled') expect(build.reload).to be_canceled @@ -361,11 +361,16 @@ describe 'Pipelines', :js do context 'dropdown jobs list' do it 'should keep the dropdown open when the user ctr/cmd + clicks in the job name' do - find('.js-builds-dropdown-button').trigger('click') - - execute_script('var e = $.Event("keydown", { keyCode: 64 }); $("body").trigger(e);') - - find('.mini-pipeline-graph-dropdown-item').trigger('click') + find('.js-builds-dropdown-button').click + dropdown_item = find('.mini-pipeline-graph-dropdown-item').native + + %i(alt control).each do |meta_key| + page.driver.browser.action + .key_down(meta_key) + .click(dropdown_item) + .key_up(meta_key) + .perform + end expect(page).to have_selector('.js-ci-action-icon') end @@ -525,7 +530,6 @@ describe 'Pipelines', :js do let(:project) { create(:project, :public, :repository) } it { expect(page).to have_content 'Build with confidence' } - it { expect(page).to have_gitlab_http_status(:success) } end context 'when project is private' do diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb index 50c0bfd580d..33ccbc1a29f 100644 --- a/spec/features/projects/ref_switcher_spec.rb +++ b/spec/features/projects/ref_switcher_spec.rb @@ -6,7 +6,7 @@ feature 'Ref switcher', :js do before do project.team << [user, :master] - page.driver.set_cookie('new_repo', 'true') + set_cookie('new_repo', 'true') sign_in(user) visit project_tree_path(project, 'master') end diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb index 0a86292ae6c..ac78b1dfb1c 100644 --- a/spec/features/projects/services/user_activates_jira_spec.rb +++ b/spec/features/projects/services/user_activates_jira_spec.rb @@ -65,7 +65,7 @@ describe 'User activates Jira', :js do expect(find('.flash-container-page')).to have_content 'Test failed. message' expect(find('.flash-container-page')).to have_content 'Save anyway' - find('.flash-alert .flash-action').trigger('click') + find('.flash-alert .flash-action').click wait_for_requests expect(page).to have_content('JIRA activated.') diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb index 95d5e8b14b9..6f057137867 100644 --- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb @@ -76,7 +76,7 @@ feature 'Setup Mattermost slash commands', :js do select_element = find('#mattermost_team_id') selected_option = select_element.find('option[selected]') - expect(select_element['disabled']).to be(true) + expect(select_element['disabled']).to eq("true") expect(selected_option).to have_content(team_name.to_s) end @@ -104,7 +104,7 @@ feature 'Setup Mattermost slash commands', :js do select_element = find('#mattermost_team_id') - expect(select_element['disabled']).to be(false) + expect(select_element['disabled']).to be_falsey expect(select_element.all('option').count).to eq(3) end @@ -122,7 +122,7 @@ feature 'Setup Mattermost slash commands', :js do click_link 'Add to Mattermost' - expect(find('input[type="submit"]')['disabled']).not_to be(true) + expect(find('input[type="submit"]')['disabled']).not_to eq("true") end it 'disables the submit button if the required fields are not provided', :js do @@ -132,7 +132,7 @@ feature 'Setup Mattermost slash commands', :js do fill_in('mattermost_trigger', with: '') - expect(find('input[type="submit"]')['disabled']).to be(true) + expect(find('input[type="submit"]')['disabled']).to eq("true") end def stub_teams(count: 0) diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb new file mode 100644 index 00000000000..28954a4fb40 --- /dev/null +++ b/spec/features/projects/settings/forked_project_settings_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +feature 'Settings for a forked project', :js do + include ProjectForksHelper + let(:user) { create(:user) } + let(:original_project) { create(:project) } + let(:forked_project) { fork_project(original_project, user) } + + before do + original_project.add_master(user) + forked_project.add_master(user) + sign_in(user) + end + + shared_examples 'project settings for a forked projects' do + it 'allows deleting the link to the forked project' do + visit edit_project_path(forked_project) + + click_button 'Remove fork relationship' + + wait_for_requests + + fill_in('confirm_name_input', with: forked_project.name) + click_button('Confirm') + + expect(page).to have_content('The fork relationship has been removed.') + expect(forked_project.reload.forked?).to be_falsy + end + end + + it_behaves_like 'project settings for a forked projects' + + context 'when the original project is deleted' do + before do + original_project.destroy! + end + + it_behaves_like 'project settings for a forked projects' + end +end diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb index b1ec556bf16..ac76c30cc7c 100644 --- a/spec/features/projects/settings/merge_requests_settings_spec.rb +++ b/spec/features/projects/settings/merge_requests_settings_spec.rb @@ -21,7 +21,7 @@ feature 'Project settings > Merge Requests', :js do within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click - click_on('Save changes') + find('input[value="Save changes"]').send_keys(:return) end expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds') @@ -41,7 +41,7 @@ feature 'Project settings > Merge Requests', :js do within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click - click_on('Save changes') + find('input[value="Save changes"]').send_keys(:return) end expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') @@ -62,7 +62,7 @@ feature 'Project settings > Merge Requests', :js do within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click - click_on('Save changes') + find('input[value="Save changes"]').send_keys(:return) end expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb index de8fbb15b9c..ea8f997409d 100644 --- a/spec/features/projects/settings/pipelines_settings_spec.rb +++ b/spec/features/projects/settings/pipelines_settings_spec.rb @@ -22,7 +22,7 @@ feature "Pipelines settings" do context 'for master' do given(:role) { :master } - scenario 'be allowed to change', :js do + scenario 'be allowed to change' do fill_in('Test coverage parsing', with: 'coverage_regex') click_on 'Save changes' diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index a4fefb0d0e7..e2a5619c22b 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -34,7 +34,6 @@ feature 'Repository settings' do visit project_settings_repository_path(project) - expect(page.status_code).to eq(200) expect(page).to have_content('private_deploy_key') expect(page).to have_content('public_deploy_key') end @@ -86,7 +85,7 @@ feature 'Repository settings' do project.deploy_keys << private_deploy_key visit project_settings_repository_path(project) - find('li', text: private_deploy_key.title).click_button('Remove') + accept_confirm { find('li', text: private_deploy_key.title).click_button('Remove') } expect(page).not_to have_content(private_deploy_key.title) end diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb index 3e79dba3f19..e4215291f99 100644 --- a/spec/features/projects/snippets/create_snippet_spec.rb +++ b/spec/features/projects/snippets/create_snippet_spec.rb @@ -10,7 +10,7 @@ feature 'Create Snippet', :js do fill_in 'project_snippet_title', with: 'My Snippet Title' fill_in 'project_snippet_description', with: 'My Snippet **Description**' page.within('.file-editor') do - find('.ace_editor').native.send_keys('Hello World!') + find('.ace_text-input', visible: false).send_keys('Hello World!') end end @@ -59,7 +59,7 @@ feature 'Create Snippet', :js do fill_form dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') - click_button('Create snippet') + find("input[value='Create snippet']").send_keys(:return) wait_for_requests expect(page).to have_content('My Snippet Title') diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index 4c1fa5a666e..8ee7b9cf015 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' feature 'Multi-file editor new directory', :js do - include WaitForRequests - let(:user) { create(:user) } let(:project) { create(:project, :repository) } @@ -10,7 +8,7 @@ feature 'Multi-file editor new directory', :js do project.add_master(user) sign_in(user) - page.driver.set_cookie('new_repo', 'true') + set_cookie('new_repo', 'true') visit project_tree_path(project, :master) @@ -32,7 +30,6 @@ feature 'Multi-file editor new directory', :js do click_button('Commit 1 file') - expect(page).to have_content('Your changes have been committed') expect(page).to have_selector('td', text: 'commit message') click_link('foldername') diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index ed3b52a5790..1e2de0711b8 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' feature 'Multi-file editor new file', :js do - include WaitForRequests - let(:user) { create(:user) } let(:project) { create(:project, :repository) } @@ -10,7 +8,7 @@ feature 'Multi-file editor new file', :js do project.add_master(user) sign_in(user) - page.driver.set_cookie('new_repo', 'true') + set_cookie('new_repo', 'true') visit project_tree_path(project, :master) @@ -32,7 +30,6 @@ feature 'Multi-file editor new file', :js do click_button('Commit 1 file') - expect(page).to have_content('Your changes have been committed') expect(page).to have_selector('td', text: 'commit message') end end diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb index 7dbe4fd0aa5..8439bb5a69e 100644 --- a/spec/features/projects/tree/upload_file_spec.rb +++ b/spec/features/projects/tree/upload_file_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' feature 'Multi-file editor upload file', :js do - include WaitForRequests - let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:txt_file) { File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') } @@ -12,7 +10,7 @@ feature 'Multi-file editor upload file', :js do project.add_master(user) sign_in(user) - page.driver.set_cookie('new_repo', 'true') + set_cookie('new_repo', 'true') visit project_tree_path(project, :master) @@ -29,7 +27,7 @@ feature 'Multi-file editor upload file', :js do find('.add-to-tree').click expect(page).to have_selector('.repo-tab', text: 'doc_sample.txt') - expect(page).to have_content(File.open(txt_file, &:readline)) + expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline)) end it 'uploads image file' do diff --git a/spec/features/projects/user_browses_files_spec.rb b/spec/features/projects/user_browses_files_spec.rb index f43b11c9485..f5e4d7f5130 100644 --- a/spec/features/projects/user_browses_files_spec.rb +++ b/spec/features/projects/user_browses_files_spec.rb @@ -175,10 +175,11 @@ describe 'User browses files' do page.within('#modal-upload-blob') do fill_in(:commit_message, with: 'New commit message') + fill_in(:branch_name, with: 'new_branch_name', visible: true) + click_button('Upload file') end - fill_in(:branch_name, with: 'new_branch_name', visible: true) - click_button('Upload file') + wait_for_all_requests visit(project_blob_path(project, 'new_branch_name/logo_sample.svg')) diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index d63cbe578d8..337baaf4dcd 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -18,13 +18,13 @@ feature 'Projects > Wiki > User previews markdown changes', :js do sign_in(user) visit project_path(project) - find('.shortcuts-wiki').trigger('click') + find('.shortcuts-wiki').click end context "while creating a new wiki page" do context "when there are no spaces or hyphens in the page name" do it "rewrites relative links as expected" do - find('.add-new-wiki').trigger('click') + find('.add-new-wiki').click page.within '#modal-new-wiki' do fill_in :new_wiki_path, with: 'a/b/c/d' click_button 'Create page' @@ -91,7 +91,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do context "while editing a wiki page" do def create_wiki_page(path) - find('.add-new-wiki').trigger('click') + find('.add-new-wiki').click page.within '#modal-new-wiki' do fill_in :new_wiki_path, with: path diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index e72b7dc0dd5..4a9d1cb87e1 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -77,14 +77,14 @@ describe 'User creates wiki page' do [stem] ++++ - \sqrt{4} = 2 + \\sqrt{4} = 2 ++++ another part [latexmath] ++++ - \beta_x \gamma + \\beta_x \\gamma ++++ stem:[2+2] is 4 diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index 89f6901eb01..ff325aeadd3 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -138,7 +138,7 @@ describe 'User views a wiki page' do it 'opens a default wiki page', :js do visit(project_path(project)) - find('.shortcuts-wiki').trigger('click') + find('.shortcuts-wiki').click expect(page).to have_content('Home · Create Page') end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 3b01ed442bf..63e6051b571 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -13,8 +13,8 @@ feature 'Project' do end it "allows creation from templates", :js do - find('#create-from-template-tab').trigger('click') - find("##{template.name}").trigger('click') + find('#create-from-template-tab').click + find("label[for=#{template.name}]").click fill_in("project_path", with: template.name) page.within '#content-body' do diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 2ab1eda90f1..a4084818284 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -48,7 +48,7 @@ feature 'Protected Branches', :js do expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) - page.find('[data-target="#modal-delete-branch"]').trigger(:click) + page.find('[data-target="#modal-delete-branch"]').click expect(page).to have_css('.js-delete-branch[disabled]') fill_in 'delete_branch_input', with: 'fix' @@ -67,9 +67,9 @@ feature 'Protected Branches', :js do form = '.js-new-protected-branch' within form do - find(".js-allowed-to-merge").trigger('click') + find(".js-allowed-to-merge").click click_link 'No one' - find(".js-allowed-to-push").trigger('click') + find(".js-allowed-to-push").click click_link 'Developers + Masters' end @@ -171,7 +171,7 @@ feature 'Protected Branches', :js do end def set_protected_branch_name(branch_name) - find(".js-protected-branch-select").trigger('click') + find(".js-protected-branch-select").click find(".dropdown-input-field").set(branch_name) click_on("Create wildcard #{branch_name}") end diff --git a/spec/features/raven_js_spec.rb b/spec/features/raven_js_spec.rb index b1f51959d54..74890c86047 100644 --- a/spec/features/raven_js_spec.rb +++ b/spec/features/raven_js_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'RavenJS', :js do +feature 'RavenJS' do let(:raven_path) { '/raven.bundle.js' } it 'should not load raven if sentry is disabled' do @@ -18,6 +18,8 @@ feature 'RavenJS', :js do end def has_requested_raven - page.driver.network_traffic.one? {|request| request.url.end_with?(raven_path)} + page.all('script', visible: false).one? do |elm| + elm[:src] =~ /#{raven_path}$/ + end end end diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb index 0ed797a62ea..77212fb105b 100644 --- a/spec/features/search/user_searches_for_code_spec.rb +++ b/spec/features/search/user_searches_for_code_spec.rb @@ -32,14 +32,14 @@ describe 'User searches for code' do include_examples 'top right search form' it 'finds code' do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click page.within('.project-filter') do click_link(project.name_with_namespace) end fill_in('dashboard_search', with: 'rspec') - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.results') do expect(find(:css, '.search-results')).to have_content('Update capybara, rspec-rails, poltergeist to recent versions') diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb index 630a81b1c5e..ef9553f2a91 100644 --- a/spec/features/search/user_searches_for_issues_spec.rb +++ b/spec/features/search/user_searches_for_issues_spec.rb @@ -18,7 +18,7 @@ describe 'User searches for issues', :js do it 'finds an issue' do fill_in('dashboard_search', with: issue1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Issues') @@ -31,14 +31,14 @@ describe 'User searches for issues', :js do context 'when on a project page' do it 'finds an issue' do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click page.within('.project-filter') do click_link(project.name_with_namespace) end fill_in('dashboard_search', with: issue1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Issues') @@ -62,7 +62,7 @@ describe 'User searches for issues', :js do it 'finds an issue' do fill_in('dashboard_search', with: issue1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Issues') diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb index 116256682f4..3b6739aecbd 100644 --- a/spec/features/search/user_searches_for_merge_requests_spec.rb +++ b/spec/features/search/user_searches_for_merge_requests_spec.rb @@ -17,7 +17,7 @@ describe 'User searches for merge requests', :js do it 'finds a merge request' do fill_in('dashboard_search', with: merge_request1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Merge requests') @@ -30,14 +30,14 @@ describe 'User searches for merge requests', :js do context 'when on a project page' do it 'finds a merge request' do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click page.within('.project-filter') do click_link(project.name_with_namespace) end fill_in('dashboard_search', with: merge_request1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Merge requests') diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb index 4fa9fe9ce8c..6e197aee498 100644 --- a/spec/features/search/user_searches_for_milestones_spec.rb +++ b/spec/features/search/user_searches_for_milestones_spec.rb @@ -17,7 +17,7 @@ describe 'User searches for milestones', :js do it 'finds a milestone' do fill_in('dashboard_search', with: milestone1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Milestones') @@ -30,14 +30,14 @@ describe 'User searches for milestones', :js do context 'when on a project page' do it 'finds a milestone' do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click page.within('.project-filter') do click_link(project.name_with_namespace) end fill_in('dashboard_search', with: milestone1.title) - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Milestones') diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb index 1ea56479ecc..00af625dc86 100644 --- a/spec/features/search/user_searches_for_wiki_pages_spec.rb +++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb @@ -15,14 +15,14 @@ describe 'User searches for wiki pages', :js do include_examples 'top right search form' it 'finds a page' do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click page.within('.project-filter') do click_link(project.name_with_namespace) end fill_in('dashboard_search', with: 'content') - find('.btn-search').trigger('click') + find('.btn-search').click page.within('.search-filter') do click_link('Wiki') diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb index 95f3eb5e805..aa883c964d2 100644 --- a/spec/features/search/user_uses_search_filters_spec.rb +++ b/spec/features/search/user_uses_search_filters_spec.rb @@ -16,7 +16,7 @@ describe 'User uses search filters', :js do context' when filtering by group' do it 'shows group projects' do - find('.js-search-group-dropdown').trigger('click') + find('.js-search-group-dropdown').click wait_for_requests @@ -27,7 +27,7 @@ describe 'User uses search filters', :js do expect(find('.js-search-group-dropdown')).to have_content(group.name) page.within('.project-filter') do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click wait_for_requests @@ -39,7 +39,7 @@ describe 'User uses search filters', :js do context' when filtering by project' do it 'shows a project' do page.within('.project-filter') do - find('.js-search-project-dropdown').trigger('click') + find('.js-search-project-dropdown').click wait_for_requests diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index bf79974b8c6..269351e55c9 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -74,24 +74,21 @@ describe 'Comments on personal snippets', :js do it 'should not have autocomplete' do wait_for_requests - request_count_before = page.driver.network_traffic.count find('#note_note').native.send_keys('') fill_in 'note[note]', with: '@' wait_for_requests - request_count_after = page.driver.network_traffic.count # This selector probably won't be in place even if autocomplete was enabled # but we want to make sure expect(page).not_to have_selector('.atwho-view') - expect(request_count_before).to eq(request_count_after) end end context 'when editing a note' do it 'changes the text' do - find('.js-note-edit').trigger('click') + find('.js-note-edit').click page.within('.current-note-edit-form') do fill_in 'note[note]', with: 'new content' @@ -113,7 +110,7 @@ describe 'Comments on personal snippets', :js do open_more_actions_dropdown(snippet_notes[0]) page.within("#notes-list li#note_#{snippet_notes[0].id}") do - click_on 'Delete comment' + accept_confirm { click_on 'Delete comment' } end wait_for_requests diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb index d732383a1e1..941765b7578 100644 --- a/spec/features/snippets/user_creates_snippet_spec.rb +++ b/spec/features/snippets/user_creates_snippet_spec.rb @@ -14,7 +14,7 @@ feature 'User creates snippet', :js do fill_in 'personal_snippet_title', with: 'My Snippet Title' fill_in 'personal_snippet_description', with: 'My Snippet **Description**' page.within('.file-editor') do - find('.ace_editor').native.send_keys 'Hello World!' + find('.ace_text-input', visible: false).send_keys 'Hello World!' end end @@ -43,8 +43,8 @@ feature 'User creates snippet', :js do link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] expect(link).to match(%r{/uploads/-/system/temp/\h{32}/banana_sample\.gif\z}) - visit(link) - expect(page.status_code).to eq(200) + reqs = inspect_requests { visit(link) } + expect(reqs.first.status_code).to eq(200) end end @@ -61,8 +61,8 @@ feature 'User creates snippet', :js do link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z}) - visit(link) - expect(page.status_code).to eq(200) + reqs = inspect_requests { visit(link) } + expect(reqs.first.status_code).to eq(200) end scenario 'validation fails for the first time' do @@ -86,15 +86,15 @@ feature 'User creates snippet', :js do link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z}) - visit(link) - expect(page.status_code).to eq(200) + reqs = inspect_requests { visit(link) } + expect(reqs.first.status_code).to eq(200) end scenario 'Authenticated user creates a snippet with + in filename' do fill_in 'personal_snippet_title', with: 'My Snippet Title' page.within('.file-editor') do find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name' - find('.ace_editor').native.send_keys 'Hello World!' + find('.ace_text-input', visible: false).send_keys 'Hello World!' end click_button 'Create snippet' diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb index 1455345bd56..1f8bd8d681e 100644 --- a/spec/features/tags/master_creates_tag_spec.rb +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -63,7 +63,7 @@ feature 'Master creates tag' do expect(ref_input.value).to eq 'master' expect(find('.dropdown-toggle-text')).to have_content 'master' - find('.js-branch-select').trigger('click') + find('.js-branch-select').click expect(find('.dropdown-menu')).to have_content 'empty-branch' end diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb index f5b3774122b..dfda664d673 100644 --- a/spec/features/tags/master_deletes_tag_spec.rb +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -64,7 +64,7 @@ feature 'Master deletes tag' do def delete_first_tag page.within('.content') do - first('.btn-remove').click + accept_confirm { first('.btn-remove').click } end end end diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 548d8372a07..bc472e74997 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -45,7 +45,7 @@ feature 'Triggers', :js do visit project_settings_ci_cd_path(@project) # See if edit page has correct descrption - find('a[title="Edit"]').click + find('a[title="Edit"]').send_keys(:return) expect(page.find('#trigger_description').value).to have_content 'trigger desc' end @@ -54,7 +54,7 @@ feature 'Triggers', :js do visit project_settings_ci_cd_path(@project) # See if edit page opens, then fill in new description and save - find('a[title="Edit"]').click + find('a[title="Edit"]').send_keys(:return) fill_in 'trigger_description', with: new_trigger_title click_button 'Save trigger' @@ -70,7 +70,7 @@ feature 'Triggers', :js do visit project_settings_ci_cd_path(@project) # See if the trigger can be edited and description is blank - find('a[title="Edit"]').click + find('a[title="Edit"]').send_keys(:return) expect(page.find('#trigger_description').value).to have_content '' # See if trigger can be updated with description and saved successfully @@ -94,12 +94,13 @@ feature 'Triggers', :js do scenario 'take trigger ownership' do # See if "Take ownership" on trigger works post trigger creation - find('a.btn-trigger-take-ownership').click page.accept_confirm do - expect(page.find('.flash-notice')).to have_content 'Trigger was re-assigned.' - expect(page.find('.triggers-list')).to have_content trigger_title - expect(page.find('.triggers-list .trigger-owner')).to have_content user.name + first(:link, "Take ownership").send_keys(:return) end + + expect(page.find('.flash-notice')).to have_content 'Trigger was re-assigned.' + expect(page.find('.triggers-list')).to have_content trigger_title + expect(page.find('.triggers-list .trigger-owner')).to have_content user.name end end @@ -116,11 +117,12 @@ feature 'Triggers', :js do scenario 'revoke trigger' do # See if "Revoke" on trigger works post trigger creation - find('a.btn-trigger-revoke').click page.accept_confirm do - expect(page.find('.flash-notice')).to have_content 'Trigger removed' - expect(page).to have_selector('p.settings-message.text-center.append-bottom-default') + find('a.btn-trigger-revoke').send_keys(:return) end + + expect(page.find('.flash-notice')).to have_content 'Trigger removed' + expect(page).to have_selector('p.settings-message.text-center.append-bottom-default') end end diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb index f3662cb184f..c9afef2a8de 100644 --- a/spec/features/u2f_spec.rb +++ b/spec/features/u2f_spec.rb @@ -79,7 +79,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do first_u2f_device = register_u2f_device second_u2f_device = register_u2f_device(name: 'My other device') - click_on "Delete", match: :first + accept_confirm { click_on "Delete", match: :first } expect(page).to have_content('Successfully deleted') expect(page.body).not_to match(first_u2f_device.name) @@ -162,7 +162,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do @u2f_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_css('.sign-out-link', visible: false) end end @@ -174,23 +173,10 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do @u2f_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_css('.sign-out-link', visible: false) end end - it 'persists remember_me value via hidden field' do - gitlab_sign_in(user, remember: true) - - @u2f_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') - - within 'div#js-authenticate-u2f' do - field = first('input#user_remember_me', visible: false) - expect(field.value).to eq '1' - end - end - describe "when a given U2F device has already been registered by another user" do describe "but not the current user" do it "does not allow logging in with that particular device" do @@ -205,7 +191,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do # Try authenticating user with the old U2F device gitlab_sign_in(current_user) @u2f_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_content('Authentication via U2F device failed') end end @@ -223,7 +208,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do # Try authenticating user with the same U2F device gitlab_sign_in(current_user) @u2f_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_css('.sign-out-link', visible: false) end @@ -235,7 +219,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do unregistered_device = FakeU2fDevice.new(page, 'My device') gitlab_sign_in(user) unregistered_device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_content('Authentication via U2F device failed') end @@ -260,7 +243,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do [first_device, second_device].each do |device| gitlab_sign_in(user) device.respond_to_u2f_authentication - expect(page).to have_content('We heard back from your U2F device') expect(page).to have_css('.sign-out-link', visible: false) @@ -283,7 +265,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do it "deletes u2f registrations" do visit profile_account_path - expect { click_on "Disable" }.to change { U2fRegistration.count }.by(-1) + expect do + accept_confirm { click_on "Disable" } + end.to change { U2fRegistration.count }.by(-1) end end end diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb index 1261ffdc2ee..972c10aaf23 100644 --- a/spec/features/uploads/user_uploads_file_to_note_spec.rb +++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb @@ -21,16 +21,12 @@ feature 'User uploads file to note' do end context 'uploading is in progress' do - it 'shows "Cancel" button on uploading', :js do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - - expect(page).to have_button('Cancel') - end - it 'cancels uploading on clicking to "Cancel" button', :js do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) + slow_requests do + dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - click_button 'Cancel' + click_button 'Cancel' + end expect(page).to have_button('Attach a file') expect(page).not_to have_button('Cancel') @@ -38,16 +34,20 @@ feature 'User uploads file to note' do end it 'shows "Attaching a file" message on uploading 1 file', :js do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) + slow_requests do + dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -') + expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -') + end end it 'shows "Attaching 2 files" message on uploading 2 file', :js do - dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4'), - Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) + slow_requests do + dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4'), + Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching 2 files -') + expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching 2 files -') + end end it 'shows error message, "retry" and "attach a new file" link a if file is too big', :js do diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 0252c957c95..a9973cdf214 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -24,6 +24,7 @@ feature 'Users', :js do user.reload expect(user.reset_password_token).not_to be_nil + find('a[href="#login-pane"]').click gitlab_sign_in(user) expect(current_path).to eq root_path diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index 5d8e818f7bf..c78f7d0d9be 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -82,7 +82,7 @@ describe 'Project variables', :js do it 'deletes variable' do page.within('.variables-table') do - click_on 'Remove' + accept_confirm { click_on 'Remove' } end expect(page).not_to have_selector('variables-table') diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 4a59e0207f8..96efdd0949b 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -147,9 +147,12 @@ deploy_keys: - user - deploy_keys_projects - projects -clusters: +cluster: +- cluster_projects - projects - user +- provider_gcp +- platform_kubernetes cluster_projects: - projects - clusters @@ -188,6 +191,7 @@ project: - tags - chat_services - cluster +- cluster_project - creator - group - namespace diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb index de9a42b46d8..9f41534441b 100644 --- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb +++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb @@ -1,88 +1,166 @@ require 'spec_helper' -require Rails.root.join('db', 'migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb') +require Rails.root.join('db', 'post_migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb') describe MigrateGcpClustersToNewClustersArchitectures, :migration do let(:project) { create(:project) } let(:user) { create(:user) } let(:service) { create(:kubernetes_service, project: project) } - let(:project_id) { project.id } - let(:user_id) { user.id } - let(:service_id) { service.id } - let(:status) { 3 } - let(:gcp_cluster_size) { 1 } - let(:created_at) { '2017-10-17 20:24:02.219679' } - let(:updated_at) { '2017-10-17 20:28:44.738998' } - let(:enabled) { true } - let(:status_reason) { 'general error' } - let(:project_namespace) { 'sample-app' } - let(:endpoint) { '111.111.111.111' } - let(:ca_cert) { 'ca_cert' } - let(:encrypted_kubernetes_token) { 'encrypted_kubernetes_token' } - let(:encrypted_kubernetes_token_iv) { 'encrypted_kubernetes_token_iv' } - let(:username) { 'username' } - let(:encrypted_password) { 'encrypted_password' } - let(:encrypted_password_iv) { 'encrypted_password_iv' } - let(:gcp_project_id) { 'gcp_project_id' } - let(:gcp_cluster_zone) { 'gcp_cluster_zone' } - let(:gcp_cluster_name) { 'gcp_cluster_name' } - let(:gcp_machine_type) { 'gcp_machine_type' } - let(:gcp_operation_id) { 'gcp_operation_id' } - let(:encrypted_gcp_token) { 'encrypted_gcp_token' } - let(:encrypted_gcp_token_iv) { 'encrypted_gcp_token_iv' } - - let(:cluster) { Clusters::Cluster.last } - let(:cluster_id) { cluster.id } - - before do - ActiveRecord::Base.connection.execute <<-SQL - INSERT INTO gcp_clusters (project_id, user_id, service_id, status, gcp_cluster_size, created_at, updated_at, enabled, status_reason, project_namespace, endpoint, ca_cert, encrypted_kubernetes_token, encrypted_kubernetes_token_iv, username, encrypted_password, encrypted_password_iv, gcp_project_id, gcp_cluster_zone, gcp_cluster_name, gcp_machine_type, gcp_operation_id, encrypted_gcp_token, encrypted_gcp_token_iv) - VALUES ('#{project_id}', '#{user_id}', '#{service_id}', '#{status}', '#{gcp_cluster_size}', '#{created_at}', '#{updated_at}', '#{enabled}', '#{status_reason}', '#{project_namespace}', '#{endpoint}', '#{ca_cert}', '#{encrypted_kubernetes_token}', '#{encrypted_kubernetes_token_iv}', '#{username}', '#{encrypted_password}', '#{encrypted_password_iv}', '#{gcp_project_id}', '#{gcp_cluster_zone}', '#{gcp_cluster_name}', '#{gcp_machine_type}', '#{gcp_operation_id}', '#{encrypted_gcp_token}', '#{encrypted_gcp_token_iv}'); - SQL + context 'when cluster is being created' do + let(:project_id) { project.id } + let(:user_id) { user.id } + let(:service_id) { service.id } + let(:status) { 2 } # creating + let(:gcp_cluster_size) { 1 } + let(:created_at) { "'2017-10-17 20:24:02'" } + let(:updated_at) { "'2017-10-17 20:28:44'" } + let(:enabled) { true } + let(:status_reason) { "''" } + let(:project_namespace) { "'sample-app'" } + let(:endpoint) { 'NULL' } + let(:ca_cert) { 'NULL' } + let(:encrypted_kubernetes_token) { 'NULL' } + let(:encrypted_kubernetes_token_iv) { 'NULL' } + let(:username) { 'NULL' } + let(:encrypted_password) { 'NULL' } + let(:encrypted_password_iv) { 'NULL' } + let(:gcp_project_id) { "'gcp_project_id'" } + let(:gcp_cluster_zone) { "'gcp_cluster_zone'" } + let(:gcp_cluster_name) { "'gcp_cluster_name'" } + let(:gcp_machine_type) { "'gcp_machine_type'" } + let(:gcp_operation_id) { 'NULL' } + let(:encrypted_gcp_token) { "'encrypted_gcp_token'" } + let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" } + + let(:cluster) { Clusters::Cluster.last } + let(:cluster_id) { cluster.id } + + before do + ActiveRecord::Base.connection.execute <<-SQL + INSERT INTO gcp_clusters (project_id, user_id, service_id, status, gcp_cluster_size, created_at, updated_at, enabled, status_reason, project_namespace, endpoint, ca_cert, encrypted_kubernetes_token, encrypted_kubernetes_token_iv, username, encrypted_password, encrypted_password_iv, gcp_project_id, gcp_cluster_zone, gcp_cluster_name, gcp_machine_type, gcp_operation_id, encrypted_gcp_token, encrypted_gcp_token_iv) + VALUES (#{project_id}, #{user_id}, #{service_id}, #{status}, #{gcp_cluster_size}, #{created_at}, #{updated_at}, #{enabled}, #{status_reason}, #{project_namespace}, #{endpoint}, #{ca_cert}, #{encrypted_kubernetes_token}, #{encrypted_kubernetes_token_iv}, #{username}, #{encrypted_password}, #{encrypted_password_iv}, #{gcp_project_id}, #{gcp_cluster_zone}, #{gcp_cluster_name}, #{gcp_machine_type}, #{gcp_operation_id}, #{encrypted_gcp_token}, #{encrypted_gcp_token_iv}); + SQL + end + + it 'correctly migrate to new clusters architectures' do + migrate! + + expect(Clusters::Cluster.count).to eq(1) + expect(Clusters::Project.count).to eq(1) + expect(Clusters::Providers::Gcp.count).to eq(1) + expect(Clusters::Platforms::Kubernetes.count).to eq(1) + + expect(cluster.user).to eq(user) + expect(cluster.enabled).to be_truthy + expect(cluster.name).to eq(gcp_cluster_name.delete!("'")) + expect(cluster.provider_type).to eq('gcp') + expect(cluster.platform_type).to eq('kubernetes') + + expect(cluster.project).to eq(project) + expect(project.cluster).to eq(cluster) + + expect(cluster.provider_gcp.cluster).to eq(cluster) + expect(cluster.provider_gcp.status).to eq(status) + expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason)) + expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id)) + expect(cluster.provider_gcp.zone).to eq(tr(gcp_cluster_zone)) + expect(cluster.provider_gcp.num_nodes).to eq(gcp_cluster_size) + expect(cluster.provider_gcp.machine_type).to eq(tr(gcp_machine_type)) + expect(cluster.provider_gcp.operation_id).to be_nil + expect(cluster.provider_gcp.endpoint).to be_nil + expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token)) + expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv)) + + expect(cluster.platform_kubernetes.cluster).to eq(cluster) + expect(cluster.platform_kubernetes.api_url).to be_nil + expect(cluster.platform_kubernetes.ca_cert).to be_nil + expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace)) + expect(cluster.platform_kubernetes.username).to be_nil + expect(cluster.platform_kubernetes.encrypted_password).to be_nil + expect(cluster.platform_kubernetes.encrypted_password_iv).to be_nil + expect(cluster.platform_kubernetes.encrypted_token).to be_nil + expect(cluster.platform_kubernetes.encrypted_token_iv).to be_nil + end + end + + context 'when cluster has been created' do + let(:project_id) { project.id } + let(:user_id) { user.id } + let(:service_id) { service.id } + let(:status) { 3 } # created + let(:gcp_cluster_size) { 1 } + let(:created_at) { "'2017-10-17 20:24:02'" } + let(:updated_at) { "'2017-10-17 20:28:44'" } + let(:enabled) { true } + let(:status_reason) { "'general error'" } + let(:project_namespace) { "'sample-app'" } + let(:endpoint) { "'111.111.111.111'" } + let(:ca_cert) { "'ca_cert'" } + let(:encrypted_kubernetes_token) { "'encrypted_kubernetes_token'" } + let(:encrypted_kubernetes_token_iv) { "'encrypted_kubernetes_token_iv'" } + let(:username) { "'username'" } + let(:encrypted_password) { "'encrypted_password'" } + let(:encrypted_password_iv) { "'encrypted_password_iv'" } + let(:gcp_project_id) { "'gcp_project_id'" } + let(:gcp_cluster_zone) { "'gcp_cluster_zone'" } + let(:gcp_cluster_name) { "'gcp_cluster_name'" } + let(:gcp_machine_type) { "'gcp_machine_type'" } + let(:gcp_operation_id) { "'gcp_operation_id'" } + let(:encrypted_gcp_token) { "'encrypted_gcp_token'" } + let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" } + + let(:cluster) { Clusters::Cluster.last } + let(:cluster_id) { cluster.id } + + before do + ActiveRecord::Base.connection.execute <<-SQL + INSERT INTO gcp_clusters (project_id, user_id, service_id, status, gcp_cluster_size, created_at, updated_at, enabled, status_reason, project_namespace, endpoint, ca_cert, encrypted_kubernetes_token, encrypted_kubernetes_token_iv, username, encrypted_password, encrypted_password_iv, gcp_project_id, gcp_cluster_zone, gcp_cluster_name, gcp_machine_type, gcp_operation_id, encrypted_gcp_token, encrypted_gcp_token_iv) + VALUES (#{project_id}, #{user_id}, #{service_id}, #{status}, #{gcp_cluster_size}, #{created_at}, #{updated_at}, #{enabled}, #{status_reason}, #{project_namespace}, #{endpoint}, #{ca_cert}, #{encrypted_kubernetes_token}, #{encrypted_kubernetes_token_iv}, #{username}, #{encrypted_password}, #{encrypted_password_iv}, #{gcp_project_id}, #{gcp_cluster_zone}, #{gcp_cluster_name}, #{gcp_machine_type}, #{gcp_operation_id}, #{encrypted_gcp_token}, #{encrypted_gcp_token_iv}); + SQL + end + + it 'correctly migrate to new clusters architectures' do + migrate! + + expect(Clusters::Cluster.count).to eq(1) + expect(Clusters::Project.count).to eq(1) + expect(Clusters::Providers::Gcp.count).to eq(1) + expect(Clusters::Platforms::Kubernetes.count).to eq(1) + + expect(cluster.user).to eq(user) + expect(cluster.enabled).to be_truthy + expect(cluster.name).to eq(tr(gcp_cluster_name)) + expect(cluster.provider_type).to eq('gcp') + expect(cluster.platform_type).to eq('kubernetes') + + expect(cluster.project).to eq(project) + expect(project.cluster).to eq(cluster) + + expect(cluster.provider_gcp.cluster).to eq(cluster) + expect(cluster.provider_gcp.status).to eq(status) + expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason)) + expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id)) + expect(cluster.provider_gcp.zone).to eq(tr(gcp_cluster_zone)) + expect(cluster.provider_gcp.num_nodes).to eq(gcp_cluster_size) + expect(cluster.provider_gcp.machine_type).to eq(tr(gcp_machine_type)) + expect(cluster.provider_gcp.operation_id).to eq(tr(gcp_operation_id)) + expect(cluster.provider_gcp.endpoint).to eq(tr(endpoint)) + expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token)) + expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv)) + + expect(cluster.platform_kubernetes.cluster).to eq(cluster) + expect(cluster.platform_kubernetes.api_url).to eq('https://' + tr(endpoint)) + expect(cluster.platform_kubernetes.ca_cert).to eq(tr(ca_cert)) + expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace)) + expect(cluster.platform_kubernetes.username).to eq(tr(username)) + expect(cluster.platform_kubernetes.encrypted_password).to eq(tr(encrypted_password)) + expect(cluster.platform_kubernetes.encrypted_password_iv).to eq(tr(encrypted_password_iv)) + expect(cluster.platform_kubernetes.encrypted_token).to eq(tr(encrypted_kubernetes_token)) + expect(cluster.platform_kubernetes.encrypted_token_iv).to eq(tr(encrypted_kubernetes_token_iv)) + end end - it 'correctly migrate to new clusters architectures' do - migrate! - - expect(Clusters::Cluster.count).to eq(1) - expect(Clusters::Project.count).to eq(1) - expect(Clusters::Providers::Gcp.count).to eq(1) - expect(Clusters::Platforms::Kubernetes.count).to eq(1) - - expect(cluster.user).to eq(user) - expect(cluster.enabled).to eq(enabled) - expect(cluster.name).to eq(gcp_cluster_name) - expect(cluster.provider_type).to eq('gcp') - expect(cluster.platform_type).to eq('kubernetes') - expect(cluster.created_at).to eq(created_at) - expect(cluster.updated_at).to eq(updated_at) - - expect(cluster.project).to eq(project) - - expect(cluster.provider_gcp.cluster).to eq(cluster) - expect(cluster.provider_gcp.status).to eq(status) - expect(cluster.provider_gcp.status_reason).to eq(status_reason) - expect(cluster.provider_gcp.gcp_project_id).to eq(gcp_project_id) - expect(cluster.provider_gcp.zone).to eq(gcp_cluster_zone) - expect(cluster.provider_gcp.num_nodes).to eq(gcp_cluster_size) - expect(cluster.provider_gcp.machine_type).to eq(gcp_machine_type) - expect(cluster.provider_gcp.operation_id).to eq(gcp_operation_id) - expect(cluster.provider_gcp.endpoint).to eq(endpoint) - expect(cluster.provider_gcp.encrypted_access_token).to eq(encrypted_gcp_token) - expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(encrypted_gcp_token_iv) - expect(cluster.provider_gcp.created_at).to eq(created_at) - expect(cluster.provider_gcp.updated_at).to eq(updated_at) - - expect(cluster.platform_kubernetes.cluster).to eq(cluster) - expect(cluster.platform_kubernetes.api_url).to eq('https://' + endpoint) - expect(cluster.platform_kubernetes.ca_cert).to eq(ca_cert) - expect(cluster.platform_kubernetes.namespace).to eq(project_namespace) - expect(cluster.platform_kubernetes.username).to eq(username) - expect(cluster.platform_kubernetes.encrypted_password).to eq(encrypted_password) - expect(cluster.platform_kubernetes.encrypted_password_iv).to eq(encrypted_password_iv) - expect(cluster.platform_kubernetes.encrypted_token).to eq(encrypted_kubernetes_token) - expect(cluster.platform_kubernetes.encrypted_token_iv).to eq(encrypted_kubernetes_token_iv) - expect(cluster.platform_kubernetes.created_at).to eq(created_at) - expect(cluster.platform_kubernetes.updated_at).to eq(updated_at) + def tr(s) + s.delete("'") end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 41ecdb604f1..5ed2e1ca99a 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1271,6 +1271,7 @@ describe Ci::Build do { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true }, + { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true }, { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true }, { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true }, diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index ab8773b7ede..3106207811a 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -134,6 +134,7 @@ describe Group, 'Routable' do context 'with RequestStore active', :request_store do it 'does not load the route table more than once' do + group.expires_full_path_cache expect(group).to receive(:uncached_full_path).once.and_call_original 3.times { group.full_path } diff --git a/spec/models/fork_network_spec.rb b/spec/models/fork_network_spec.rb index 605ccd6db06..a43baf1820a 100644 --- a/spec/models/fork_network_spec.rb +++ b/spec/models/fork_network_spec.rb @@ -24,6 +24,16 @@ describe ForkNetwork do end end + describe '#merge_requests' do + it 'finds merge requests within the fork network' do + project = create(:project) + forked_project = fork_project(project) + merge_request = create(:merge_request, source_project: forked_project, target_project: project) + + expect(project.fork_network.merge_requests).to include(merge_request) + end + end + context 'for a deleted project' do it 'keeps the fork network' do project = create(:project, :public) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index f36d6eeb327..0e1a7fdce0b 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -488,6 +488,47 @@ describe Group do end end + describe '#path_changed_hook' do + let(:system_hook_service) { SystemHooksService.new } + + context 'for a new group' do + let(:group) { build(:group) } + + before do + expect(group).to receive(:system_hook_service).and_return(system_hook_service) + end + + it 'does not trigger system hook' do + expect(system_hook_service).to receive(:execute_hooks_for).with(group, :create) + + group.save! + end + end + + context 'for an existing group' do + let(:group) { create(:group, path: 'old-path') } + + context 'when the path is changed' do + let(:new_path) { 'very-new-path' } + + it 'triggers the rename system hook' do + expect(group).to receive(:system_hook_service).and_return(system_hook_service) + expect(system_hook_service).to receive(:execute_hooks_for).with(group, :rename) + + group.update_attributes!(path: new_path) + end + end + + context 'when the path is not changed' do + it 'does not trigger system hook' do + expect(group).not_to receive(:system_hook_service) + + group.update_attributes!(name: 'new name') + end + end + end + end + describe '#secret_variables_for' do let(:project) { create(:project, group: group) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ed6e42d476e..e8588975118 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1923,6 +1923,20 @@ describe Project do expect(forked_project.in_fork_network_of?(other_project)).to be_falsy end end + + describe '#fork_source' do + let!(:second_fork) { fork_project(forked_project) } + + it 'returns the direct source if it exists' do + expect(second_fork.fork_source).to eq(forked_project) + end + + it 'returns the root of the fork network when the directs source was deleted' do + forked_project.destroy + + expect(second_fork.fork_source).to eq(project) + end + end end describe '#pushes_since_gc' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fb03e320734..e0896d64c8f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2217,6 +2217,42 @@ describe User do end end + describe '#username_changed_hook' do + context 'for a new user' do + let(:user) { build(:user) } + + it 'does not trigger system hook' do + expect(user).not_to receive(:system_hook_service) + + user.save! + end + end + + context 'for an existing user' do + let(:user) { create(:user, username: 'old-username') } + + context 'when the username is changed' do + let(:new_username) { 'very-new-name' } + + it 'triggers the rename system hook' do + system_hook_service = SystemHooksService.new + expect(system_hook_service).to receive(:execute_hooks_for).with(user, :rename) + expect(user).to receive(:system_hook_service).and_return(system_hook_service) + + user.update_attributes!(username: new_username) + end + end + + context 'when the username is not changed' do + it 'does not trigger system hook' do + expect(user).not_to receive(:system_hook_service) + + user.update_attributes!(email: 'asdf@asdf.com') + end + end + end + end + describe '#sync_attribute?' do let(:user) { described_class.new } diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb index 50d3a4ec982..2bba71fef4f 100644 --- a/spec/services/projects/unlink_fork_service_spec.rb +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -12,6 +12,9 @@ describe Projects::UnlinkForkService do context 'with opened merge request on the source project' do let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: fork_link.forked_from_project) } + let(:merge_request2) { create(:merge_request, source_project: forked_project, target_project: fork_project(project)) } + let(:merge_request_in_fork) { create(:merge_request, source_project: forked_project, target_project: forked_project) } + let(:mr_close_service) { MergeRequests::CloseService.new(forked_project, user) } before do @@ -22,9 +25,14 @@ describe Projects::UnlinkForkService do it 'close all pending merge requests' do expect(mr_close_service).to receive(:execute).with(merge_request) + expect(mr_close_service).to receive(:execute).with(merge_request2) subject.execute end + + it 'does not close merge requests for the project being unlinked' do + expect(mr_close_service).not_to receive(:execute).with(merge_request_in_fork) + end end it 'remove fork relation' do @@ -53,4 +61,14 @@ describe Projects::UnlinkForkService do expect(source.forks_count).to be_zero end + + context 'when the original project was deleted' do + it 'does not fail when the original project is deleted' do + source = forked_project.forked_from_project + source.destroy + forked_project.reload + + expect { subject.execute }.not_to raise_error + end + end end diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 8f7aea533dc..46cd10cdc12 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -69,11 +69,48 @@ describe SystemHooksService do expect(data[:project_visibility]).to eq('private') end + + context 'group_rename' do + it 'contains old and new path' do + allow(group).to receive(:path_was).and_return('old-path') + + data = event_data(group, :rename) + + expect(data).to include(:event_name, :name, :created_at, :updated_at, :full_path, :path, :group_id, :old_path, :old_full_path) + expect(data[:path]).to eq(group.path) + expect(data[:full_path]).to eq(group.path) + expect(data[:old_path]).to eq(group.path_was) + expect(data[:old_full_path]).to eq(group.path_was) + end + + it 'contains old and new full_path for subgroup' do + subgroup = create(:group, parent: group) + allow(subgroup).to receive(:path_was).and_return('old-path') + + data = event_data(subgroup, :rename) + + expect(data[:full_path]).to eq(subgroup.full_path) + expect(data[:old_path]).to eq('old-path') + end + end + + context 'user_rename' do + it 'contains old and new username' do + allow(user).to receive(:username_was).and_return('old-username') + + data = event_data(user, :rename) + + expect(data).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username, :old_username) + expect(data[:username]).to eq(user.username) + expect(data[:old_username]).to eq(user.username_was) + end + end end context 'event names' do it { expect(event_name(user, :create)).to eq "user_create" } it { expect(event_name(user, :destroy)).to eq "user_destroy" } + it { expect(event_name(user, :rename)).to eq 'user_rename' } it { expect(event_name(project, :create)).to eq "project_create" } it { expect(event_name(project, :destroy)).to eq "project_destroy" } it { expect(event_name(project, :rename)).to eq "project_rename" } @@ -85,6 +122,7 @@ describe SystemHooksService do it { expect(event_name(key, :destroy)).to eq 'key_destroy' } it { expect(event_name(group, :create)).to eq 'group_create' } it { expect(event_name(group, :destroy)).to eq 'group_destroy' } + it { expect(event_name(group, :rename)).to eq 'group_rename' } it { expect(event_name(group_member, :create)).to eq 'user_add_to_group' } it { expect(event_name(group_member, :destroy)).to eq 'user_remove_from_group' } end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0dc417b3cb6..7c8331f6c60 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -48,6 +48,9 @@ RSpec.configure do |config| config.include Warden::Test::Helpers, type: :request config.include LoginHelpers, type: :feature config.include SearchHelpers, type: :feature + config.include CookieHelper, :js + config.include InputHelper, :js + config.include InspectRequests, :js config.include WaitForRequests, :js config.include LiveDebugger, :js config.include StubConfiguration diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index c45c4a4310d..9f672bc92fc 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -1,25 +1,25 @@ # rubocop:disable Style/GlobalVars require 'capybara/rails' require 'capybara/rspec' -require 'capybara/poltergeist' require 'capybara-screenshot/rspec' +require 'selenium-webdriver' # Give CI some extra time timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 -Capybara.javascript_driver = :poltergeist -Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new( - app, - js_errors: true, - timeout: timeout, - window_size: [1366, 768], - url_whitelist: %w[localhost 127.0.0.1], - url_blacklist: %w[.mp4 .png .gif .avi .bmp .jpg .jpeg], - phantomjs_options: [ - '--load-images=yes' - ] +Capybara.javascript_driver = :chrome +Capybara.register_driver :chrome do |app| + extra_args = [] + extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i + + capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( + chromeOptions: { + 'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args + } ) + + Capybara::Selenium::Driver + .new(app, browser: :chrome, desired_capabilities: capabilities) end Capybara.default_max_wait_time = timeout @@ -27,6 +27,10 @@ Capybara.ignore_hidden_elements = true # Keep only the screenshots generated from the last failing test suite 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 RSpec.configure do |config| config.before(:context, :js) do @@ -37,13 +41,23 @@ RSpec.configure do |config| end config.before(:example, :js) do + session = Capybara.current_session + allow(Gitlab::Application.routes).to receive(:default_url_options).and_return( - host: Capybara.current_session.server.host, - port: Capybara.current_session.server.port, + host: session.server.host, + port: session.server.port, protocol: 'http') + + # reset window size between tests + unless session.current_window.size == [1240, 1400] + session.current_window.resize_to(1240, 1400) rescue nil + end end config.after(:example, :js) do |example| + # prevent localstorage from introducing side effects based on test order + execute_script("localStorage.clear();") + # capybara/rspec already calls Capybara.reset_sessions! in an `after` hook, # but `block_and_wait_for_requests_complete` is called before it so by # calling it explicitely here, we prevent any new requests from being fired diff --git a/spec/support/capybara_helpers.rb b/spec/support/capybara_helpers.rb index 3eb7bea3227..868233416bf 100644 --- a/spec/support/capybara_helpers.rb +++ b/spec/support/capybara_helpers.rb @@ -38,7 +38,7 @@ module CapybaraHelpers # Simulate a browser restart by clearing the session cookie. def clear_browser_session - page.driver.remove_cookie('_gitlab_session') + page.driver.browser.manage.delete_cookie('_gitlab_session') end end diff --git a/spec/support/cookie_helper.rb b/spec/support/cookie_helper.rb new file mode 100644 index 00000000000..224619c899c --- /dev/null +++ b/spec/support/cookie_helper.rb @@ -0,0 +1,17 @@ +# Helper for setting cookies in Selenium/WebDriver +# +module CookieHelper + def set_cookie(name, value, options = {}) + # Selenium driver will not set cookies for a given domain when the browser is at `about:blank`. + # It also doesn't appear to allow overriding the cookie path. loading `/` is the most inclusive. + visit options.fetch(:path, '/') unless on_a_page? + page.driver.browser.manage.add_cookie(name: name, value: value, **options) + end + + private + + def on_a_page? + current_url = Capybara.current_session.driver.browser.current_url + current_url && current_url != '' && current_url != 'about:blank' && current_url != 'data:,' + end +end diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb index 7132b9cd221..aabc64d972b 100644 --- a/spec/support/features/discussion_comments_shared_example.rb +++ b/spec/support/features/discussion_comments_shared_example.rb @@ -71,26 +71,28 @@ shared_examples 'discussion comments' do |resource_name| expect(page).not_to have_selector menu_selector find(toggle_selector).click - find('body').trigger 'click' + find('body').click expect(page).not_to have_selector menu_selector end it 'clicking the ul padding or divider should not change the text' do - find(menu_selector).trigger 'click' + execute_script("document.querySelector('#{menu_selector}').click()") + # on issues page, the menu closes when clicking anywhere, on other pages it will + # remain open if clicking divider or menu padding, but should not change button action if resource_name == 'issue' expect(find(dropdown_selector)).to have_content 'Comment' find(toggle_selector).click - find("#{menu_selector} .divider").trigger 'click' + execute_script("document.querySelector('#{menu_selector} .divider').click()") else - find(menu_selector).trigger 'click' + execute_script("document.querySelector('#{menu_selector}').click()") expect(page).to have_selector menu_selector expect(find(dropdown_selector)).to have_content 'Comment' - find("#{menu_selector} .divider").trigger 'click' + execute_script("document.querySelector('#{menu_selector} .divider').click()") expect(page).to have_selector menu_selector end @@ -105,7 +107,12 @@ shared_examples 'discussion comments' do |resource_name| end it 'updates the submit button text and closes the dropdown' do - expect(find(dropdown_selector)).to have_content 'Start discussion' + # on issues page, the submit input is a <button>, on other pages it is <input> + if resource_name == 'issue' + expect(find(submit_selector)).to have_content 'Start discussion' + else + expect(find(submit_selector).value).to eq 'Start discussion' + end expect(page).not_to have_selector menu_selector end @@ -187,7 +194,12 @@ shared_examples 'discussion comments' do |resource_name| end it 'updates the submit button text and closes the dropdown' do - expect(find(dropdown_selector)).to have_content 'Comment' + # on issues page, the submit input is a <button>, on other pages it is <input> + if resource_name == 'issue' + expect(find(submit_selector)).to have_content 'Comment' + else + expect(find(submit_selector).value).to eq 'Comment' + end expect(page).not_to have_selector menu_selector end @@ -226,6 +238,7 @@ shared_examples 'discussion comments' do |resource_name| describe "on a closed #{resource_name}" do before do find("#{form_selector} .js-note-target-close").click + wait_for_requests find("#{form_selector} .note-textarea").send_keys('a') end diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb index 061e0d35590..08e21ee2537 100644 --- a/spec/support/features/issuable_slash_commands_shared_examples.rb +++ b/spec/support/features/issuable_slash_commands_shared_examples.rb @@ -61,7 +61,7 @@ shared_examples 'issuable record that supports quick actions in its description context 'with a note containing commands' do it 'creates a note without the commands and interpret the commands accordingly' do assignee = create(:user, username: 'bob') - write_note("Awesome!\n/assign @bob\n/label ~bug\n/milestone %\"ASAP\"") + write_note("Awesome!\n\n/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"") expect(page).to have_content 'Awesome!' expect(page).not_to have_content '/assign @bob' @@ -82,7 +82,7 @@ shared_examples 'issuable record that supports quick actions in its description context 'with a note containing only commands' do it 'does not create a note but interpret the commands accordingly' do assignee = create(:user, username: 'bob') - write_note("/assign @bob\n/label ~bug\n/milestone %\"ASAP\"") + write_note("/assign @bob\n\n/label ~bug\n\n/milestone %\"ASAP\"") expect(page).not_to have_content '/assign @bob' expect(page).not_to have_content '/label ~bug' diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 192a2fed0a8..836e5e7be23 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -39,7 +39,7 @@ shared_examples 'reportable note' do |type| end def open_dropdown(dropdown) - dropdown.find('.more-actions-toggle').trigger('click') + dropdown.find('.more-actions-toggle').click dropdown.find('.dropdown-menu li', match: :first) end end diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb index fd22e384b1b..c98aa503ed1 100644 --- a/spec/support/helpers/merge_request_diff_helpers.rb +++ b/spec/support/helpers/merge_request_diff_helpers.rb @@ -2,7 +2,7 @@ module MergeRequestDiffHelpers def click_diff_line(line_holder, diff_side = nil) line = get_line_components(line_holder, diff_side) line[:content].hover - line[:num].find('.add-diff-note').trigger('click') + line[:num].find('.add-diff-note', visible: false).send_keys(:return) end def get_line_components(line_holder, diff_side = nil) diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb index 86008698692..79a0aa174b1 100644 --- a/spec/support/helpers/note_interaction_helpers.rb +++ b/spec/support/helpers/note_interaction_helpers.rb @@ -2,7 +2,7 @@ module NoteInteractionHelpers def open_more_actions_dropdown(note) note_element = find("#note_#{note.id}") - note_element.find('.more-actions-toggle').trigger('click') + note_element.find('.more-actions-toggle').click note_element.find('.more-actions .dropdown-menu li', match: :first) end end diff --git a/spec/support/input_helper.rb b/spec/support/input_helper.rb new file mode 100644 index 00000000000..acbb42274ec --- /dev/null +++ b/spec/support/input_helper.rb @@ -0,0 +1,7 @@ +# see app/assets/javascripts/test_utils/simulate_input.js + +module InputHelper + def simulate_input(selector, input = '') + evaluate_script("window.simulateInput(#{selector.to_json}, #{input.to_json});") + end +end diff --git a/spec/support/inspect_requests.rb b/spec/support/inspect_requests.rb new file mode 100644 index 00000000000..88ddc5c7f6c --- /dev/null +++ b/spec/support/inspect_requests.rb @@ -0,0 +1,17 @@ +require_relative './wait_for_requests' + +module InspectRequests + extend self + include WaitForRequests + + def inspect_requests(inject_headers: {}) + Gitlab::Testing::RequestInspectorMiddleware.log_requests!(inject_headers) + + yield + + wait_for_all_requests + Gitlab::Testing::RequestInspectorMiddleware.requests + ensure + Gitlab::Testing::RequestInspectorMiddleware.stop_logging! + end +end diff --git a/spec/support/mobile_helpers.rb b/spec/support/mobile_helpers.rb index 431f20a2a5c..3b9eb84e824 100644 --- a/spec/support/mobile_helpers.rb +++ b/spec/support/mobile_helpers.rb @@ -12,6 +12,6 @@ module MobileHelpers end def resize_window(width, height) - page.driver.resize_window width, height + Capybara.current_session.current_window.resize_to(width, height) end end diff --git a/spec/support/protected_tags/access_control_ce_shared_examples.rb b/spec/support/protected_tags/access_control_ce_shared_examples.rb index 421a51fc336..2770cdcbefc 100644 --- a/spec/support/protected_tags/access_control_ce_shared_examples.rb +++ b/spec/support/protected_tags/access_control_ce_shared_examples.rb @@ -9,7 +9,7 @@ RSpec.shared_examples "protected tags > access control > CE" do allowed_to_create_button = find(".js-allowed-to-create") unless allowed_to_create_button.text == access_type_name - allowed_to_create_button.trigger('click') + allowed_to_create_button.click find('.create_access_levels-container .dropdown-menu li', match: :first) within('.create_access_levels-container .dropdown-menu') { click_on access_type_name } end diff --git a/spec/support/quick_actions_helpers.rb b/spec/support/quick_actions_helpers.rb index d2aaae7518f..361190aa352 100644 --- a/spec/support/quick_actions_helpers.rb +++ b/spec/support/quick_actions_helpers.rb @@ -3,7 +3,7 @@ module QuickActionsHelpers Sidekiq::Testing.fake! do page.within('.js-main-target-form') do fill_in 'note[note]', with: text - find('.js-comment-submit-button').trigger('click') + find('.js-comment-submit-button').click end end end diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index d5bc12f3bc5..5fde91512da 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -9,7 +9,7 @@ shared_examples "protected branches > access control > CE" do allowed_to_push_button = find(".js-allowed-to-push") unless allowed_to_push_button.text == access_type_name - allowed_to_push_button.trigger('click') + allowed_to_push_button.click within(".dropdown.open .dropdown-menu") { click_on access_type_name } end end @@ -34,7 +34,7 @@ shared_examples "protected branches > access control > CE" do within('.js-allowed-to-push-container') do expect(first("li")).to have_content("Roles") - click_on access_type_name + find(:link, access_type_name).click end end @@ -79,7 +79,7 @@ shared_examples "protected branches > access control > CE" do within('.js-allowed-to-merge-container') do expect(first("li")).to have_content("Roles") - click_on access_type_name + find(:link, access_type_name).click end end diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index 4d448a55978..4ead78529c3 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -38,6 +38,10 @@ module StubConfiguration allow(Gitlab.config.backup).to receive_messages(to_settings(messages)) end + def stub_lfs_setting(messages) + allow(Gitlab.config.lfs).to receive_messages(to_settings(messages)) + end + def stub_storage_settings(messages) # Default storage is always required messages['default'] ||= Gitlab.config.repositories.storages.default diff --git a/spec/support/time_tracking_shared_examples.rb b/spec/support/time_tracking_shared_examples.rb index 0fa74f911f6..909d4e2ee8d 100644 --- a/spec/support/time_tracking_shared_examples.rb +++ b/spec/support/time_tracking_shared_examples.rb @@ -80,6 +80,6 @@ end def submit_time(quick_action) fill_in 'note[note]', with: quick_action - find('.js-comment-submit-button').trigger('click') + find('.js-comment-submit-button').click wait_for_requests end diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb index b5c3c0f55b8..f4130d68271 100644 --- a/spec/support/wait_for_requests.rb +++ b/spec/support/wait_for_requests.rb @@ -1,25 +1,47 @@ -require_relative './wait_for_requests' - module WaitForRequests extend self # This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests def block_and_wait_for_requests_complete + block_requests { wait_for_all_requests } + end + + # Block all requests inside block with 503 response + def block_requests Gitlab::Testing::RequestBlockerMiddleware.block_requests! - wait_for('pending requests complete') do - Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? && finished_all_requests? - end + yield ensure Gitlab::Testing::RequestBlockerMiddleware.allow_requests! end + # Slow down requests inside block by injecting `sleep 0.2` before each response + def slow_requests + Gitlab::Testing::RequestBlockerMiddleware.slow_requests! + yield + ensure + Gitlab::Testing::RequestBlockerMiddleware.allow_requests! + end + + # Wait for client-side AJAX requests def wait_for_requests - wait_for('JS requests') { finished_all_requests? } + wait_for('JS requests complete') { finished_all_js_requests? } + end + + # Wait for active Rack requests and client-side AJAX requests + def wait_for_all_requests + wait_for('pending requests complete') do + finished_all_rack_reqiests? && + finished_all_js_requests? + end end private - def finished_all_requests? + def finished_all_rack_reqiests? + Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? + end + + def finished_all_js_requests? return true unless javascript_test? finished_all_ajax_requests? && diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index f7e77de34ff..6a341a3f0fe 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -1,5 +1,14 @@ +/* + * This is a modified version of https://github.com/peek/peek/blob/master/app/assets/javascripts/peek.js + * + * - Removed the dependency on jquery.tipsy + * - Removed the initializeTipsy and toggleBar functions + * - Customized updatePerformanceBar to handle SQL queries report specificities + * - Changed /peek/results to /-/peek/results + * - Removed the keypress, pjax:end, page:change, and turbolinks:load handlers + */ (function($) { - var fetchRequestResults, getRequestId, peekEnabled, toggleBar, updatePerformanceBar; + var fetchRequestResults, getRequestId, peekEnabled, updatePerformanceBar; getRequestId = function() { return $('#peek').data('request-id'); }; @@ -41,22 +50,6 @@ }); return $(document).trigger('peek:render', [getRequestId(), results]); }; - toggleBar = function(event) { - var wrapper; - if ($(event.target).is(':input')) { - return; - } - if (event.which === 96 && !event.metaKey) { - wrapper = $('#peek'); - if (wrapper.hasClass('disabled')) { - wrapper.removeClass('disabled'); - return document.cookie = "peek=true; path=/"; - } else { - wrapper.addClass('disabled'); - return document.cookie = "peek=false; path=/"; - } - } - }; fetchRequestResults = function() { return $.ajax('/-/peek/results', { data: { @@ -68,7 +61,6 @@ error: function(xhr, textStatus, error) {} }); }; - $(document).on('keypress', toggleBar); $(document).on('peek:update', fetchRequestResults); return $(function() { if (peekEnabled()) { |