提交 08362d80 编写于 作者: F Filipa Lacerda

Merge branch 'master' into 37220-es-modules

* master: (35 commits)
  Use WikiPages::CreateService in spec/features/projects/wiki/user_updates_wiki_page_spec.rb
  Replace the 'project/merge_requests/revert.feature' spinach test with an rspec analog
  Docs group mrs list view search bar
  Replace the project/milestone.feature spinach test with an rspec analog
  Make all the tooltips in the same direction on the commit info box
  Reset all connection schema cache after migration tests
  Emoji was rendered as italic
  Adds Event polyfill for IE
  Add gitaly to patch update doc
  Document how to swap database tables.
  Replace 'project/wiki.feature' spinach test with an rspec analog
  Check for sidebar cookie instead of class when resizing window
  update installation and update instructions for 10.0
  Replace the 'project/merge_requests/accept.feature' spinach test with an rspec analog
  Remove confidential toggle checkbox and related code as no longer necessary
  Bump grape_logging gem to 1.7.0 to get status codes for error messages
  Expand filtered parameters to include `token`
  Replace the project/team_management.feature spinach test with an rspec analog
  Replace the profile/emails.feature spinach test with an rspec analog
  Replace project/group_links.feature spinach test with an rspec analog
  ...
......@@ -407,4 +407,4 @@ gem 'flipper-active_record', '~> 0.10.2'
# Structured logging
gem 'lograge', '~> 0.5'
gem 'grape_logging', '~> 1.6'
gem 'grape_logging', '~> 1.7'
......@@ -355,7 +355,7 @@ GEM
activesupport
grape (>= 0.16.0)
rake
grape_logging (1.6.0)
grape_logging (1.7.0)
grape
grpc (1.4.5)
google-protobuf (~> 3.1)
......@@ -1037,7 +1037,7 @@ DEPENDENCIES
grape (~> 1.0)
grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.1.0)
grape_logging (~> 1.6)
grape_logging (~> 1.7)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hashie-forbidden_attributes
......
......@@ -12,4 +12,5 @@ import 'core-js/fn/symbol';
// Browser polyfills
import './polyfills/custom_event';
import './polyfills/element';
import './polyfills/event';
import './polyfills/nodelist';
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function CustomEvent(event, params) {
const evt = document.createEvent('CustomEvent');
const evtParams = params || { bubbles: false, cancelable: false, detail: undefined };
const evtParams = {
bubbles: false,
cancelable: false,
detail: undefined,
...params,
};
evt.initCustomEvent(event, evtParams.bubbles, evtParams.cancelable, evtParams.detail);
return evt;
};
......
/**
* Polyfill for IE11 support.
* new Event() is not supported by IE11.
* Although `initEvent` is deprecated for modern browsers it is the one supported by IE
*/
if (typeof window.Event !== 'function') {
window.Event = function Event(event, params) {
const evt = document.createEvent('Event');
const evtParams = {
bubbles: false,
cancelable: false,
...params,
};
evt.initEvent(event, evtParams.bubbles, evtParams.cancelable);
return evt;
};
window.Event.prototype = Event;
}
......@@ -72,10 +72,6 @@ export default {
required: false,
default: () => [],
},
isConfidential: {
type: Boolean,
required: true,
},
markdownPreviewPath: {
type: String,
required: true,
......@@ -131,7 +127,6 @@ export default {
this.showForm = true;
this.store.setFormState({
title: this.state.titleText,
confidential: this.isConfidential,
description: this.state.descriptionText,
lockedWarningVisible: false,
updateLoading: false,
......@@ -147,8 +142,6 @@ export default {
.then((data) => {
if (location.pathname !== data.web_url) {
gl.utils.visitUrl(data.web_url);
} else if (data.confidential !== this.isConfidential) {
gl.utils.visitUrl(location.pathname);
}
return this.service.getData();
......
<script>
export default {
props: {
formState: {
type: Object,
required: true,
},
},
};
</script>
<template>
<fieldset class="checkbox">
<label for="issue-confidential">
<input
type="checkbox"
value="1"
id="issue-confidential"
v-model="formState.confidential" />
This issue is confidential and should only be visible to team members with at least Reporter access.
</label>
</fieldset>
</template>
......@@ -4,7 +4,6 @@
import descriptionField from './fields/description.vue';
import editActions from './edit_actions.vue';
import descriptionTemplate from './fields/description_template.vue';
import confidentialCheckbox from './fields/confidential_checkbox.vue';
export default {
props: {
......@@ -44,7 +43,6 @@
descriptionField,
descriptionTemplate,
editActions,
confidentialCheckbox,
},
computed: {
hasIssuableTemplates() {
......@@ -81,8 +79,6 @@
:form-state="formState"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" />
<confidential-checkbox
:form-state="formState" />
<edit-actions
:form-state="formState"
:can-destroy="canDestroy" />
......
......@@ -35,7 +35,6 @@ document.addEventListener('DOMContentLoaded', () => {
initialDescriptionHtml: this.initialDescriptionHtml,
initialDescriptionText: this.initialDescriptionText,
issuableTemplates: this.issuableTemplates,
isConfidential: this.isConfidential,
markdownPreviewPath: this.markdownPreviewPath,
markdownDocsPath: this.markdownDocsPath,
projectPath: this.projectPath,
......
......@@ -3,7 +3,6 @@ export default class Store {
this.state = initialState;
this.formState = {
title: '',
confidential: false,
description: '',
lockedWarningVisible: false,
updateLoading: false,
......
......@@ -68,7 +68,7 @@ export default class NewNavSidebar {
if (breakpoint === 'sm' || breakpoint === 'md') {
this.toggleCollapsedSidebar(true);
} else if (breakpoint === 'lg') {
const collapse = this.$sidebar.hasClass('sidebar-icons-only');
const collapse = Cookies.get('sidebar_collapsed') === 'true';
this.toggleCollapsedSidebar(collapse);
}
}
......
gl-emoji {
font-style: normal;
display: inline-flex;
vertical-align: middle;
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
......
......@@ -17,8 +17,11 @@
max-width: $limited-layout-width-sm;
margin-left: auto;
margin-right: auto;
padding-top: 64px;
padding-bottom: 64px;
@media (min-width: $screen-md-min) {
padding-top: 64px;
padding-bottom: 64px;
}
}
}
......
......@@ -608,7 +608,7 @@
+ .files,
+ .alert {
margin-top: 30px;
margin-top: 32px;
}
}
}
......
......@@ -213,7 +213,6 @@ module IssuablesHelper
canUpdate: can?(current_user, :update_issue, issuable),
canDestroy: can?(current_user, :destroy_issue, issuable),
issuableRef: issuable.to_reference,
isConfidential: issuable.confidential,
markdownPreviewPath: preview_markdown_path(@project),
markdownDocsPath: help_page_path('user/markdown'),
issuableTemplates: issuable_templates(issuable),
......
......@@ -137,15 +137,7 @@ module ProjectsHelper
end
def last_push_event
return unless current_user
return current_user.recent_push unless @project
project_ids = [@project.id]
if fork = current_user.fork_of(@project)
project_ids << fork.id
end
current_user.recent_push(project_ids)
current_user&.recent_push(@project)
end
def project_feature_access_select(field)
......
......@@ -247,7 +247,7 @@ class ApplicationSetting < ActiveRecord::Base
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Gitlab::ImportSources.values,
import_sources: Settings.gitlab['import_sources'],
koding_enabled: false,
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
......
......@@ -49,7 +49,7 @@ class Event < ActiveRecord::Base
belongs_to :author, class_name: "User"
belongs_to :project
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
has_one :push_event_payload, foreign_key: :event_id
has_one :push_event_payload
# Callbacks
after_create :reset_project_activity
......
class GpgSignature < ActiveRecord::Base
include ShaAttribute
include IgnorableColumn
ignore_column :valid_signature
sha_attribute :commit_sha
sha_attribute :gpg_key_primary_keyid
......
......@@ -30,6 +30,44 @@ class PushEvent < Event
delegate :commit_count, to: :push_event_payload
alias_method :commits_count, :commit_count
# Returns events of pushes that either pushed to an existing ref or created a
# new one.
def self.created_or_pushed
actions = [
PushEventPayload.actions[:pushed],
PushEventPayload.actions[:created]
]
joins(:push_event_payload)
.where(push_event_payloads: { action: actions })
end
# Returns events of pushes to a branch.
def self.branch_events
ref_type = PushEventPayload.ref_types[:branch]
joins(:push_event_payload)
.where(push_event_payloads: { ref_type: ref_type })
end
# Returns PushEvent instances for which no merge requests have been created.
def self.without_existing_merge_requests
existing_mrs = MergeRequest.except(:order)
.select(1)
.where('merge_requests.source_project_id = events.project_id')
.where('merge_requests.source_branch = push_event_payloads.ref')
# For reasons unknown the use of #eager_load will result in the
# "push_event_payload" association not being set. Because of this we're
# using "joins" here, which does mean an additional query needs to be
# executed in order to retrieve the "push_event_association" when the
# returned PushEvent is used.
joins(:push_event_payload)
.where('NOT EXISTS (?)', existing_mrs)
.created_or_pushed
.branch_events
end
def self.sti_name
PUSHED
end
......
......@@ -650,20 +650,13 @@ class User < ActiveRecord::Base
@personal_projects_count ||= personal_projects.count
end
def recent_push(project_ids = nil)
# Get push events not earlier than 2 hours ago
events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
events = events.where(project_id: project_ids) if project_ids
def recent_push(project = nil)
service = Users::LastPushEventService.new(self)
# Use the latest event that has not been pushed or merged recently
events.includes(:project).recent.find do |event|
next unless event.project.repository.branch_exists?(event.branch_name)
merge_requests = MergeRequest.where("created_at >= ?", event.created_at)
.where(source_project_id: event.project.id,
source_branch: event.branch_name)
merge_requests.empty?
if project
service.last_event_for_project(project)
else
service.last_event_for_user
end
end
......
......@@ -74,12 +74,19 @@ class EventCreateService
# We're using an explicit transaction here so that any errors that may occur
# when creating push payload data will result in the event creation being
# rolled back as well.
Event.transaction do
event = create_event(project, current_user, Event::PUSHED)
event = Event.transaction do
new_event = create_event(project, current_user, Event::PUSHED)
PushEventPayloadService.new(event, push_data).execute
PushEventPayloadService
.new(new_event, push_data)
.execute
new_event
end
Users::LastPushEventService.new(current_user)
.cache_last_push_event(event)
Users::ActivityService.new(current_user, 'push').execute
end
......
module Users
# Service class for caching and retrieving the last push event of a user.
class LastPushEventService
EXPIRATION = 2.hours
def initialize(user)
@user = user
end
# Caches the given push event for the current user in the Rails cache.
#
# event - An instance of PushEvent to cache.
def cache_last_push_event(event)
keys = [
project_cache_key(event.project),
user_cache_key
]
if event.project.forked?
keys << project_cache_key(event.project.forked_from_project)
end
keys.each { |key| set_key(key, event.id) }
end
# Returns the last PushEvent for the current user.
#
# This method will return nil if no event was found.
def last_event_for_user
find_cached_event(user_cache_key)
end
# Returns the last PushEvent for the current user and the given project.
#
# project - An instance of Project for which to retrieve the PushEvent.
#
# This method will return nil if no event was found.
def last_event_for_project(project)
find_cached_event(project_cache_key(project))
end
def find_cached_event(cache_key)
event_id = get_key(cache_key)
return unless event_id
unless (event = find_event_in_database(event_id))
# We don't want to keep querying the same data over and over when a
# merge request has been created, thus we remove the key if no event
# (meaning an MR was created) is returned.
Rails.cache.delete(cache_key)
end
event
end
private
def find_event_in_database(id)
PushEvent
.without_existing_merge_requests
.find_by(id: id)
end
def user_cache_key
"last-push-event/#{@user.id}"
end
def project_cache_key(project)
"last-push-event/#{@user.id}/#{project.id}"
end
def get_key(key)
Rails.cache.read(key, raw: true)
end
def set_key(key, value)
# We're using raw values here since this takes up less space and we don't
# store complex objects.
Rails.cache.write(key, value, raw: true, expires_in: EXPIRATION)
end
end
end
......@@ -6,7 +6,7 @@
= icon('wrench')
.sidebar-context-title Admin Area
%ul.sidebar-top-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: {class: 'home'}) do
= sidebar_link admin_root_path, title: _('Overview'), css: 'shortcuts-tree' do
.nav-icon-container
= custom_icon('overview')
......@@ -14,7 +14,7 @@
Overview
%ul.sidebar-sub-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: { class: "fly-out-top-item" } ) do
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
#{ _('Overview') }
......@@ -52,16 +52,16 @@
%span
ConvDev Index
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
= sidebar_link admin_conversational_development_index_path, title: _('Monitoring') do
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
= sidebar_link admin_system_info_path, title: _('Monitoring') do
.nav-icon-container
= custom_icon('monitoring')
%span.nav-item-name
Monitoring
%ul.sidebar-sub-level-items
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_conversational_development_index_path do
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_system_info_path do
%strong.fly-out-top-item-name
#{ _('Monitoring') }
%li.divider.fly-out-top-item
......
......@@ -16,10 +16,10 @@
.preview= image_tag "#{scheme.css_class}-scheme-preview.png"
= f.radio_button :color_scheme_id, scheme.id
= scheme.name
.col-sm-12
%hr
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Behavior
......
......@@ -32,7 +32,7 @@
.commiter
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- commit_timeago = time_ago_with_tooltip(commit.committed_date)
- commit_timeago = time_ago_with_tooltip(commit.committed_date, placement: 'bottom')
- commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
#{ commit_text.html_safe }
......
---
title: Fixed merge request changes bar jumping
merge_request:
author:
type: fixed
---
title: Tooltips in the commit info box now all face the same direction
merge_request:
author: Jedidiah Broadbent
type: fixed
---
title: Fix ConvDev Index nav item and Monitoring submenu regression
merge_request: !14124
author:
type: fixed
---
title: Adds Event polyfill for IE11
merge_request:
author:
type: fixed
---
title: Read import sources from setting at first initialization
merge_request: 14141
author: Visay Keo
type: fixed
---
title: Update native unicode emojis to always render as normal text (previously could render italicized)
merge_request:
author: Branka Martinovic
type: fixed
---
title: Replace the profile/emails.feature spinach test with an rspec analog
merge_request: 14172
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace project/group_links.feature spinach test with an rspec analog
merge_request: 14169
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the project/milestone.feature spinach test with an rspec analog
merge_request: 14171
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the 'project/merge_requests/accept.feature' spinach test with an rspec analog
merge_request: 14176
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the 'project/merge_requests/revert.feature' spinach test with an rspec
analog
merge_request: 14201
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace 'project/wiki.feature' spinach test with an rspec analog
merge_request: 13856
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the project/team_management.feature spinach test with an rspec analog
merge_request: 14173
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Rework how recent push events are retrieved
merge_request:
author:
type: other
......@@ -51,7 +51,7 @@ module Gitlab
# Configure sensitive parameters which will be filtered from the log file.
#
# Parameters filtered:
# - Any parameter ending with `_token`
# - Any parameter ending with `token`
# - Any parameter containing `password`
# - Any parameter containing `secret`
# - Two-factor tokens (:otp_attempt)
......@@ -61,7 +61,7 @@ module Gitlab
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key)
config.filter_parameters += [/_token$/, /password/, /secret/]
config.filter_parameters += [/token$/, /password/, /secret/]
config.filter_parameters += %i(
certificate
encrypted_key
......
......@@ -269,7 +269,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.__send__(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project gitea]
Settings.gitlab['import_sources'] ||= Gitlab::ImportSources.values
Settings.gitlab['trusted_proxies'] ||= []
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
Settings.gitlab['usage_ping_enabled'] = true if Settings.gitlab['usage_ping_enabled'].nil?
......
......@@ -4,12 +4,21 @@
- title: "Throughput"
y_label: "Requests / Sec"
required_metrics:
- nginx_upstream_requests_total
- nginx_upstream_responses_total
weight: 1
queries:
- query_range: 'sum(rate(nginx_upstream_requests_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))'
label: Total
- query_range: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code)'
unit: req / sec
label: Status Code
series:
- label: status_code
when:
- value: 2xx
color: green
- value: 4xx
color: orange
- value: 5xx
color: red
- title: "Latency"
y_label: "Latency (ms)"
required_metrics:
......@@ -37,9 +46,17 @@
- haproxy_frontend_http_requests_total
weight: 1
queries:
- query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m]))'
label: Total
- query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) by (code)'
unit: req / sec
series:
- label: code
when:
- value: 2xx
color: green
- value: 4xx
color: yellow
- value: 5xx
color: red
- title: "HTTP Error Rate"
y_label: "Error Rate (%)"
required_metrics:
......@@ -86,12 +103,21 @@
- title: "Throughput"
y_label: "Requests / Sec"
required_metrics:
- nginx_requests_total
- nginx_responses_total
weight: 1
queries:
- query_range: 'sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m]))'
label: Total
- query_range: 'sum(rate(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (status_code)'
unit: req / sec
label: Status Code
series:
- label: status_code
when:
- value: 2xx
color: green
- value: 4xx
color: orange
- value: 5xx
color: red
- title: "Latency"
y_label: "Latency (ms)"
required_metrics:
......@@ -128,6 +154,8 @@
- container_cpu_usage_seconds_total
weight: 1
queries:
- query_range: 'sum(rate(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}[2m])) / count(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}) * 100'
label: Average
- query_range: 'sum(rate(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}[2m])) by (cpu) * 100'
label: CPU
unit: "%"
series:
- label: cpu
......@@ -60,6 +60,7 @@
- [Ordering Table Columns](ordering_table_columns.md)
- [Verifying Database Capabilities](verifying_database_capabilities.md)
- [Hash Indexes](hash_indexes.md)
- [Swapping Tables](swapping_tables.md)
## i18n
......
# Swapping Tables
Sometimes you need to replace one table with another. For example, when
migrating data in a very large table it's often better to create a copy of the
table and insert & migrate the data into this new table in the background.
Let's say you want to swap the table "events" with "events_for_migration". In
this case you need to follow 3 steps:
1. Rename "events" to "events_temporary"
2. Rename "events_for_migration" to "events"
3. Rename "events_temporary" to "events_for_migration"
Rails allows you to do this using the `rename_table` method:
```ruby
rename_table :events, :events_temporary
rename_table :events_for_migration, :events
rename_table :events_temporary, :events_for_migration
```
This does not require any downtime as long as the 3 `rename_table` calls are
executed in the _same_ database transaction. Rails by default uses database
transactions for migrations, but if it doesn't you'll need to start one
manually:
```ruby
Event.transaction do
rename_table :events, :events_temporary
rename_table :events_for_migration, :events
rename_table :events_temporary, :events_for_migration
end
```
Once swapped you _have to_ reset the primary key of the new table. For
PostgreSQL you can use the `reset_pk_sequence!` method like so:
```ruby
reset_pk_sequence!('events')
```
For MySQL however you need to do run the following:
```ruby
amount = Event.pluck('COALESCE(MAX(id), 1)').first
execute "ALTER TABLE events AUTO_INCREMENT = #{amount}"
```
Failure to reset the primary keys will result in newly created rows starting
with an ID value of 1. Depending on the existing data this can then lead to
duplicate key constraints from popping up, preventing users from creating new
data.
......@@ -299,9 +299,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-5-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-0-stable gitlab
**Note:** You can change `9-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
**Note:** You can change `10-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
......
......@@ -19,7 +19,7 @@ should be deployed, upgraded, and configured.
## GitLab-Omnibus Chart (Recommended)
> **Note**: This chart is in beta while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being added.
This chart is the best available way to operate GitLab on Kubernetes. It deploys and configures nearly all features of GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html#gitlab-container-registry), [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and a [load balancer](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). It is based on our [GitLab Omnibus Docker Images](https://docs.gitlab.com/omnibus/docker/README.html).
This chart is the best available way to operate GitLab on Kubernetes. It deploys and configures nearly all features of GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](../../user/project/container_registry.html#gitlab-container-registry), [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and a [load balancer](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). It is based on our [GitLab Omnibus Docker Images](https://docs.gitlab.com/omnibus/docker/README.html).
Once the [cloud native charts](#upcoming-cloud-native-helm-charts) are ready for production use, this chart will be deprecated. Due to the difficulty in supporting upgrades to the new architecture, migrating will require exporting data out of this instance and importing it into the new deployment.
......@@ -41,7 +41,7 @@ This is a large project and will be worked on over the span of multiple releases
### GitLab Runner Chart
If you already have a GitLab instance running, inside or outside of Kubernetes, and you'd like to leverage the Runner's [Kubernetes capabilities](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/executors/kubernetes.md), it can be deployed with the GitLab Runner chart.
If you already have a GitLab instance running, inside or outside of Kubernetes, and you'd like to leverage the Runner's [Kubernetes capabilities](https://docs.gitlab.com/runner/executors/kubernetes.html), it can be deployed with the GitLab Runner chart.
Learn more about [gitlab-runner chart.](gitlab_runner_chart.md)
......
......@@ -236,7 +236,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-0-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
......@@ -236,7 +236,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-0-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-1-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
......@@ -194,7 +194,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-1-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-2-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
......@@ -230,7 +230,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-2-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-3-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
......@@ -243,7 +243,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-3-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-4-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
......@@ -252,7 +252,7 @@ ActionMailer::Base.delivery_method = :smtp
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-4-stable/config/initializers/smtp_settings.rb.sample#L13
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-5-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
......
# From 9.5 to 10.0
Make sure you view this update guide from the tag (version) of GitLab you would
like to install. In most cases this should be the highest numbered production
tag (without rc in it). You can select the tag in the version dropdown at the
top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the
[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
guide links by version.
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 3. Update Ruby
NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be
sure to upgrade your interpreter if necessary.
You can check which version you are running with `ruby -v`.
Download and compile Ruby:
```bash
mkdir /tmp/ruby && cd /tmp/ruby
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
echo '1014ee699071aa2ddd501907d18cbe15399c997d ruby-2.3.3.tar.gz' | shasum -c - && tar xzf ruby-2.3.3.tar.gz
cd ruby-2.3.3
./configure --disable-install-rdoc
make
sudo make install
```
Install Bundler:
```bash
sudo gem install bundler --no-ri --no-rdoc
```
### 4. Update Node
GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
it has a minimum requirement of node v4.3.0.
You can check which version you are running with `node -v`. If you are running
a version older than `v4.3.0` you will need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the nodejs.org website.
<https://nodejs.org/en/download/>
Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
### 5. Update Go
NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go
1.5.x through 1.7.x. Be sure to upgrade your installation if necessary.
You can check which version you are running with `go version`.
Download and install Go:
```bash
# Remove former Go installation folder
sudo rm -rf /usr/local/go
curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
rm go1.8.3.linux-amd64.tar.gz
```
### 6. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout -- locale
```
For GitLab Community Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 10-0-stable
```
OR
For GitLab Enterprise Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 10-0-stable-ee
```
### 7. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
sudo -u git -H bin/compile
```
### 8. Update gitlab-workhorse
Install and compile gitlab-workhorse. GitLab-Workhorse uses
[GNU Make](https://www.gnu.org/software/make/).
If you are not using Linux you may have to run `gmake` instead of
`make` below.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
sudo -u git -H make
```
### 9. Update Gitaly
#### New Gitaly configuration options required
In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell'.
```shell
echo '
[gitaly-ruby]
dir = "/home/git/gitaly/ruby"
[gitlab-shell]
dir = "/home/git/gitlab-shell"
' | sudo -u git tee -a /home/git/gitaly/config.toml
```
#### Check Gitaly configuration
Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
configuration file may contain syntax errors. The block name
`[[storages]]`, which may occur more than once in your `config.toml`
file, should be `[[storage]]` instead.
```shell
sudo -u git -H sed -i.pre-10.0 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
```
#### Compile Gitaly
```shell
cd /home/git/gitaly
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
sudo -u git -H make
```
### 10. Update MySQL permissions
If you are using MySQL you need to grant the GitLab user the necessary
permissions on the database:
```bash
mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
```
If you use MySQL with replication, or just have MySQL configured with binary logging,
you will need to also run the following on all of your MySQL servers:
```bash
mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
```
You can make this setting permanent by adding it to your `my.cnf`:
```
log_bin_trust_function_creators=1
```
### 11. Update configuration files
#### New configuration options for `gitlab.yml`
There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
```sh
cd /home/git/gitlab
git diff origin/9-5-stable:config/gitlab.yml.example origin/10-0-stable:config/gitlab.yml.example
```
#### Nginx configuration
Ensure you're still up-to-date with the latest NGINX configuration changes:
```sh
cd /home/git/gitlab
# For HTTPS configurations
git diff origin/9-5-stable:lib/support/nginx/gitlab-ssl origin/10-0-stable:lib/support/nginx/gitlab-ssl
# For HTTP configurations
git diff origin/9-5-stable:lib/support/nginx/gitlab origin/10-0-stable:lib/support/nginx/gitlab
```
If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
configuration as GitLab application no longer handles setting it.
If you are using Apache instead of NGINX please see the updated [Apache templates].
Also note that because Apache does not support upstreams behind Unix sockets you
will need to let gitlab-workhorse listen on a TCP port. You can do this
via [/etc/default/gitlab].
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-0-stable/lib/support/init.d/gitlab.default.example#L38
#### SMTP configuration
If you're installing from source and use SMTP to deliver mail, you will need to add the following line
to config/initializers/smtp_settings.rb:
```ruby
ActionMailer::Base.delivery_method = :smtp
```
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-0-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
```sh
cd /home/git/gitlab
git diff origin/9-5-stable:lib/support/init.d/gitlab.default.example origin/10-0-stable:lib/support/init.d/gitlab.default.example
```
Ensure you're still up-to-date with the latest init script changes:
```bash
cd /home/git/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
For Ubuntu 16.04.1 LTS:
```bash
sudo systemctl daemon-reload
```
### 12. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without postgres')
sudo -u git -H bundle install --without postgres development test --deployment
# PostgreSQL installations (note: the line below states '--without mysql')
sudo -u git -H bundle install --without mysql development test --deployment
# Optional: clean up old gems
sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Compile GetText PO files
sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
# Update node dependencies and recompile assets
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
# Clean up cache
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
### 13. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 14. Check application status
Check if GitLab and its environment are configured correctly:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations, the upgrade is complete!
## Things went south? Revert to previous version (9.5)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 9.4 to 9.5](9.4-to-9.5.md), except for the
database migration (the backup is already migrated to the previous version).
### 2. Restore from the backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-0-stable/config/gitlab.yml.example
[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-0-stable/lib/support/init.d/gitlab.default.example
......@@ -74,7 +74,15 @@ cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 5. Update gitlab-shell to the corresponding version
### 5. Update gitaly to the corresponding version
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
```
### 6. Update gitlab-shell to the corresponding version
```bash
cd /home/git/gitlab-shell
......@@ -84,14 +92,14 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca
sudo -u git -H sh -c 'if [ -x bin/compile ]; then bin/compile; fi'
```
### 6. Start application
### 7. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 7. Check application status
### 8. Check application status
Check if GitLab and its environment are configured correctly:
......
......@@ -23,7 +23,7 @@ If you have just started using GitLab, it may take a few weeks for data to be
collected before this feature is available.
This feature is accessible only to a system admin, at
**Admin area > Monitoring > ConvDev Index**.
**Admin area > Overview > ConvDev Index**.
[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
[ping]: ../settings/usage_statistics.md#usage-ping
......@@ -7,7 +7,7 @@ GitLab has support for automatically detecting and monitoring HAProxy. This is p
| Name | Query |
| ---- | ----- |
| Throughput (req/sec) | sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) |
| Throughput (req/sec) | sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) by (code) |
| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_requests_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) |
## Configuring Prometheus to monitor for HAProxy metrics
......
......@@ -8,7 +8,7 @@ GitLab has support for automatically detecting and monitoring Kubernetes metrics
| Name | Query |
| ---- | ----- |
| Average Memory Usage (MB) | (sum(container_memory_usage_bytes{container_name!="POD",%{environment_filter}}) / count(container_memory_usage_bytes{container_name!="POD",%{environment_filter}})) /1024/1024 |
| Average CPU Utilization (%) | sum(rate(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}[2m])) / count(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}) * 100 |
| Average CPU Utilization (%) | sum(rate(container_cpu_usage_seconds_total{container_name!="POD",%{environment_filter}}[2m])) by (cpu) * 100 |
## Configuring Prometheus to monitor for Kubernetes node metrics
......
......@@ -7,7 +7,7 @@ GitLab has support for automatically detecting and monitoring NGINX. This is pro
| Name | Query |
| ---- | ----- |
| Throughput (req/sec) | sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) |
| Throughput (req/sec) | sum(rate(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (status_code) |
| Latency (ms) | avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) |
| HTTP Error Rate (HTTP Errors / sec) | rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m])) |
......
......@@ -7,19 +7,33 @@ GitLab has support for automatically detecting and monitoring the Kubernetes NGI
| Name | Query |
| ---- | ----- |
| Throughput (req/sec) | sum(rate(nginx_upstream_requests_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) |
| Throughput (req/sec) | sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code) |
| Latency (ms) | avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}) |
| HTTP Error Rate (HTTP Errors / sec) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) |
## Configuring Prometheus to monitor for NGINX ingress metrics
The easiest way to get started is to use at least version 0.9.0 of [NGINX ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). If you are using NGINX as your Kubernetes ingress, there is [direct support](https://github.com/kubernetes/ingress/pull/423) for enabling Prometheus monitoring in the 0.9.0 release.
If you have deployed with the [gitlab-omnibus](https://docs.gitlab.com/ee/install/kubernetes/gitlab_omnibus.md) Helm chart, and your application is running in the same cluster, no further action is required. The ingress metrics will be automatically enabled and annotated for Prometheus monitoring. Simply ensure Prometheus monitoring is [enabled for your project](../prometheus.md), which is on by default.
If you have deployed with the [gitlab-omnibus](https://docs.gitlab.com/ee/install/kubernetes/gitlab_omnibus.md) Helm chart, these metrics will be automatically enabled and annotated for Prometheus monitoring.
For other deployments, there is some configuration required depending on your installation:
* NGINX Ingress should be version 0.9.0 or above
* NGINX Ingress should be annotated for Prometheus monitoring
* Prometheus should be configured to monitor annotated pods
### Configuring NGINX Ingress for Prometheus monitoring
Version 0.9.0 and above of [NGINX ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx) have built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint will start running on port 10254.
With metric data now available, Prometheus needs to be configured to collect it. The easiest way to do this is to leverage Prometheus' [built-in Kubernetes service discovery](https://prometheus.io/docs/operating/configuration/#kubernetes_sd_config), which automatically detects a variety of Kubernetes components and makes them available for monitoring. NGINX ingress metrics are exposed per pod, a sample scrape configuration [is available](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml#L248). This configuration will detect pods and enable collection of metrics **only if** they have been specifically annotated for monitoring.
Depending on how NGINX ingress was deployed, typically a DaemonSet or Deployment, edit the corresponding YML spec. Two new annotations need to be added:
* `prometheus.io/port: "true"`
* `prometheus.io/port: "10254"`
Prometheus should now be collecting NGINX ingress metrics. To validate view the Prometheus Targets, available under `Status > Targets` on the Prometheus dashboard. New entries for NGINX should be listed in the kubernetes pod monitoring job, `kubernetes-pods`.
## Specifying the Environment label
In order to isolate and only display relevant metrics for a given environment
however, GitLab needs a method to detect which labels are associated. To do this, GitLab will search metrics with appropriate labels. In this case, the `upstream` label must be of the form `<Kubernetes Namespace>-<CI_ENVIRONMENT_SLUG>-*`.
In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do this, GitLab will search for metrics with appropriate labels. In this case, the `upstream` label must be of the form `<KUBE_NAMESPACE>-<CI_ENVIRONMENT_SLUG>-*`.
If you have used [Auto Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html) to deploy your app, this format will be used automatically and metrics will be detected with no action on your part.
......@@ -63,8 +63,6 @@ the same way as you do for projects.
![filter issues in a group](img/group_issues_filter.png)
The same process is valid for merge requests. Navigate to your project's **Merge Requests** tab.
The search and filter UI currently uses dropdowns. In a future release, the same
dynamic UI as above will be carried over here.
## Search history
......
@profile
Feature: Profile Emails
Background:
Given I sign in as a user
And I visit profile emails page
Scenario: I should see emails
Then I should see my emails
Scenario: Add new email
Given I submit new email "my@email.com"
Then I should see new email "my@email.com"
And I should see my emails
Scenario: Add duplicate email
Given I submit duplicate email @user.email
Then I should not have @user.email added
And I should see my emails
Scenario: Remove email
Given I submit new email "my@email.com"
Then I should see new email "my@email.com"
And I should see my emails
Then I click link "Remove" for "my@email.com"
Then I should not see email "my@email.com"
And I should see my emails
Feature: Project Group Links
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" is shared with group "Ops"
And project "Shop" is not shared with group "Market"
And I visit project group links page
Scenario: I should see list of groups
Then I should see project already shared with group "Ops"
Then I should see project is not shared with group "Market"
@javascript
Scenario: I share project with group
When I select group "Market" for share
Then I should see project is shared with group "Market"
@project_merge_requests
Feature: Project Merge Requests Acceptance
Background:
Given There is an open Merge Request
And I am signed in as a developer of the project
@javascript
Scenario: Accepting the Merge Request and removing the source branch
Given I am on the Merge Request detail page
When I check the "Remove source branch" option
And I click on Accept Merge Request
Then I should see merge request merged
And I should not see the Remove Source Branch button
@javascript
Scenario: Accepting the Merge Request when URL has an anchor
Given I am on the Merge Request detail with note anchor page
When I check the "Remove source branch" option
And I click on Accept Merge Request
Then I should see merge request merged
And I should not see the Remove Source Branch button
@javascript
Scenario: Accepting the Merge Request without removing the source branch
Given I am on the Merge Request detail page
When I click on Accept Merge Request
Then I should see merge request merged
And I should see the Remove Source Branch button
@project_merge_requests
Feature: Revert Merge Requests
Background:
Given There is an open Merge Request
And I am signed in as a developer of the project
And I am on the Merge Request detail page
And I click on Accept Merge Request
And I am on the Merge Request detail page
@javascript
Scenario: I revert a merge request
Given I click on the revert button
And I revert the changes directly
Then I should see the revert merge request notice
@javascript
Scenario: I revert a merge request that was previously reverted
Given I click on the revert button
And I revert the changes directly
And I am on the Merge Request detail page
And I click on the revert button
And I revert the changes directly
Then I should see a revert error
@javascript
Scenario: I revert a merge request in a new merge request
Given I click on the revert button
And I revert the changes in a new merge request
Then I should see the new merge request notice
Feature: Project Milestone
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has labels: "bug", "feature", "enhancement"
And project "Shop" has milestone "v2.2"
And milestone has issue "Bugfix1" with labels: "bug", "feature"
And milestone has issue "Bugfix2" with labels: "bug", "enhancement"
@javascript
Scenario: Listing labels from labels tab
Given I visit project "Shop" milestones page
And I click link "v2.2"
And I click link "Labels"
Then I should see the list of labels
And I should see the labels "bug", "enhancement" and "feature"
Feature: Project Team Management
Background:
Given I sign in as a user
And I own project "Shop"
And gitlab user "Mike"
And gitlab user "Dmitriy"
And "Dmitriy" is "Shop" developer
And I visit project "Shop" team page
Scenario: Cancel team member
Given I click cancel link for "Dmitriy"
Then I visit project "Shop" team page
And I should not see "Dmitriy" in team list
Scenario: Import team from another project
Given I own project "Website"
And "Mike" is "Website" reporter
When I visit project "Shop" team page
And I click link "Import team from another project"
And I submit "Website" project for import team
Then I should see "Mike" in team list as "Reporter"
Scenario: See all members of projects shared group
Given I share project with group "OpenSource"
And I visit project "Shop" team page
Then I should see "Opensource" group user listing
Feature: Project Wiki
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project wiki page
Scenario: Add new page
Given I create the Wiki Home page
Then I should see the newly created wiki page
Scenario: Add new page with errors
Given I create the Wiki Home page with no content
Then I should see a "Content can't be blank" error message
When I create the Wiki Home page
Then I should see the newly created wiki page
Scenario: Pressing Cancel while editing a brand new Wiki
Given I click on the Cancel button
Then I should be redirected back to the Edit Home Wiki page
Scenario: Edit existing page
Given I have an existing Wiki page
And I browse to that Wiki page
And I click on the Edit button
And I change the content
Then I should see the updated content
Scenario: Pressing Cancel while editing an existing Wiki page
Given I have an existing Wiki page
And I browse to that Wiki page
And I click on the Edit button
And I click on the Cancel button
Then I should be redirected back to that Wiki page
Scenario: View page history
Given I have an existing wiki page
And That page has two revisions
And I browse to that Wiki page
And I click the History button
Then I should see both revisions
Scenario: Destroy Wiki page
Given I have an existing wiki page
And I browse to that Wiki page
And I click on the Edit button
And I click on the "Delete this page" button
Then The page should be deleted
Scenario: View all pages
Given I have an existing wiki page
And I browse to that Wiki page
Then I should see the existing page in the pages list
Scenario: File exists in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on existing image link
Then I should see the image from wiki repo
Scenario: Image in wiki repo shown on the page
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
Then Image should be shown on the page
Scenario: File does not exist in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on image link
Then I should see the new wiki page form
@javascript
Scenario: New Wiki page that has a path
Given I create a New page with paths
Then I should see non-escaped link in the pages list
@javascript
Scenario: Edit Wiki page that has a path
Given I create a New page with paths
And I edit the Wiki page with a path
Then I should see a non-escaped path
And I should see the Editing page
And I change the content
Then I should see the updated content
@javascript
Scenario: View the page history of a Wiki page that has a path
Given I create a New page with paths
And I view the page history of a Wiki page that has a path
Then I should see a non-escaped path
And I should see the page history
@javascript
Scenario: View an old page version of a Wiki page
Given I create a New page with paths
And I edit the Wiki page with a path
Then I should see a non-escaped path
And I should see the Editing page
And I change the content
Then I click on Page History
And I should see the page history
And I should see a link with a version ID
class Spinach::Features::ProfileEmails < Spinach::FeatureSteps
include SharedAuthentication
step 'I visit profile emails page' do
visit profile_emails_path
end
step 'I should see my emails' do
expect(page).to have_content(@user.email)
@user.emails.each do |email|
expect(page).to have_content(email.email)
end
end
step 'I submit new email "my@email.com"' do
fill_in "email_email", with: "my@email.com"
click_button "Add"
end
step 'I should see new email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
expect(email).not_to be_nil
expect(page).to have_content("my@email.com")
end
step 'I should not see email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
expect(email).to be_nil
expect(page).not_to have_content("my@email.com")
end
step 'I click link "Remove" for "my@email.com"' do
# there should only be one remove button at this time
click_link "Remove"
# force these to reload as they have been cached
@user.emails.reload
end
step 'I submit duplicate email @user.email' do
fill_in "email_email", with: @user.email
click_button "Add"
end
step 'I should not have @user.email added' do
email = @user.emails.find_by(email: @user.email)
expect(email).to be_nil
end
end
class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
include LoginHelpers
include WaitForRequests
step 'I am on the Merge Request detail page' do
visit merge_request_path(@merge_request)
end
step 'I am on the Merge Request detail with note anchor page' do
visit merge_request_path(@merge_request, anchor: 'note_123')
end
step 'I uncheck the "Remove source branch" option' do
uncheck('Remove source branch')
end
step 'I check the "Remove source branch" option' do
check('Remove source branch')
end
step 'I click on Accept Merge Request' do
click_button('Merge')
end
step 'I should see the Remove Source Branch button' do
expect(page).to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
wait_for_requests
end
step 'I should not see the Remove Source Branch button' do
expect(page).not_to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
wait_for_requests
end
step 'There is an open Merge Request' do
@user = create(:user)
@project = create(:project, :public, :repository)
@project_member = create(:project_member, :developer, user: @user, project: @project)
@merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project)
end
step 'I am signed in as a developer of the project' do
sign_in(@user)
end
step 'I should see merge request merged' do
expect(page).to have_content('The changes were merged into')
end
end
class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps
include LoginHelpers
include WaitForRequests
step 'I click on the revert button' do
find("a[href='#modal-revert-commit']").click
end
step 'I revert the changes directly' do
page.within('#modal-revert-commit') do
uncheck 'create_merge_request'
click_button 'Revert'
end
end
step 'I should see the revert merge request notice' do
page.should have_content('The merge request has been successfully reverted.')
wait_for_requests
end
step 'I should not see the revert button' do
expect(page).not_to have_selector(:xpath, "a[href='#modal-revert-commit']")
end
step 'I am on the Merge Request detail page' do
visit merge_request_path(@merge_request)
end
step 'I click on Accept Merge Request' do
click_button('Merge')
end
step 'I am signed in as a developer of the project' do
@user = create(:user) { |u| @project.add_developer(u) }
sign_in(@user)
end
step 'There is an open Merge Request' do
@merge_request = create(:merge_request, :with_diffs, :simple)
@project = @merge_request.source_project
end
step 'I should see a revert error' do
page.should have_content('Sorry, we cannot revert this merge request automatically.')
end
step 'I revert the changes in a new merge request' do
page.within('#modal-revert-commit') do
click_button 'Revert'
end
end
step 'I should see the new merge request notice' do
page.should have_content('The merge request has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
end
end
class Spinach::Features::ProjectGroupLinks < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
include Select2Helper
step 'I should see project already shared with group "Ops"' do
page.within '.project-members-groups' do
expect(page).to have_content "Ops"
end
end
step 'I should see project is not shared with group "Market"' do
page.within '.project-members-groups' do
expect(page).not_to have_content "Market"
end
end
step 'I select group "Market" for share' do
click_link 'Share with group'
group = Group.find_by(path: 'market')
select2(group.id, from: "#link_group_id")
select "Master", from: 'link_group_access'
click_button "Share"
end
step 'I should see project is shared with group "Market"' do
page.within '.project-members-groups' do
expect(page).to have_content "Market"
end
end
step 'project "Shop" is shared with group "Ops"' do
group = create(:group, name: 'Ops')
share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
share_link.group_id = group.id
share_link.save!
end
step 'project "Shop" is not shared with group "Market"' do
create(:group, name: 'Market', path: 'market')
end
step 'I visit project group links page' do
visit project_group_links_path(project)
end
def project
@project ||= Project.find_by_name "Shop"
end
end
class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
include WaitForRequests
step 'milestone has issue "Bugfix1" with labels: "bug", "feature"' do
project = Project.find_by(name: "Shop")
milestone = project.milestones.find_by(title: 'v2.2')
issue = create(:issue, title: "Bugfix1", project: project, milestone: milestone)
issue.labels << project.labels.find_by(title: 'bug')
issue.labels << project.labels.find_by(title: 'feature')
end
step 'milestone has issue "Bugfix2" with labels: "bug", "enhancement"' do
project = Project.find_by(name: "Shop")
milestone = project.milestones.find_by(title: 'v2.2')
issue = create(:issue, title: "Bugfix2", project: project, milestone: milestone)
issue.labels << project.labels.find_by(title: 'bug')
issue.labels << project.labels.find_by(title: 'enhancement')
end
step 'project "Shop" has milestone "v2.2"' do
project = Project.find_by(name: "Shop")
milestone = create(:milestone,
title: "v2.2",
project: project,
description: "# Description header"
)
3.times { create(:issue, project: project, milestone: milestone) }
end
step 'I should see the list of labels' do
expect(page).to have_selector('ul.manage-labels-list')
end
step 'I should see the labels "bug", "enhancement" and "feature"' do
wait_for_requests
page.within('#tab-issues') do
expect(page).to have_content 'bug'
expect(page).to have_content 'enhancement'
expect(page).to have_content 'feature'
end
end
step 'I should see the "bug" label listed only once' do
page.within('#tab-labels') do
expect(page).to have_content('bug', count: 1)
end
end
step 'I click link "v2.2"' do
click_link "v2.2"
end
step 'I click link "Labels"' do
page.within('.nav-sidebar') do
page.find(:xpath, "//a[@href='#tab-labels']").click
end
end
end
class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
include Select2Helper
step 'I should not see "Dmitriy" in team list' do
user = User.find_by(name: "Dmitriy")
expect(page).not_to have_content(user.name)
expect(page).not_to have_content(user.username)
end
step 'I should see "Mike" in team list as "Reporter"' do
user = User.find_by(name: 'Mike')
project_member = project.project_members.find_by(user_id: user.id)
page.within "#project_member_#{project_member.id}" do
expect(page).to have_content('Mike')
expect(page).to have_content('Reporter')
end
end
step 'gitlab user "Mike"' do
create(:user, name: "Mike")
end
step 'gitlab user "Dmitriy"' do
create(:user, name: "Dmitriy")
end
step '"Dmitriy" is "Shop" developer' do
user = User.find_by(name: "Dmitriy")
project = Project.find_by(name: "Shop")
project.team << [user, :developer]
end
step 'I own project "Website"' do
@project = create(:project, name: "Website", namespace: @user.namespace)
@project.team << [@user, :master]
end
step '"Mike" is "Website" reporter' do
user = User.find_by(name: "Mike")
project = Project.find_by(name: "Website")
project.team << [user, :reporter]
end
step 'I click link "Import team from another project"' do
page.within '.users-project-form' do
click_link "Import"
end
end
When 'I submit "Website" project for import team' do
project = Project.find_by(name: "Website")
select project.name_with_namespace, from: 'source_project_id'
click_button 'Import'
end
step 'I click cancel link for "Dmitriy"' do
project = Project.find_by(name: "Shop")
user = User.find_by(name: 'Dmitriy')
project_member = project.project_members.find_by(user_id: user.id)
page.within "#project_member_#{project_member.id}" do
click_link('Remove user from project')
end
end
step 'I share project with group "OpenSource"' do
project = Project.find_by(name: 'Shop')
os_group = create(:group, name: 'OpenSource')
create(:project, group: os_group)
@os_user1 = create(:user)
@os_user2 = create(:user)
os_group.add_owner(@os_user1)
os_group.add_user(@os_user2, Gitlab::Access::DEVELOPER)
share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
share_link.group_id = os_group.id
share_link.save!
end
step 'I should see "Opensource" group user listing' do
page.within '.project-members-groups' do
expect(page).to have_content('OpenSource')
expect(first('.group_member')).to have_content('Master')
end
end
end
class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
step 'I click on the Cancel button' do
page.within(:css, ".wiki-form .form-actions") do
click_on "Cancel"
end
end
step 'I should be redirected back to the Edit Home Wiki page' do
expect(current_path).to eq project_wiki_path(project, :home)
end
step 'I create the Wiki Home page' do
fill_in "wiki_content", with: '[link test](test)'
page.within '.wiki-form' do
click_on "Create page"
end
end
step 'I create the Wiki Home page with no content' do
fill_in "wiki_content", with: ''
page.within '.wiki-form' do
click_on "Create page"
end
end
step 'I should see the newly created wiki page' do
expect(page).to have_content "Home"
expect(page).to have_content "link test"
click_link "link test"
expect(page).to have_content "Create page"
end
step 'I have an existing Wiki page' do
wiki.create_page("existing", "content", :markdown, "first commit")
@page = wiki.find_page("existing")
end
step 'I browse to that Wiki page' do
visit project_wiki_path(project, @page)
end
step 'I click on the Edit button' do
click_on "Edit"
end
step 'I change the content' do
fill_in "Content", with: 'Updated Wiki Content'
click_on "Save changes"
end
step 'I should see the updated content' do
expect(page).to have_content "Updated Wiki Content"
end
step 'I should be redirected back to that Wiki page' do
expect(current_path).to eq project_wiki_path(project, @page)
end
step 'That page has two revisions' do
@page.update(content: "new content", message: "second commit")
end
step 'I click the History button' do
click_on 'Page history'
end
step 'I should see both revisions' do
expect(page).to have_content current_user.name
expect(page).to have_content "first commit"
expect(page).to have_content "second commit"
end
step 'I click on the "Delete this page" button' do
click_on "Delete"
end
step 'The page should be deleted' do
expect(page).to have_content "Page was successfully deleted"
end
step 'I should see the existing page in the pages list' do
expect(page).to have_content current_user.name
expect(find('.wiki-pages')).to have_content @page.title.capitalize
end
step 'I have an existing Wiki page with images linked on page' do
wiki.create_page("pictures", "Look at this [image](image.jpg)\n\n ![alt text](image.jpg)", :markdown, "first commit")
@wiki_page = wiki.find_page("pictures")
end
step 'I browse to wiki page with images' do
visit project_wiki_path(project, @wiki_page)
end
step 'I click on existing image link' do
file = Gollum::File.new(wiki.wiki)
Gollum::Wiki.any_instance.stub(:file).with("image.jpg", "master", true).and_return(file)
Gollum::File.any_instance.stub(:mime_type).and_return("image/jpeg")
expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/image.jpg")
click_on "image"
end
step 'I should see the image from wiki repo' do
expect(current_path).to match('wikis/image.jpg')
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
Gollum::Wiki.any_instance.unstub(:file)
Gollum::File.any_instance.unstub(:mime_type)
end
step 'Image should be shown on the page' do
expect(page).to have_xpath("//img[@data-src=\"image.jpg\"]")
end
step 'I click on image link' do
expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/image.jpg")
click_on "image"
end
step 'I should see the new wiki page form' do
expect(current_path).to match('wikis/image.jpg')
expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end
step 'I create a New page with paths' do
click_on 'New page'
fill_in 'Page slug', with: 'one/two/three-test'
page.within '#modal-new-wiki' do
click_on 'Create page'
end
fill_in "wiki_content", with: 'wiki content'
page.within '.wiki-form' do
click_on "Create page"
end
expect(current_path).to include 'one/two/three-test'
end
step 'I should see non-escaped link in the pages list' do
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
step 'I edit the Wiki page with a path' do
expect(find('.wiki-pages')).to have_content('Three')
click_on 'Three'
expect(find('.nav-text')).to have_content('Three')
click_on 'Edit'
end
step 'I should see a non-escaped path' do
expect(current_path).to include 'one/two/three-test'
end
step 'I should see the Editing page' do
expect(page).to have_content('Edit Page')
end
step 'I view the page history of a Wiki page that has a path' do
click_on 'Three'
click_on 'Page history'
end
step 'I click on Page History' do
click_on 'Page history'
end
step 'I should see the page history' do
page.within(:css, ".nav-text") do
expect(page).to have_content('History')
end
end
step 'I search for Wiki content' do
fill_in "Search", with: "wiki_content"
click_button "Search"
end
step 'I should see a link with a version ID' do
find('a[href*="?version_id"]')
end
step 'I should see a "Content can\'t be blank" error message' do
expect(page).to have_content('The form contains the following error:')
expect(page).to have_content('Content can\'t be blank')
end
def wiki
@project_wiki = ProjectWiki.new(project, current_user)
end
end
......@@ -121,10 +121,10 @@ module Gitlab
]
end
def send_artifacts_entry(build, path)
def send_artifacts_entry(build, entry)
params = {
'Archive' => build.artifacts_file.path,
'Entry' => Base64.encode64(path.to_s)
'Entry' => Base64.encode64(entry.to_s)
}
[
......
......@@ -13,7 +13,7 @@ describe 'Issue Boards', js: true do
project.team << [user, :master]
project.team << [user2, :master]
allow_any_instance_of(ApplicationHelper).to receive(:collapsed_sidebar?).and_return(true)
page.driver.set_cookie('sidebar_collapsed', 'true')
sign_in(user)
end
......
......@@ -83,12 +83,14 @@ feature 'Dashboard Projects' do
end
end
context 'last push widget' do
context 'last push widget', :use_clean_rails_memory_store_caching do
before do
event = create(:push_event, project: project, author: user)
create(:push_event_payload, event: event, ref: 'feature', action: :created)
Users::LastPushEventService.new(user).cache_last_push_event(event)
visit dashboard_projects_path
end
......
......@@ -28,7 +28,7 @@ describe 'Visual tokens', js: true do
sign_in(user)
create(:issue, project: project)
allow_any_instance_of(ApplicationHelper).to receive(:collapsed_sidebar?).and_return(true)
page.driver.set_cookie('sidebar_collapsed', 'true')
visit project_issues_path(project)
end
......
......@@ -22,7 +22,7 @@ feature 'Diff note avatars', js: true do
project.team << [user, :master]
sign_in user
allow_any_instance_of(ApplicationHelper).to receive(:collapsed_sidebar?).and_return(true)
page.driver.set_cookie('sidebar_collapsed', 'true')
end
context 'discussion tab' do
......
......@@ -6,7 +6,7 @@ feature 'Merge requests > User posts diff notes', :js do
let(:project) { merge_request.source_project }
before do
allow_any_instance_of(ApplicationHelper).to receive(:collapsed_sidebar?).and_return(true)
page.driver.set_cookie('sidebar_collapsed', 'true')
project.add_developer(user)
sign_in(user)
......
require 'spec_helper'
describe 'User manages emails' do
let(:user) { create(:user) }
before do
sign_in(user)
visit(profile_emails_path)
end
it "shows user's emails" do
expect(page).to have_content(user.email)
user.emails.each do |email|
expect(page).to have_content(email.email)
end
end
it 'adds an email' do
fill_in('email_email', with: 'my@email.com')
click_button('Add')
email = user.emails.find_by(email: 'my@email.com')
expect(email).not_to be_nil
expect(page).to have_content('my@email.com')
expect(page).to have_content(user.email)
user.emails.each do |email|
expect(page).to have_content(email.email)
end
end
it 'does not add a duplicate email' do
fill_in('email_email', with: user.email)
click_button('Add')
email = user.emails.find_by(email: user.email)
expect(email).to be_nil
expect(page).to have_content(user.email)
user.emails.each do |email|
expect(page).to have_content(email.email)
end
end
it 'removes an email' do
fill_in('email_email', with: 'my@email.com')
click_button('Add')
email = user.emails.find_by(email: 'my@email.com')
expect(email).not_to be_nil
expect(page).to have_content('my@email.com')
expect(page).to have_content(user.email)
user.emails.each do |email|
expect(page).to have_content(email.email)
end
# There should be only one remove button at this time
click_link('Remove')
# Force these to reload as they have been cached
user.emails.reload
email = user.emails.find_by(email: 'my@email.com')
expect(email).to be_nil
expect(page).not_to have_content('my@email.com')
expect(page).to have_content(user.email)
user.emails.each do |email|
expect(page).to have_content(email.email)
end
end
end
require 'spec_helper'
describe 'User accepts a merge request', :js do
let(:merge_request) { create(:merge_request, :with_diffs, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
before do
project.add_developer(user)
sign_in(user)
end
context 'with removing the source branch' do
before do
visit(merge_request_path(merge_request))
end
it 'accepts a merge request' do
check('Remove source branch')
click_button('Merge')
expect(page).to have_content('The changes were merged into')
expect(page).not_to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run.
wait_for_requests
end
end
context 'without removing the source branch' do
before do
visit(merge_request_path(merge_request))
end
it 'accepts a merge request' do
click_button('Merge')
expect(page).to have_content('The changes were merged into')
expect(page).to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
wait_for_requests
end
end
context 'when a URL has an anchor' do
before do
visit(merge_request_path(merge_request, anchor: 'note_123'))
end
it 'accepts a merge request' do
check('Remove source branch')
click_button('Merge')
expect(page).to have_content('The changes were merged into')
expect(page).not_to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
wait_for_requests
end
end
end
require 'spec_helper'
describe 'User reverts a merge request', :js do
let(:merge_request) { create(:merge_request, :with_diffs, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
before do
project.add_developer(user)
sign_in(user)
visit(merge_request_path(merge_request))
click_button('Merge')
visit(merge_request_path(merge_request))
end
it 'reverts a merge request' do
find("a[href='#modal-revert-commit']").click
page.within('#modal-revert-commit') do
uncheck('create_merge_request')
click_button('Revert')
end
expect(page).to have_content('The merge request has been successfully reverted.')
wait_for_requests
end
it 'does not revert a merge request that was previously reverted' do
find("a[href='#modal-revert-commit']").click
page.within('#modal-revert-commit') do
uncheck('create_merge_request')
click_button('Revert')
end
find("a[href='#modal-revert-commit']").click
page.within('#modal-revert-commit') do
uncheck('create_merge_request')
click_button('Revert')
end
expect(page).to have_content('Sorry, we cannot revert this merge request automatically.')
end
it 'reverts a merge request in a new merge request' do
find("a[href='#modal-revert-commit']").click
page.within('#modal-revert-commit') do
click_button('Revert')
end
expect(page).to have_content('The merge request has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
end
end
require 'spec_helper'
describe 'User interacts with labels' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:milestone) { create(:milestone, project: project, title: 'v2.2', description: '# Description header') }
let(:issue1) { create(:issue, project: project, title: 'Bugfix1', milestone: milestone) }
let(:issue2) { create(:issue, project: project, title: 'Bugfix2', milestone: milestone) }
let(:label_bug) { create(:label, project: project, title: 'bug') }
let(:label_feature) { create(:label, project: project, title: 'feature') }
let(:label_enhancement) { create(:label, project: project, title: 'enhancement') }
before do
project.add_master(user)
sign_in(user)
issue1.labels << [label_bug, label_feature]
issue2.labels << [label_bug, label_enhancement]
visit(project_milestones_path(project))
end
it 'shows the list of labels', :js do
click_link('v2.2')
page.within('.nav-sidebar') do
page.find(:xpath, "//a[@href='#tab-labels']").click
end
expect(page).to have_selector('ul.manage-labels-list')
wait_for_requests
page.within('#tab-labels') do
expect(page).to have_content(label_bug.title)
expect(page).to have_content(label_enhancement.title)
expect(page).to have_content(label_feature.title)
end
end
end
require 'spec_helper'
describe 'User manages group links' do
include Select2Helper
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:group_ops) { create(:group, name: 'Ops') }
let(:group_market) { create(:group, name: 'Market', path: 'market') }
before do
project.add_master(user)
sign_in(user)
share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
share_link.group_id = group_ops.id
share_link.save!
visit(project_group_links_path(project))
end
it 'shows a list of groups' do
page.within('.project-members-groups') do
expect(page).to have_content('Ops')
expect(page).not_to have_content('Market')
end
end
it 'shares a project with a group', :js do
click_link('Share with group')
select2(group_market.id, from: '#link_group_id')
select('Master', from: 'link_group_access')
click_button('Share')
page.within('.project-members-groups') do
expect(page).to have_content('Market')
end
end
end
require 'spec_helper'
describe 'User manages project members' do
let(:group) { create(:group, name: 'OpenSource') }
let(:project) { create(:project) }
let(:project2) { create(:project) }
let(:user) { create(:user) }
let(:user_dmitriy) { create(:user, name: 'Dmitriy') }
let(:user_mike) { create(:user, name: 'Mike') }
before do
project.add_master(user)
project.add_developer(user_dmitriy)
sign_in(user)
end
it 'cancels a team member' do
visit(project_project_members_path(project))
project_member = project.project_members.find_by(user_id: user_dmitriy.id)
page.within("#project_member_#{project_member.id}") do
click_link('Remove user from project')
end
visit(project_project_members_path(project))
expect(page).not_to have_content(user_dmitriy.name)
expect(page).not_to have_content(user_dmitriy.username)
end
it 'imports a team from another project' do
project2.add_master(user)
project2.add_reporter(user_mike)
visit(project_project_members_path(project))
page.within('.users-project-form') do
click_link('Import')
end
select(project2.name_with_namespace, from: 'source_project_id')
click_button('Import')
project_member = project.project_members.find_by(user_id: user_mike.id)
page.within("#project_member_#{project_member.id}") do
expect(page).to have_content('Mike')
expect(page).to have_content('Reporter')
end
end
it 'shows all members of project shared group' do
group.add_owner(user)
group.add_developer(user_dmitriy)
share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
share_link.group_id = group.id
share_link.save!
visit(project_project_members_path(project))
page.within('.project-members-groups') do
expect(page).to have_content('OpenSource')
expect(first('.group_member')).to have_content('Master')
end
end
end
require 'spec_helper'
feature 'Projects > Wiki > User creates wiki page', :js do
describe 'User creates wiki page' do
let(:user) { create(:user) }
background do
project.team << [user, :master]
before do
project.add_master(user)
sign_in(user)
visit project_path(project)
visit(project_wikis_path(project))
end
context 'in the user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do
context 'in a user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
it 'shows validation error message' do
page.within('.wiki-form') do
fill_in(:wiki_content, with: '')
click_on('Create page')
end
expect(page).to have_content('The form contains the following error:')
expect(page).to have_content("Content can't be blank")
page.within('.wiki-form') do
fill_in(:wiki_content, with: '[link test](test)')
click_on('Create page')
end
expect(page).to have_content('Home')
expect(page).to have_content('link test')
click_link('link test')
expect(page).to have_content('Create Page')
end
it 'shows non-escaped link in the pages list', :js do
click_link('New page')
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
expect(current_path).to include('one/two/three-test')
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
scenario 'commit message field has value "Create home"' do
it 'has "Create home" as a commit message' do
expect(page).to have_field('wiki[message]', with: 'Create home')
end
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
page.within '.wiki-form' do
click_button 'Create page'
it 'creates a page from the home page' do
fill_in(:wiki_content, with: 'My awesome wiki!')
page.within('.wiki-form') do
click_button('Create page')
end
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
scenario 'creates ASCII wiki with LaTeX blocks' do
it 'creates ASCII wiki with LaTeX blocks', :js do
stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true)
ascii_content = <<~MD
......@@ -54,10 +91,10 @@ feature 'Projects > Wiki > User creates wiki page', :js do
MD
find('#wiki_format option[value=asciidoc]').select_option
fill_in :wiki_content, with: ascii_content
fill_in(:wiki_content, with: ascii_content)
page.within '.wiki-form' do
click_button 'Create page'
page.within('.wiki-form') do
click_button('Create page')
end
page.within '.wiki' do
......@@ -67,27 +104,49 @@ feature 'Projects > Wiki > User creates wiki page', :js do
end
end
context 'when wiki is not empty' do
before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
context 'in a group namespace', :js do
let(:project) { create(:project, namespace: create(:group, :public)) }
it 'has "Create home" as a commit message' do
expect(page).to have_field('wiki[message]', with: 'Create home')
end
it 'creates a page from from the home page' do
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
end
context 'when wiki is not empty', :js do
before do
create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: 'home', content: 'Home page' })
end
context 'in a user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'via the "new wiki page" page' do
scenario 'when the wiki page has a single word name' do
click_link 'New page'
it 'creates a page with a single word' do
click_link('New page')
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'foo'
click_button 'Create page'
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'foo')
click_button('Create page')
end
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create foo')
page.within '.wiki-form' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end
expect(page).to have_content('Foo')
......@@ -95,20 +154,20 @@ feature 'Projects > Wiki > User creates wiki page', :js do
expect(page).to have_content('My awesome wiki!')
end
scenario 'when the wiki page has spaces in the name' do
click_link 'New page'
it 'creates a page with spaces in the name' do
click_link('New page')
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'Spaces in the name'
click_button 'Create page'
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'Spaces in the name')
click_button('Create page')
end
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create spaces in the name')
page.within '.wiki-form' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end
expect(page).to have_content('Spaces in the name')
......@@ -116,20 +175,20 @@ feature 'Projects > Wiki > User creates wiki page', :js do
expect(page).to have_content('My awesome wiki!')
end
scenario 'when the wiki page has hyphens in the name' do
click_link 'New page'
it 'creates a page with hyphens in the name' do
click_link('New page')
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'hyphens-in-the-name'
click_button 'Create page'
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'hyphens-in-the-name')
click_button('Create page')
end
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name')
page.within '.wiki-form' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end
expect(page).to have_content('Hyphens in the name')
......@@ -138,73 +197,47 @@ feature 'Projects > Wiki > User creates wiki page', :js do
end
end
scenario 'content has autocomplete' do
click_link 'New page'
it 'shows the autocompletion dropdown' do
click_link('New page')
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'test-autocomplete'
click_button 'Create page'
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'test-autocomplete')
click_button('Create page')
end
page.within '.wiki-form' do
page.within('.wiki-form') do
find('#wiki_content').native.send_keys('')
fill_in :wiki_content, with: '@'
fill_in(:wiki_content, with: '@')
end
expect(page).to have_selector('.atwho-view')
end
end
end
context 'in a group namespace' do
let(:project) { create(:project, namespace: create(:group, :public)) }
context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
end
scenario 'commit message field has value "Create home"' do
expect(page).to have_field('wiki[message]', with: 'Create home')
end
context 'in a group namespace' do
let(:project) { create(:project, namespace: create(:group, :public)) }
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
page.within '.wiki-form' do
click_button 'Create page'
end
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
context 'when wiki is not empty' do
before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
end
context 'via the "new wiki page" page' do
it 'creates a page' do
click_link('New page')
scenario 'via the "new wiki page" page' do
click_link 'New page'
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'foo')
click_button('Create page')
end
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'foo'
click_button 'Create page'
end
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create foo')
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create foo')
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end
page.within '.wiki-form' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
expect(page).to have_content('Foo')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
expect(page).to have_content('Foo')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
end
......
require 'spec_helper'
feature 'User deletes wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
before do
sign_in(user)
visit(project_wiki_path(project, wiki_page))
end
it 'deletes a page' do
click_on('Edit')
click_on('Delete')
expect(page).to have_content('Page was successfully deleted')
end
end
require 'spec_helper'
feature 'Projects > Wiki > User updates wiki page' do
describe 'User updates wiki page' do
let(:user) { create(:user) }
let!(:wiki_page) { WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute }
background do
project.team << [user, :master]
before do
project.add_master(user)
sign_in(user)
end
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
end
context 'in a user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
it 'redirects back to the home edit page' do
page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel')
end
expect(current_path).to eq project_wiki_path(project, :home)
end
it 'updates a page that has a path', :js do
click_on('New page')
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within '.wiki-form' do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
visit project_wikis_path(project)
expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three')
click_on('Three')
expect(find('.nav-text')).to have_content('Three')
click_on('Edit')
expect(current_path).to include('one/two/three-test')
expect(page).to have_content('Edit Page')
fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes')
expect(page).to have_content('Updated Wiki Content')
end
end
end
context 'in the user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is not empty' do
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) }
context 'the home page' do
scenario 'success when the wiki content is not empty' do
click_link 'Edit'
before do
visit(project_wikis_path(project))
end
context 'in a user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
it 'updates a page' do
click_link('Edit')
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes')
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
scenario 'failure when the wiki content is empty' do
click_link 'Edit'
it 'shows a validation error message' do
click_link('Edit')
fill_in :wiki_content, with: ''
click_button 'Save changes'
fill_in(:wiki_content, with: '')
click_button('Save changes')
expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page')
expect(page).to have_content('The form contains the following error:')
expect(page).to have_content('Content can\'t be blank')
expect(find('textarea#wiki_content').value).to eq ''
expect(page).to have_content("Content can't be blank")
expect(find('textarea#wiki_content').value).to eq('')
end
scenario 'content has autocomplete', :js do
click_link 'Edit'
it 'shows the autocompletion dropdown', :js do
click_link('Edit')
find('#wiki_content').native.send_keys('')
fill_in :wiki_content, with: '@'
fill_in(:wiki_content, with: '@')
expect(page).to have_selector('.atwho-view')
end
end
scenario 'page has been updated since the user opened the edit page' do
click_link 'Edit'
it 'shows the error message' do
click_link('Edit')
wiki_page.update(content: 'Update')
wiki_page.update(content: 'Update')
click_button('Save changes')
expect(page).to have_content('Someone edited the page the same time you did.')
end
it 'updates a page' do
click_on('Edit')
fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes')
expect(page).to have_content('Updated Wiki Content')
end
click_button 'Save changes'
it 'cancels edititng of a page' do
click_on('Edit')
expect(page).to have_content 'Someone edited the page the same time you did.'
page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel')
end
expect(current_path).to eq(project_wiki_path(project, wiki_page))
end
end
end
context 'in a group namespace' do
let(:project) { create(:project, namespace: create(:group, :public)) }
context 'in a group namespace' do
let(:project) { create(:project, namespace: create(:group, :public)) }
scenario 'the home page' do
click_link 'Edit'
it 'updates a page' do
click_link('Edit')
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes')
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
end
end
require 'spec_helper'
feature 'Projects > Wiki > User views the wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:old_page_version_id) { wiki_page.versions.last.id }
let(:wiki_page) do
WikiPages::CreateService.new(
project,
user,
title: 'home',
content: '[some link](other-page)'
).execute
end
background do
project.team << [user, :master]
sign_in(user)
WikiPages::UpdateService.new(
project,
user,
message: 'updated home',
content: 'updated [some link](other-page)',
format: :markdown
).execute(wiki_page)
end
scenario 'Visit Wiki Page Current Commit' do
visit project_wiki_path(project, wiki_page)
expect(page).to have_selector('a.btn', text: 'Edit')
end
scenario 'Visit Wiki Page Historical Commit' do
visit project_wiki_path(project, wiki_page, version_id: old_page_version_id)
expect(page).not_to have_selector('a.btn', text: 'Edit')
end
end
require 'spec_helper'
describe 'User views a wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:wiki_page) do
create(:wiki_page,
wiki: project.wiki,
attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' })
end
before do
project.add_master(user)
sign_in(user)
end
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
click_on('New page')
page.within('#modal-new-wiki') do
fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
end
it 'shows the history of a page that has a path', :js do
expect(current_path).to include('one/two/three-test')
click_on('Three')
click_on('Page history')
expect(current_path).to include('one/two/three-test')
page.within(:css, '.nav-text') do
expect(page).to have_content('History')
end
end
it 'shows an old version of a page', :js do
expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three')
click_on('Three')
expect(find('.nav-text')).to have_content('Three')
click_on('Edit')
expect(current_path).to include('one/two/three-test')
expect(page).to have_content('Edit Page')
fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes')
click_on('Page history')
page.within(:css, '.nav-text') do
expect(page).to have_content('History')
end
find('a[href*="?version_id"]')
end
end
context 'when a page does not have history' do
before do
visit(project_wiki_path(project, wiki_page))
end
it 'shows all the pages' do
expect(page).to have_content(user.name)
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
end
it 'shows a file stored in a page' do
file = Gollum::File.new(project.wiki)
allow_any_instance_of(Gollum::Wiki).to receive(:file).with('image.jpg', 'master', true).and_return(file)
allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return('image/jpeg')
expect(page).to have_xpath('//img[@data-src="image.jpg"]')
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
click_on('image')
expect(current_path).to match('wikis/image.jpg')
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end
it 'shows the creation page if file does not exist' do
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
click_on('image')
expect(current_path).to match('wikis/image.jpg')
expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end
end
context 'when a page has history' do
before do
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
end
it 'shows the page history' do
visit(project_wiki_path(project, wiki_page))
expect(page).to have_selector('a.btn', text: 'Edit')
click_on('Page history')
expect(page).to have_content(user.name)
expect(page).to have_content("#{user.username} created page: home")
expect(page).to have_content('updated home')
end
it 'does not show the "Edit" button' do
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
expect(page).not_to have_selector('a.btn', text: 'Edit')
end
end
it 'opens a default wiki page', :js do
visit(project_path(project))
find('.shortcuts-wiki').trigger('click')
expect(page).to have_content('Home · Create Page')
end
end
......@@ -313,23 +313,10 @@ describe ProjectsHelper do
it 'returns recent push on the current project' do
event = double(:event)
expect(user).to receive(:recent_push).with([project.id]).and_return(event)
expect(user).to receive(:recent_push).with(project).and_return(event)
expect(helper.last_push_event).to eq(event)
end
context 'when current user has a fork of the current project' do
let(:fork) { double(:fork, id: 2) }
it 'returns recent push considering fork events' do
expect(user).to receive(:fork_of).with(project).and_return(fork)
event_on_fork = double(:event)
expect(user).to receive(:recent_push).with([project.id, fork.id]).and_return(event_on_fork)
expect(helper.last_push_event).to eq(event_on_fork)
end
end
end
describe "#project_feature_access_select" do
......
......@@ -42,7 +42,6 @@ describe('Issuable output', () => {
initialDescriptionText: '',
markdownPreviewPath: '/',
markdownDocsPath: '/',
isConfidential: false,
projectNamespace: '/',
projectPath: '/',
},
......@@ -157,30 +156,6 @@ describe('Issuable output', () => {
});
});
it('reloads the page if the confidential status has changed', (done) => {
spyOn(gl.utils, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
json() {
return {
confidential: true,
web_url: location.pathname,
};
},
});
}));
vm.updateIssuable();
setTimeout(() => {
expect(
gl.utils.visitUrl,
).toHaveBeenCalledWith(location.pathname);
done();
});
});
it('correctly updates issuable data', (done) => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve();
......
......@@ -11,6 +11,94 @@ describe PushEvent do
event
end
describe '.created_or_pushed' do
let(:event1) { create(:push_event) }
let(:event2) { create(:push_event) }
let(:event3) { create(:push_event) }
before do
create(:push_event_payload, event: event1, action: :pushed)
create(:push_event_payload, event: event2, action: :created)
create(:push_event_payload, event: event3, action: :removed)
end
let(:relation) { described_class.created_or_pushed }
it 'includes events for pushing to existing refs' do
expect(relation).to include(event1)
end
it 'includes events for creating new refs' do
expect(relation).to include(event2)
end
it 'does not include events for removing refs' do
expect(relation).not_to include(event3)
end
end
describe '.branch_events' do
let(:event1) { create(:push_event) }
let(:event2) { create(:push_event) }
before do
create(:push_event_payload, event: event1, ref_type: :branch)
create(:push_event_payload, event: event2, ref_type: :tag)
end
let(:relation) { described_class.branch_events }
it 'includes events for branches' do
expect(relation).to include(event1)
end
it 'does not include events for tags' do
expect(relation).not_to include(event2)
end
end
describe '.without_existing_merge_requests' do
let(:project) { create(:project, :repository) }
let(:event1) { create(:push_event, project: project) }
let(:event2) { create(:push_event, project: project) }
let(:event3) { create(:push_event, project: project) }
let(:event4) { create(:push_event, project: project) }
before do
create(:push_event_payload, event: event1, ref: 'foo', action: :created)
create(:push_event_payload, event: event2, ref: 'bar', action: :created)
create(:push_event_payload, event: event3, ref: 'baz', action: :removed)
create(:push_event_payload, event: event4, ref: 'baz', ref_type: :tag)
project.repository.create_branch('bar', 'master')
create(
:merge_request,
source_project: project,
target_project: project,
source_branch: 'bar'
)
end
let(:relation) { described_class.without_existing_merge_requests }
it 'includes events that do not have a corresponding merge request' do
expect(relation).to include(event1)
end
it 'does not include events that have a corresponding merge request' do
expect(relation).not_to include(event2)
end
it 'does not include events for removed refs' do
expect(relation).not_to include(event3)
end
it 'does not include events for pushing to tags' do
expect(relation).not_to include(event4)
end
end
describe '.sti_name' do
it 'returns Event::PUSHED' do
expect(described_class.sti_name).to eq(Event::PUSHED)
......
......@@ -1347,56 +1347,24 @@ describe User do
end
describe "#recent_push" do
subject { create(:user) }
let!(:project1) { create(:project, :repository) }
let!(:project2) { create(:project, :repository, forked_from_project: project1) }
let!(:push_event) do
event = create(:push_event, project: project2, author: subject)
create(:push_event_payload,
event: event,
commit_to: '1cf19a015df3523caf0a1f9d40c98a267d6a2fc2',
commit_count: 0,
ref: 'master')
event
end
before do
project1.team << [subject, :master]
project2.team << [subject, :master]
end
it "includes push event" do
expect(subject.recent_push).to eq(push_event)
end
it "excludes push event if branch has been deleted" do
allow_any_instance_of(Repository).to receive(:branch_exists?).with('master').and_return(false)
expect(subject.recent_push).to eq(nil)
end
let(:user) { build(:user) }
let(:project) { build(:project) }
let(:event) { build(:push_event) }
it "excludes push event if MR is opened for it" do
create(:merge_request, source_project: project2, target_project: project1, source_branch: project2.default_branch, target_branch: 'fix', author: subject)
it 'returns the last push event for the user' do
expect_any_instance_of(Users::LastPushEventService)
.to receive(:last_event_for_user)
.and_return(event)
expect(subject.recent_push).to eq(nil)
expect(user.recent_push).to eq(event)
end
it "includes push events on any of the provided projects" do
expect(subject.recent_push(project1)).to eq(nil)
expect(subject.recent_push(project2)).to eq(push_event)
push_event1 = create(:push_event, project: project1, author: subject)
create(:push_event_payload,
event: push_event1,
commit_to: '1cf19a015df3523caf0a1f9d40c98a267d6a2fc2',
commit_count: 0,
ref: 'master')
it 'returns the last push event for a project when one is given' do
expect_any_instance_of(Users::LastPushEventService)
.to receive(:last_event_for_project)
.and_return(event)
expect(subject.recent_push([project1, project2])).to eq(push_event1) # Newest
expect(user.recent_push(project)).to eq(event)
end
end
......
......@@ -149,6 +149,14 @@ describe EventCreateService do
.to change { user_activity(user) }
end
it 'caches the last push event for the user' do
expect_any_instance_of(Users::LastPushEventService)
.to receive(:cache_last_push_event)
.with(an_instance_of(PushEvent))
service.push(project, user, push_data)
end
it 'does not create any event data when an error is raised' do
payload_service = double(:service)
......
require 'spec_helper'
describe Users::LastPushEventService do
let(:user) { build(:user, id: 1) }
let(:project) { build(:project, id: 2) }
let(:event) { build(:push_event, id: 3, author: user, project: project) }
let(:service) { described_class.new(user) }
describe '#cache_last_push_event' do
it "caches the event for the event's project and current user" do
expect(service).to receive(:set_key)
.ordered
.with('last-push-event/1/2', 3)
expect(service).to receive(:set_key)
.ordered
.with('last-push-event/1', 3)
service.cache_last_push_event(event)
end
it 'caches the event for the origin project when pushing to a fork' do
source = build(:project, id: 5)
allow(project).to receive(:forked?).and_return(true)
allow(project).to receive(:forked_from_project).and_return(source)
expect(service).to receive(:set_key)
.ordered
.with('last-push-event/1/2', 3)
expect(service).to receive(:set_key)
.ordered
.with('last-push-event/1', 3)
expect(service).to receive(:set_key)
.ordered
.with('last-push-event/1/5', 3)
service.cache_last_push_event(event)
end
end
describe '#last_event_for_user' do
it 'returns the last push event for the current user' do
expect(service).to receive(:find_cached_event)
.with('last-push-event/1')
.and_return(event)
expect(service.last_event_for_user).to eq(event)
end
it 'returns nil when no push event could be found' do
expect(service).to receive(:find_cached_event)
.with('last-push-event/1')
.and_return(nil)
expect(service.last_event_for_user).to be_nil
end
end
describe '#last_event_for_project' do
it 'returns the last push event for the given project' do
expect(service).to receive(:find_cached_event)
.with('last-push-event/1/2')
.and_return(event)
expect(service.last_event_for_project(project)).to eq(event)
end
it 'returns nil when no push event could be found' do
expect(service).to receive(:find_cached_event)
.with('last-push-event/1/2')
.and_return(nil)
expect(service.last_event_for_project(project)).to be_nil
end
end
describe '#find_cached_event', :use_clean_rails_memory_store_caching do
context 'with a non-existing cache key' do
it 'returns nil' do
expect(service.find_cached_event('bla')).to be_nil
end
end
context 'with an existing cache key' do
before do
service.cache_last_push_event(event)
end
it 'returns a PushEvent when no merge requests exist for the event' do
allow(service).to receive(:find_event_in_database)
.with(event.id)
.and_return(event)
expect(service.find_cached_event('last-push-event/1')).to eq(event)
end
it 'removes the cache key when no event could be found and returns nil' do
allow(PushEvent).to receive(:without_existing_merge_requests)
.and_return(PushEvent.none)
expect(Rails.cache).to receive(:delete)
.with('last-push-event/1')
.and_call_original
expect(service.find_cached_event('last-push-event/1')).to be_nil
end
end
end
end
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册