Commit 2faf1563 authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into ce-to-ee-2018-08-28

parents d50dd3e8 da338f4e
......@@ -160,7 +160,8 @@ stages:
.single-script-job: &single-script-job
image: ruby:2.4-alpine
stage: build
before_script: []
stage: test
cache: {}
dependencies: []
variables: &single-script-job-variables
......
Please view this file on the master branch, on stable branches it's out of date.
## 11.2.3 (2018-08-28)
- No changes.
## 11.2.2 (2018-08-27)
### Security (1 change)
- Prevent regular users from moving projects to different storage shards.
## 11.2.1 (2018-08-22)
- No changes.
......@@ -96,6 +107,18 @@ Please view this file on the master branch, on stable branches it's out of date.
- Geo: Log to geo.log when the Log Cursor skips an event.
## 11.1.6 (2018-08-28)
- No changes.
## 11.1.5 (2018-08-27)
- No changes.
### Security (1 change)
- Prevent regular users from moving projects to different storage shards.
## 11.1.4 (2018-07-30)
- No changes.
......@@ -189,6 +212,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Geo - Make Geo repository verification flag opt-out by default. !6369
## 11.0.6 (2018-08-27)
### Security (1 change)
- Prevent regular users from moving projects to different storage shards.
## 11.0.5 (2018-07-26)
### Security (1 change)
......
......@@ -2,6 +2,19 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.2.3 (2018-08-28)
- No changes.
## 11.2.2 (2018-08-27)
### Security (3 changes)
- Fixed persistent XSS rendering/escaping of diff location lines.
- Adding CSRF protection to Hooks resend action.
- Block link-local addresses in URLBlocker.
## 11.2.1 (2018-08-22)
### Fixed (2 changes)
......@@ -256,6 +269,24 @@ entry.
- Moves help_popover component to a common location.
## 11.1.6 (2018-08-28)
- No changes.
## 11.1.5 (2018-08-27)
- No changes.
### Security (3 changes)
- Fixed persistent XSS rendering/escaping of diff location lines.
- Adding CSRF protection to Hooks resend action.
- Block link-local addresses in URLBlocker.
### Fixed (1 change, 1 of them is from the community)
- Sanitize git URL in import errors. (Jamie Schembri)
## 11.1.4 (2018-07-30)
### Fixed (4 changes, 1 of them is from the community)
......@@ -538,6 +569,19 @@ entry.
- Use monospaced font for MR diff commit link ref on GFM.
## 11.0.6 (2018-08-27)
### Security (3 changes)
- Fixed persistent XSS rendering/escaping of diff location lines.
- Adding CSRF protection to Hooks resend action.
- Block link-local addresses in URLBlocker.
### Fixed (1 change, 1 of them is from the community)
- Sanitize git URL in import errors. (Jamie Schembri)
## 11.0.5 (2018-07-26)
### Security (4 changes)
......
......@@ -4,7 +4,6 @@
%hr
= link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), class: "btn btn-default float-right prepend-left-10"
= link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10"
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
......@@ -4,6 +4,6 @@
Request details
.col-lg-9
= link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), class: "btn btn-default float-right prepend-left-10"
= link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10"
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
---
title: Adding CSRF protection to Hooks resend action
merge_request:
author:
type: security
......@@ -65,7 +65,7 @@ namespace :admin do
resources :hook_logs, only: [:show] do
member do
get :retry
post :retry
end
end
end
......
......@@ -362,7 +362,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :hook_logs, only: [:show] do
member do
get :retry
post :retry
end
end
end
......
......@@ -8,7 +8,7 @@ from source, follow the
NOTE: **Note:**
If your GitLab installation uses external PostgreSQL, the Omnibus roles
will not be able to perform all necessary configuration steps. Refer to the
section on [External PostreSQL][external postgresql] for additional instructions.
section on [External PostgreSQL][external postgresql] for additional instructions.
NOTE: **Note:**
The stages of the setup process must be completed in the documented order.
......
---
comments: false
description: "Learn how to use GitLab CI/CD, the GitLab built-in Continuous Integration, Continuous Deployment, and Continuous Delivery toolset to build, test, and deploy your application."
---
......
## Custom project templates **[PREMIUM ONLY]**
# Custom project templates **[PREMIUM ONLY]**
> **Notes:**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing) 11.2
......
......@@ -52,4 +52,4 @@
%a{ href: 'https://about.gitlab.com/license-faq/' } true-up model
has a retroactive charge for these users at the next renewal. If you want to update your
license sooner to prevent this, please contact
#{mail_to 'sales@gitlab.com'}.
#{mail_to 'renewals@gitlab.com'}.
---
title: Prevent regular users from moving projects to different storage shards
merge_request:
author:
type: security
......@@ -33,9 +33,16 @@ module EE
def verify_update_project_attrs!(project, attrs)
super
verify_storage_attrs!(attrs)
verify_mirror_attrs!(project, attrs)
end
def verify_storage_attrs!(attrs)
unless current_user.admin?
attrs.delete(:repository_storage)
end
end
def verify_mirror_attrs!(project, attrs)
unless can?(current_user, :admin_mirror, project)
attrs.delete(:mirror)
......
......@@ -77,17 +77,61 @@ describe API::Projects do
describe 'PUT /projects/:id' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when updating external classification' do
before do
enable_external_authorization_service_check
end
it 'updates the classification label when enabled' do
it 'updates the classification label' do
put(api("/projects/#{project.id}", user), external_authorization_classification_label: 'new label')
expect(response).to have_gitlab_http_status(200)
expect(project.reload.external_authorization_classification_label).to eq('new label')
end
end
context 'when updating repository storage' do
let(:unknown_storage) { 'new-storage' }
let(:new_project) { create(:project, :repository, namespace: user.namespace) }
context 'as a user' do
it 'returns 200 but does not change repository_storage' do
expect do
Sidekiq::Testing.fake! do
put(api("/projects/#{new_project.id}", user), repository_storage: unknown_storage, issues_enabled: false)
end
end.not_to change(ProjectUpdateRepositoryStorageWorker.jobs, :size)
expect(response).to have_gitlab_http_status(200)
expect(json_response['issues_enabled']).to eq(false)
expect(new_project.reload.repository.storage).to eq('default')
end
end
context 'as an admin' do
let(:admin) { create(:admin) }
it 'returns 500 when repository storage is unknown' do
put(api("/projects/#{new_project.id}", admin), repository_storage: unknown_storage)
expect(response).to have_gitlab_http_status(500)
expect(json_response['message']).to match('ArgumentError')
end
it 'returns 200 when repository storage has changed' do
stub_storage_settings('extra' => { 'path' => 'tmp/tests/extra_storage' })
expect do
Sidekiq::Testing.fake! do
put(api("/projects/#{new_project.id}", admin), repository_storage: 'extra')
end
end.to change(ProjectUpdateRepositoryStorageWorker.jobs, :size).by(1)
expect(response).to have_gitlab_http_status(200)
end
end
end
context 'when updating mirror related attributes' do
let(:import_url) { generate(:url) }
......
......@@ -21,14 +21,18 @@ module QA
def perform(address, *rspec_options)
Runtime::Scenario.define(:gitlab_address, address)
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
Specs::Runner.perform do |specs|
specs.tty = true
specs.tags = self.class.focus
specs.options =
if rspec_options.any?
rspec_options
else
::File.expand_path('../specs/features', __dir__)
['--tag', self.class.focus.join(','), '--', ::File.expand_path('../specs/features', __dir__)]
end
end
end
......
......@@ -20,13 +20,18 @@ module QA
def self.do_perform(address, *rspec_options)
Runtime::Scenario.define(:gitlab_address, address)
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
Specs::Runner.perform do |specs|
specs.tty = true
specs.options =
if rspec_options.any?
rspec_options
else
::File.expand_path('../../specs/features', __dir__)
['--', ::File.expand_path('../../specs/features', __dir__)]
end
end
end
......
# frozen_string_literal: true
module QA
describe 'API users' do
context :manage do
describe 'Users API' do
before(:context) do
@api_client = Runtime::API::Client.new(:gitlab)
end
context 'when authenticated' do
let(:request) { Runtime::API::Request.new(@api_client, '/users') }
it 'get list of users' do
it 'GET /users' do
get request.url
expect_status(200)
end
it 'submit request with a valid user name' do
it 'GET /users/:username with a valid username' do
get request.url, { params: { username: Runtime::User.username } }
expect_status(200)
......@@ -22,20 +24,12 @@ module QA
)
end
it 'submit request with an invalid user name' do
it 'GET /users/:username with an invalid username' do
get request.url, { params: { username: SecureRandom.hex(10) } }
expect_status(200)
expect(json_body).to eq([])
end
end
it 'submit request with an invalid token' do
request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid')
get request.url
expect_status(401)
end
end
end
# frozen_string_literal: true
module QA
context :geo, :orchestrated, :geo do
describe 'Geo Nodes API' do
before(:all) do
get_personal_access_token
end
shared_examples 'retrieving configuration about Geo nodes' do
it 'GET /geo_nodes' do
get api_endpoint('/geo_nodes')
expect_status(200)
expect(json_body.size).to be >= 2
expect_json('?', primary: true)
expect_json_types('*', primary: :boolean, current: :boolean,
files_max_capacity: :integer, repos_max_capacity: :integer,
clone_protocol: :string, _links: :object)
end
it 'GET /geo_nodes/:id' do
get api_endpoint("/geo_nodes/#{geo_node[:id]}")
expect_status(200)
expect(json_body).to eq geo_node
end
end
shared_examples 'retrieving status about all Geo nodes' do
it 'GET /geo_nodes/status' do
get api_endpoint('/geo_nodes/status')
expect_status(200)
expect(json_body.size).to be >= 2
# only need to check that some of the key values are there
expect_json_types('*', health: :string,
attachments_count: :integer,
db_replication_lag_seconds: :integer_or_null,
lfs_objects_count: :integer,
job_artifacts_count: :integer,
projects_count: :integer,
repositories_count: :integer,
wikis_count: :integer,
replication_slots_count: :integer_or_null,
version: :string_or_null)
end
end
shared_examples 'retrieving status about a specific Geo node' do
it 'GET /geo_nodes/:id/status of primary node' do
get api_endpoint("/geo_nodes/#{@primary_node[:id]}/status")
expect_status(200)
expect_json(geo_node_id: @primary_node[:id])
end
it 'GET /geo_nodes/:id/status of secondary node' do
get api_endpoint("/geo_nodes/#{@secondary_node[:id]}/status")
expect_status(200)
expect_json(geo_node_id: @secondary_node[:id])
end
it 'GET /geo_nodes/:id/status of an invalid node' do
get api_endpoint("/geo_nodes/1000/status")
expect_status(404)
end
end
shared_examples 'retrieving project sync failures ocurred on the current node' do
it 'GET /geo_nodes/current/failures' do
get api_endpoint("/geo_nodes/current/failures")
expect_status(200)
expect(json_body).to be_an Array
end
end
describe 'Geo Nodes API on primary node', :geo do
before(:context) do
fetch_nodes(:geo_primary)
end
include_examples 'retrieving configuration about Geo nodes' do
let(:geo_node) { @primary_node }
end
include_examples 'retrieving status about all Geo nodes'
include_examples 'retrieving status about a specific Geo node'
describe 'editing a Geo node' do
it 'PUT /geo_nodes/:id for primary node' do
put api_endpoint("/geo_nodes/#{@primary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for secondary node' do
endpoint = api_endpoint("/geo_nodes/#{@secondary_node[:id]}")
new_attributes = { enabled: false, files_max_capacity: 1000, repos_max_capacity: 2000 }
put endpoint, new_attributes
expect_status(200)
expect_json(new_attributes)
# restore the original values
put endpoint, { enabled: @secondary_node[:enabled],
files_max_capacity: @secondary_node[:files_max_capacity],
repos_max_capacity: @secondary_node[:repos_max_capacity] }
expect_status(200)
end
it 'PUT /geo_nodes/:id for an invalid node' do
put api_endpoint("/geo_nodes/1000"),
{ params: { files_max_capacity: 1000 } }
expect_status(404)
end
end
describe 'repairing a Geo node' do
it 'POST /geo_nodes/:id/repair for primary node' do
post api_endpoint("/geo_nodes/#{@primary_node[:id]}/repair")
expect_status(200)
expect_json(geo_node_id: @primary_node[:id])
end
it 'POST /geo_nodes/:id/repair for secondary node' do
post api_endpoint("/geo_nodes/#{@secondary_node[:id]}/repair")
expect_status(200)
expect_json(geo_node_id: @secondary_node[:id])
end
it 'POST /geo_nodes/:id/repair for an invalid node' do
post api_endpoint("/geo_nodes/1000/repair")
expect_status(404)
end
end
end
describe 'Geo Nodes API on secondary node', :geo do
before(:context) do
fetch_nodes(:geo_secondary)
end
include_examples 'retrieving configuration about Geo nodes' do
let(:geo_node) { @nodes.first }
end
include_examples 'retrieving status about all Geo nodes'
include_examples 'retrieving status about a specific Geo node'
include_examples 'retrieving project sync failures ocurred on the current node'
it 'GET /geo_nodes is not current' do
get api_endpoint('/geo_nodes')
expect_status(200)
expect_json('?', current: false)
end
describe 'editing a Geo node' do
it 'PUT /geo_nodes/:id for primary node' do
put api_endpoint("/geo_nodes/#{@primary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for secondary node' do
put api_endpoint("/geo_nodes/#{@secondary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for an invalid node' do
put api_endpoint('/geo_nodes/1000'),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
end
describe 'repairing a Geo node' do
it 'POST /geo_nodes/:id/repair for primary node' do
post api_endpoint("/geo_nodes/#{@primary_node[:id]}/repair")
expect_status(403)
end
it 'POST /geo_nodes/:id/repair for secondary node' do
post api_endpoint("/geo_nodes/#{@secondary_node[:id]}/repair")
expect_status(403)
end
it 'POST /geo_nodes/:id/repair for an invalid node' do
post api_endpoint('/geo_nodes/1000/repair')
expect_status(403)
end
end
end
def api_endpoint(endpoint)
QA::Runtime::API::Request.new(@api_client, endpoint).url
end
def fetch_nodes(node_type)
@api_client = Runtime::API::Client.new(node_type, personal_access_token: @personal_access_token)
get api_endpoint('/geo_nodes')
@nodes = json_body
@primary_node = @nodes.detect { |node| node[:primary] == true }
@secondary_node = @nodes.detect { |node| node[:primary] == false }
end
# go to the primary and create a personal_access_token, which will be used
# for accessing both the primary and secondary
def get_personal_access_token
api_client = Runtime::API::Client.new(:geo_primary)
@personal_access_token = api_client.personal_access_token
end
end
end
end
module QA
context :manage, :smoke do
describe 'basic user login' do
it 'user logs in using basic credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
end
end
# frozen_string_literal: true
module QA
context :manage, :orchestrated, :ldap do
describe 'LDAP login' do
before do
Runtime::Env.user_type = 'ldap'
end
it 'user logs into GitLab using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
end
end
# frozen_string_literal: true
module QA
context :manage, :orchestrated, :mattermost do
describe 'Mattermost login' do
it 'user logs into Mattermost using GitLab OAuth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do
Page::Mattermost::Login.act { sign_in_using_oauth }
Page::Mattermost::Main.perform do |page|
expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
context :manage, :smoke do
describe 'Project creation' do
it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
created_project = Factory::Resource::Project.fabricate! do |project|
project.name = 'awesome-project'
project.description = 'create awesome project test'
end
expect(created_project.name).to match /^awesome-project-\h{16}$/
expect(page).to have_content(
/Project \S?awesome-project\S+ was successfully created/
)
expect(page).to have_content('create awesome project test')
expect(page).to have_content('The repository for this project is empty')
end
end
end
end
# frozen_string_literal: true
module QA
context :manage, :orchestrated, :github do
describe 'Project import from GitHub' do
let(:imported_project) do
Factory::Resource::ProjectImportedFromGithub.fabricate! do |project|
project.name = 'imported-project'
project.personal_access_token = Runtime::Env.github_access_token
project.github_repository_path = 'gitlab-qa/test-project'
end
end
after do
# We need to delete the imported project because it's impossible to import
# the same GitHub project twice for a given user.
api_client = Runtime::API::Client.new(:gitlab)
delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
delete delete_project_request.url
expect_status(202)
end
it 'user imports a GitHub repo' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
imported_project # import the project
Page::Menu::Main.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(imported_project.name)
end
Page::Project::Show.act { wait_for_import }
verify_repository_import
verify_issues_import
verify_merge_requests_import
verify_labels_import
verify_milestones_import
verify_wiki_import
end
def verify_repository_import
expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
expect(page).to have_content(imported_project.name)
end
def verify_issues_import
Page::Menu::Side.act { click_issues }
expect(page).to have_content('This is a sample issue')
click_link 'This is a sample issue'
expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
# Comments
expect(page).to have_content('This is a comment from @rymai.')
Page::Issuable::Sidebar.perform do |issuable|
expect(issuable).to have_label('enhancement')
expect(issuable).to have_label('help wanted')
expect(issuable).to have_label('good first issue')
end
end
def verify_merge_requests_import
Page::Menu::Side.act { click_merge_requests }
expect(page).to have_content('Improve README.md')
click_link 'Improve README.md'
expect(page).to have_content('This improves the README file a bit.')
# Review comment are not supported yet
expect(page).not_to have_content('Really nice change.')
# Comments
expect(page).to have_content('Nice work! This is a comment from @rymai.')
# Diff comments
expect(page).to have_content('[Review comment] I like that!')
expect(page).to have_content('[Review comment] Nice blank line.')
expect(page).to have_content('[Single diff comment] Much better without this line!')
Page::Issuable::Sidebar.perform do |issuable|
expect(issuable).to have_label('bug')
expect(issuable).to have_label('enhancement')
end
end
def verify_labels_import
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
# to build upon it.
end
def verify_milestones_import
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
# to build upon it.
end
def verify_wiki_import
Page::Menu::Side.act { click_wiki }
expect(page).to have_content('Welcome to the test-project wiki!')
end
end
end
end
# frozen_string_literal: true
module QA
context :manage do
describe 'Project activity' do
it 'user creates an event in the activity page upon Git push' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is a test project'
push.commit_message = 'Add README.md'
end
Page::Menu::Side.act { go_to_activity }
Page::Project::Activity.act { go_to_push_events }
expect(page).to have_content('pushed new branch master')
end
end
end
end
# frozen_string_literal: true
module QA
context :plan, :smoke do
describe 'Issue creation' do
let(:issue_title) { 'issue title' }
it 'user creates an issue' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Issue.fabricate! do |issue|
issue.title = issue_title
end
Page::Menu::Side.act { click_issues }
expect(page).to have_content(issue_title)
end
end
end
end
# frozen_string_literal: true
module QA
describe 'creates a merge request with milestone' do
context :create do
describe 'Merge request creation' do
it 'user creates a new merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
......@@ -29,6 +32,7 @@ module QA
end
end
end
end
describe 'creates a merge request', :smoke do
it 'user creates a new merge request' do
......
# frozen_string_literal: true
module QA
context :create do
describe 'Merge request creation from fork' do
it 'user forks a project, submits a merge request and maintainer merges it' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
merge_request.fork_branch = 'feature-branch'
end
Page::Menu::Main.perform { |main| main.sign_out }
Page::Main::Login.perform { |login| login.sign_in_using_credentials }
merge_request.visit!
Page::MergeRequest::Show.perform { |show| show.merge! }
expect(page).to have_content('The changes were merged')
end
end
end
end
# frozen_string_literal: true
module QA
context :create do
describe 'Merge request rebasing' do
it 'user rebases source branch of merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = "only-fast-forward"
end
Page::Menu::Side.act { go_to_settings }
Page::Project::Settings::MergeRequest.act { enable_ff_only }
merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
merge_request.project = project
merge_request.title = 'Needs rebasing'
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = "other.txt"
push.file_content = "New file added!"
push.branch_name = "master"
push.new_branch = false
end
merge_request.visit!
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_content('Needs rebasing')
expect(merge_request).not_to be_fast_forward_possible
expect(merge_request).not_to have_merge_button
merge_request.rebase!
expect(merge_request).to have_merge_button
expect(merge_request.fast_forward_possible?).to be_truthy
end
end
end
end
end
# frozen_string_literal: true
module QA
context :create do
describe 'Merge request squashing' do
it 'user squashes commits while merging' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = "squash-before-merge"
end
merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
merge_request.project = project
merge_request.title = 'Squashing commits'
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.commit_message = 'to be squashed'
push.branch_name = merge_request.source_branch
push.new_branch = false
push.file_name = 'other.txt'
push.file_content = "Test with unicode characters ❤✓€❄"
end
merge_request.visit!
expect(page).to have_text('to be squashed')
Page::MergeRequest::Show.perform do |merge_request_page|
merge_request_page.mark_to_squash
merge_request_page.merge!
merge_request.project.visit!
Git::Repository.perform do |repository|
repository.uri = Page::Project::Show.act do
choose_repository_clone_http
repository_location.uri
end
repository.use_default_credentials
repository.act { clone }
expect(repository.commits.size).to eq 3
end
end
end
end
end
end
# frozen_string_literal: true
module QA
describe 'clone code from the repository' do
context 'with regular account over http' do
context :create do
describe 'Git clone over HTTP' do
let(:location) do
Page::Project::Show.act do
choose_repository_clone_http
......
# frozen_string_literal: true
module QA
context :create do
describe 'Files management' do
it 'user creates, edits and deletes a file via the Web' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# Create
file_name = 'QA Test - File name'
file_content = 'QA Test - File content'
commit_message_for_create = 'QA Test - Create new file'
Factory::Resource::File.fabricate! do |file|
file.name = file_name
file.content = file_content
file.commit_message = commit_message_for_create
end
expect(page).to have_content('The file has been successfully created.')
expect(page).to have_content(file_name)
expect(page).to have_content(file_content)
expect(page).to have_content(commit_message_for_create)
# Edit
updated_file_content = 'QA Test - Updated file content'
commit_message_for_update = 'QA Test - Update file'
Page::File::Show.act { click_edit }
Page::File::Form.act do
remove_content
add_content(updated_file_content)
add_commit_message(commit_message_for_update)
commit_changes
end
expect(page).to have_content('Your changes have been successfully committed.')
expect(page).to have_content(updated_file_content)
expect(page).to have_content(commit_message_for_update)
# Delete
commit_message_for_delete = 'QA Test - Delete file'
Page::File::Show.act do
click_delete
add_commit_message(commit_message_for_delete)
click_delete_file
end
expect(page).to have_content('The file has been successfully deleted.')
expect(page).to have_content(commit_message_for_delete)
expect(page).to have_no_content(file_name)
end
end
end
end
# frozen_string_literal: true
module QA
describe 'push code to repository' do
context 'with regular account over http' do
context :create do
describe 'Git push over HTTP' do
it 'user pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
......
# frozen_string_literal: true
module QA
context :create do
describe 'Protected branch support' do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
let(:project) do
Factory::Resource::Project.fabricate! do |resource|
resource.name = 'protected-branch-project'
end
end
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
end
context 'when developers and maintainers are allowed to push to a protected branch' do
it 'user with push rights successfully pushes to the protected branch' do
create_protected_branch(allow_to_push: true)
push = push_new_file(branch_name)
expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
context 'when developers and maintainers are not allowed to push to a protected branch' do
it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
push = push_new_file(branch_name)
expect(push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
expect(push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
def create_protected_branch(allow_to_push:)
Factory::Resource::Branch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allow_to_push = allow_to_push
resource.protected = true
end
end
def push_new_file(branch)
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.file_name = 'new_file.md'
resource.file_content = '# This is a new file'
resource.commit_message = 'Add new_file.md'
resource.branch_name = branch_name
resource.new_branch = false
end
end
end
end
end
# frozen_string_literal: true
module QA
context :create do
describe 'Wiki management' do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
def validate_content(content)
expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/)
end
before do
login
end
it 'user creates, edits, clones, and pushes to the wiki' do
wiki = Factory::Resource::Wiki.fabricate! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
resource.message = 'Update home'
end
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.act { go_to_edit_page }
Page::Project::Wiki::New.perform do |page|
page.set_content("My Second Wiki Content")
page.save_changes
end
validate_content('My Second Wiki Content')
Factory::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki
push.file_name = 'Home.md'
push.file_content = '# My Third Wiki Content'
push.commit_message = 'Update Home.md'
end
Page::Menu::Side.act { click_wiki }
expect(page).to have_content('My Third Wiki Content')
end
end
end
end
# frozen_string_literal: true
module QA
context :verify, :docker do
describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
Service::Runner.new(executor).remove!
end
it 'users creates a pipeline which gets processed' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'project-with-pipelines'
project.description = 'Project with CI/CD Pipelines.'
end
Factory::Resource::Runner.fabricate! do |runner|
runner.project = project
runner.name = executor
runner.tags = %w[qa test]
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = '.gitlab-ci.yml'
push.commit_message = 'Add .gitlab-ci.yml'
push.file_content = <<~EOF
test-success:
tags:
- qa
- test
script: echo 'OK'
test-failure:
tags:
- qa
- test
script:
- echo 'FAILURE'
- exit 1
test-tags:
tags:
- qa
- docker
script: echo 'NOOP'
test-artifacts:
tags:
- qa
- test
script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt
artifacts:
paths:
- my-artifacts/
EOF
end
Page::Project::Show.act { wait_for_push }
expect(page).to have_content('Add .gitlab-ci.yml')
Page::Menu::Side.act { click_ci_cd_pipelines }
expect(page).to have_content('All 1')
expect(page).to have_content('Add .gitlab-ci.yml')
puts 'Waiting for the runner to process the pipeline'
sleep 15 # Runner should process all jobs within 15 seconds.
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to be_running
expect(pipeline).to have_build('test-success', status: :success)
expect(pipeline).to have_build('test-failure', status: :failed)
expect(pipeline).to have_build('test-tags', status: :pending)
expect(pipeline).to have_build('test-artifacts', status: :success)
end
end
end
end
end
# frozen_string_literal: true
module QA
context :verify, :docker do
describe 'Runner registration' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
Service::Runner.new(executor).remove!
end
it 'user registers a new specific runner' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Runner.fabricate! do |runner|
runner.name = executor
end
Page::Project::Settings::CICD.perform do |settings|
sleep 5 # Runner should register within 5 seconds
settings.refresh
settings.expand_runners_settings do |page|
expect(page).to have_content(executor)
expect(page).to have_online_runner
end
end
end
end
end
end
# frozen_string_literal: true
module QA
context :verify do
describe 'Secret variable support' do
it 'user adds a secret variable' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = 'VARIABLE_KEY'
resource.value = 'some secret variable'
end
Page::Project::Settings::CICD.perform do |settings|
settings.expand_secret_variables do |page|
expect(page).to have_field(with: 'VARIABLE_KEY')
expect(page).not_to have_field(with: 'some secret variable')
page.reveal_variables
expect(page).to have_field(with: 'some secret variable')
end
end
end
end
end
end
# frozen_string_literal: true
module QA
context :release do
describe 'Deploy key creation' do
it 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
key = Runtime::Key::RSA.new
deploy_key_title = 'deploy key title'
deploy_key_value = key.public_key
deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
resource.title = deploy_key_title
resource.key = deploy_key_value
end
expect(deploy_key.fingerprint).to eq(key.fingerprint)
end
end
end
end
# frozen_string_literal: true
require 'digest/sha1'
module QA
context :release, :docker do
describe 'Git clone using a deploy key' do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
before(:all) do
login
@runner_name = "qa-runner-#{Time.now.to_i}"
@project = Factory::Resource::Project.fabricate! do |resource|
resource.name = 'deploy-key-clone-project'
end
@repository_location = @project.repository_ssh_location
Factory::Resource::Runner.fabricate! do |resource|
resource.project = @project
resource.name = @runner_name
resource.tags = %w[qa docker]
resource.image = 'gitlab/gitlab-runner:ubuntu'
end
Page::Menu::Main.act { sign_out }
end
after(:all) do
Service::Runner.new(@runner_name).remove!
end
keys = [
[Runtime::Key::RSA, 8192],
[Runtime::Key::ECDSA, 521],
[Runtime::Key::ED25519]
]
keys.each do |(key_class, bits)|
it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
key = key_class.new(*bits)
login
Factory::Resource::DeployKey.fabricate! do |resource|
resource.project = @project
resource.title = "deploy key #{key.name}(#{key.bits})"
resource.key = key.public_key
end
deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.project = @project
resource.key = deploy_key_name
resource.value = key.private_key
end
gitlab_ci = <<~YAML
cat-config:
script:
- mkdir -p ~/.ssh
- ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s)
- ssh-add -D
- echo "$#{deploy_key_name}" | ssh-add -
- git clone #{@repository_location.git_uri}
- cd #{@project.name}
- git checkout #{deploy_key_name}
- sha1sum .gitlab-ci.yml
tags:
- qa
- docker
YAML
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = @project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
resource.file_content = gitlab_ci
resource.branch_name = deploy_key_name
resource.new_branch = true
end
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Show.act { wait_for_push }
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.act { go_to_first_job }
Page::Project::Job::Show.perform do |job|
job.wait(reload: false) do
job.completed? && !job.trace_loading?
end
expect(job.passed?).to be_truthy, "Job status did not become \"passed\"."
expect(job.output).to include(sha1sum)
end
end
end
end
end
end
# frozen_string_literal: true
require 'pathname'
module QA
context :configure, :orchestrated, :kubernetes do
describe 'Auto DevOps support' do
after do
@cluster&.remove!
end
it 'user creates a new project and runs auto devops' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |p|
p.name = 'project-with-autodevops'
p.description = 'Project with Auto Devops'
end
# Disable code_quality check in Auto DevOps pipeline as it takes
# too long and times out the test
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = 'CODE_QUALITY_DISABLED'
resource.value = '1'
end
# Create Auto Devops compatible repo
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.directory = Pathname
.new(__dir__)
.join('../../../fixtures/auto_devops_rack')
push.commit_message = 'Create Auto DevOps compatible rack application'
end
Page::Project::Show.act { wait_for_push }
# Create and connect K8s cluster
@cluster = Service::KubernetesCluster.new.create!
kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster|
cluster.project = project
cluster.cluster = @cluster
cluster.install_helm_tiller = true
cluster.install_ingress = true
cluster.install_prometheus = true
cluster.install_runner = true
end
project.visit!
Page::Menu::Side.act { click_ci_cd_settings }
Page::Project::Settings::CICD.perform do |p|
p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io")
end
project.visit!
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_build('build', status: :success, wait: 600)
expect(pipeline).to have_build('test', status: :success, wait: 600)
expect(pipeline).to have_build('production', status: :success, wait: 1200)
end
end
end
end
end
# frozen_string_literal: true
module QA
context :configure, :orchestrated, :mattermost do
describe 'Mattermost support' do
it 'user creates a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Page::Menu::Main.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
page.go_to_new_group
expect(page).to have_content(
/Create a Mattermost team for this group/
)
end
end
end
end
end
# frozen_string_literal: true
module QA
context :geo, :orchestrated, :geo do
describe 'GitLab Geo attachment replication' do
let(:file_to_attach) { File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif')) }
it 'user uploads attachment to the primary node' do
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'project-for-issues'
project.description = 'project for adding issues'
end
issue = Factory::Resource::Issue.fabricate! do |issue|
issue.title = 'My geo issue'
issue.project = project
end
Page::Project::Issue::Show.perform do |show|
show.comment('See attached banana for scale', attachment: file_to_attach)
end
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do |session|
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
expect(page).to have_content 'You are on a secondary, read-only Geo node'
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(project.name)
dashboard.go_to_project(project.name)
end
Page::Menu::Side.act { click_issues }
Page::Project::Issue::Index.perform do |index|
index.wait_for_issue_replication(issue)
end
image_url = find('a[href$="banana_sample.gif"]')[:href]
Page::Project::Issue::Show.perform do |show|
# Wait for attachment replication
found = show.wait(reload: false) do
show.asset_exists?(image_url)
end
expect(found).to be_truthy
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
context :geo, :orchestrated, :geo do
describe 'GitLab Geo project rename replication' do
it 'user renames project' do
# create the project and push code
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-before-rename'
project.description = 'Geo project to be renamed'
end
geo_project_name = project.name
expect(project.name).to include 'geo-before-rename'
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
end
# rename the project
Page::Menu::Main.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(geo_project_name)
end
Page::Menu::Side.act { go_to_settings }
geo_project_renamed = "geo-after-rename-#{SecureRandom.hex(8)}"
Page::Project::Settings::Main.perform do |settings|
settings.expand_advanced_settings do |page|
page.rename_to(geo_project_renamed)
end
end
# check renamed project exist on secondary node
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(geo_project_renamed)
dashboard.go_to_project(geo_project_renamed)
end
Page::Project::Show.perform do |show|
show.wait_for_repository_replication
expect(page).to have_content 'README.md'
expect(page).to have_content 'This is Geo project!'
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
context :geo, :orchestrated, :geo do
describe 'GitLab Geo repository replication' do
it 'users pushes code to the primary node' do
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-project'
project.description = 'Geo test project'
end
geo_project_name = Page::Project::Show.act { project_name }
expect(geo_project_name).to include 'geo-project'
Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
push.project = project
end
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(geo_project_name)
dashboard.go_to_project(geo_project_name)
end
Page::Project::Show.perform do |show|
show.wait_for_repository_replication
expect(page).to have_content 'README.md'
expect(page).to have_content 'This is Geo project!'
end
end
end
end
end
end
end
module QA
describe 'Geo Nodes API' do
before(:all) do
get_personal_access_token
end
shared_examples 'retrieving configuration about Geo nodes' do
it 'GET /geo_nodes' do
get api_endpoint('/geo_nodes')
expect_status(200)
expect(json_body.size).to be >= 2
expect_json('?', primary: true)
expect_json_types('*', primary: :boolean, current: :boolean,
files_max_capacity: :integer, repos_max_capacity: :integer,
clone_protocol: :string, _links: :object)
end
it 'GET /geo_nodes/:id' do
get api_endpoint("/geo_nodes/#{geo_node[:id]}")
expect_status(200)
expect(json_body).to eq geo_node
end
end
shared_examples 'retrieving status about all Geo nodes' do
it 'GET /geo_nodes/status' do
get api_endpoint('/geo_nodes/status')
expect_status(200)
expect(json_body.size).to be >= 2
# only need to check that some of the key values are there
expect_json_types('*', health: :string,
attachments_count: :integer,
db_replication_lag_seconds: :integer_or_null,
lfs_objects_count: :integer,
job_artifacts_count: :integer,
projects_count: :integer,
repositories_count: :integer,
wikis_count: :integer,
replication_slots_count: :integer_or_null,
version: :string_or_null)
end
end
shared_examples 'retrieving status about a specific Geo node' do
it 'GET /geo_nodes/:id/status of primary node' do
get api_endpoint("/geo_nodes/#{@primary_node[:id]}/status")
expect_status(200)
expect_json(geo_node_id: @primary_node[:id])
end
it 'GET /geo_nodes/:id/status of secondary node' do
get api_endpoint("/geo_nodes/#{@secondary_node[:id]}/status")
expect_status(200)
expect_json(geo_node_id: @secondary_node[:id])
end
it 'GET /geo_nodes/:id/status of an invalid node' do
get api_endpoint("/geo_nodes/1000/status")
expect_status(404)
end
end
shared_examples 'retrieving project sync failures ocurred on the current node' do
it 'GET /geo_nodes/current/failures' do
get api_endpoint("/geo_nodes/current/failures")
expect_status(200)
expect(json_body).to be_an Array
end
end
describe 'Geo Nodes API on primary node', :geo do
before(:context) do
fetch_nodes(:geo_primary)
end
include_examples 'retrieving configuration about Geo nodes' do
let(:geo_node) { @primary_node }
end
include_examples 'retrieving status about all Geo nodes'
include_examples 'retrieving status about a specific Geo node'
describe 'editing a Geo node' do
it 'PUT /geo_nodes/:id for primary node' do
put api_endpoint("/geo_nodes/#{@primary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for secondary node' do
endpoint = api_endpoint("/geo_nodes/#{@secondary_node[:id]}")
new_attributes = { enabled: false, files_max_capacity: 1000, repos_max_capacity: 2000 }
put endpoint, new_attributes
expect_status(200)
expect_json(new_attributes)
# restore the original values
put endpoint, { enabled: @secondary_node[:enabled],
files_max_capacity: @secondary_node[:files_max_capacity],
repos_max_capacity: @secondary_node[:repos_max_capacity] }
expect_status(200)
end
it 'PUT /geo_nodes/:id for an invalid node' do
put api_endpoint("/geo_nodes/1000"),
{ params: { files_max_capacity: 1000 } }
expect_status(404)
end
end
describe 'repairing a Geo node' do
it 'POST /geo_nodes/:id/repair for primary node' do
post api_endpoint("/geo_nodes/#{@primary_node[:id]}/repair")
expect_status(200)
expect_json(geo_node_id: @primary_node[:id])
end
it 'POST /geo_nodes/:id/repair for secondary node' do
post api_endpoint("/geo_nodes/#{@secondary_node[:id]}/repair")
expect_status(200)
expect_json(geo_node_id: @secondary_node[:id])
end
it 'POST /geo_nodes/:id/repair for an invalid node' do
post api_endpoint("/geo_nodes/1000/repair")
expect_status(404)
end
end
end
describe 'Geo Nodes API on secondary node', :geo do
before(:context) do
fetch_nodes(:geo_secondary)
end
include_examples 'retrieving configuration about Geo nodes' do
let(:geo_node) { @nodes.first }
end
include_examples 'retrieving status about all Geo nodes'
include_examples 'retrieving status about a specific Geo node'
include_examples 'retrieving project sync failures ocurred on the current node'
it 'GET /geo_nodes is not current' do
get api_endpoint('/geo_nodes')
expect_status(200)
expect_json('?', current: false)
end
describe 'editing a Geo node' do
it 'PUT /geo_nodes/:id for primary node' do
put api_endpoint("/geo_nodes/#{@primary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for secondary node' do
put api_endpoint("/geo_nodes/#{@secondary_node[:id]}"),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
it 'PUT /geo_nodes/:id for an invalid node' do
put api_endpoint('/geo_nodes/1000'),
{ params: { files_max_capacity: 1000 } }
expect_status(403)
end
end
describe 'repairing a Geo node' do
it 'POST /geo_nodes/:id/repair for primary node' do
post api_endpoint("/geo_nodes/#{@primary_node[:id]}/repair")
expect_status(403)
end
it 'POST /geo_nodes/:id/repair for secondary node' do
post api_endpoint("/geo_nodes/#{@secondary_node[:id]}/repair")
expect_status(403)
end
it 'POST /geo_nodes/:id/repair for an invalid node' do
post api_endpoint('/geo_nodes/1000/repair')
expect_status(403)
end
end
end
def api_endpoint(endpoint)
QA::Runtime::API::Request.new(@api_client, endpoint).url
end
def fetch_nodes(node_type)
@api_client = Runtime::API::Client.new(node_type, personal_access_token: @personal_access_token)
get api_endpoint('/geo_nodes')
@nodes = json_body
@primary_node = @nodes.detect { |node| node[:primary] == true }
@secondary_node = @nodes.detect { |node| node[:primary] == false }
end
# go to the primary and create a personal_access_token, which will be used
# for accessing both the primary and secondary
def get_personal_access_token
api_client = Runtime::API::Client.new(:geo_primary)
@personal_access_token = api_client.personal_access_token
end
end
end
module QA
describe 'GitLab Geo attachment replication', :geo do
let(:file_to_attach) { File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif')) }
it 'user uploads attachment to the primary node' do
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'project-for-issues'
project.description = 'project for adding issues'
end
issue = Factory::Resource::Issue.fabricate! do |issue|
issue.title = 'My geo issue'
issue.project = project
end
Page::Project::Issue::Show.perform do |show|
show.comment('See attached banana for scale', attachment: file_to_attach)
end
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do |session|
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
expect(page).to have_content 'You are on a secondary, read-only Geo node'
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(project.name)
dashboard.go_to_project(project.name)
end
Page::Menu::Side.act { click_issues }
Page::Project::Issue::Index.perform do |index|
index.wait_for_issue_replication(issue)
end
image_url = find('a[href$="banana_sample.gif"]')[:href]
Page::Project::Issue::Show.perform do |show|
# Wait for attachment replication
found = show.wait(reload: false) do
show.asset_exists?(image_url)
end
expect(found).to be_truthy
end
end
end
end
end
end
module QA
describe 'GitLab Geo project rename replication', :geo do
it 'user renames project' do
# create the project and push code
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-before-rename'
project.description = 'Geo project to be renamed'
end
geo_project_name = project.name
expect(project.name).to include 'geo-before-rename'
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
end
# rename the project
Page::Menu::Main.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(geo_project_name)
end
Page::Menu::Side.act { go_to_settings }
geo_project_renamed = "geo-after-rename-#{SecureRandom.hex(8)}"
Page::Project::Settings::Main.perform do |settings|
settings.expand_advanced_settings do |page|
page.rename_to(geo_project_renamed)
end
end
# check renamed project exist on secondary node
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(geo_project_renamed)
dashboard.go_to_project(geo_project_renamed)
end
Page::Project::Show.perform do |show|
show.wait_for_repository_replication
expect(page).to have_content 'README.md'
expect(page).to have_content 'This is Geo project!'
end
end
end
end
end
end
module QA
describe 'GitLab Geo repository replication', :geo do
it 'users pushes code to the primary node' do
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-project'
project.description = 'Geo test project'
end
geo_project_name = Page::Project::Show.act { project_name }
expect(geo_project_name).to include 'geo-project'
Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
push.project = project
end
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
EE::Page::Main::Banner.perform do |banner|
expect(banner).to have_secondary_read_only_banner
end
Page::Menu::Main.perform do |menu|
menu.go_to_projects
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.wait_for_project_replication(geo_project_name)
dashboard.go_to_project(geo_project_name)
end
Page::Project::Show.perform do |show|
show.wait_for_repository_replication
expect(page).to have_content 'README.md'
expect(page).to have_content 'This is Geo project!'
end
end
end
end
end
end
module QA
describe 'basic user login', :smoke do
it 'user logs in using basic credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
end
module QA
describe 'LDAP user login', :orchestrated, :ldap do
before do
Runtime::Env.user_type = 'ldap'
end
it 'user logs in using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
end
module QA
describe 'create a new group', :orchestrated, :mattermost do
it 'creating a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Page::Menu::Main.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
page.go_to_new_group
expect(page).to have_content(
/Create a Mattermost team for this group/
)
end
end
end
end
module QA
describe 'logging in to Mattermost', :orchestrated, :mattermost do
it 'can use gitlab oauth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do
Page::Mattermost::Login.act { sign_in_using_oauth }
Page::Mattermost::Main.perform do |page|
expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
end
end
end
end
end
end
module QA
describe 'merge request rebase' do
it 'rebases source branch of merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = "only-fast-forward"
end
Page::Menu::Side.act { go_to_settings }
Page::Project::Settings::MergeRequest.act { enable_ff_only }
merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
merge_request.project = project
merge_request.title = 'Needs rebasing'
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = "other.txt"
push.file_content = "New file added!"
push.branch_name = "master"
push.new_branch = false
end
merge_request.visit!
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_content('Needs rebasing')
expect(merge_request).not_to be_fast_forward_possible
expect(merge_request).not_to have_merge_button
merge_request.rebase!
expect(merge_request).to have_merge_button
expect(merge_request.fast_forward_possible?).to be_truthy
end
end
end
end
module QA
describe 'merge request squash commits' do
it 'when squash commits is marked before merge' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = "squash-before-merge"
end
merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
merge_request.project = project
merge_request.title = 'Squashing commits'
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.commit_message = 'to be squashed'
push.branch_name = merge_request.source_branch
push.new_branch = false
push.file_name = 'other.txt'
push.file_content = "Test with unicode characters ❤✓€❄"
end
merge_request.visit!
expect(page).to have_text('to be squashed')
Page::MergeRequest::Show.perform do |merge_request_page|
merge_request_page.mark_to_squash
merge_request_page.merge!
merge_request.project.visit!
Git::Repository.perform do |repository|
repository.uri = Page::Project::Show.act do
choose_repository_clone_http
repository_location.uri
end
repository.use_default_credentials
repository.act { clone }
expect(repository.commits.size).to eq 3
end
end
end
end
end
module QA
describe 'activity page' do
it 'push creates an event in the activity page' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is a test project'
push.commit_message = 'Add README.md'
end
Page::Menu::Side.act { go_to_activity }
Page::Project::Activity.act { go_to_push_events }
expect(page).to have_content('pushed new branch master')
end
end
end
module QA
describe 'deploy keys support' do
it 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
key = Runtime::Key::RSA.new
deploy_key_title = 'deploy key title'
deploy_key_value = key.public_key
deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
resource.title = deploy_key_title
resource.key = deploy_key_value
end
expect(deploy_key.fingerprint).to eq(key.fingerprint)
end
end
end
module QA
describe 'secret variables support' do
it 'user adds a secret variable' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = 'VARIABLE_KEY'
resource.value = 'some secret variable'
end
Page::Project::Settings::CICD.perform do |settings|
settings.expand_secret_variables do |page|
expect(page).to have_field(with: 'VARIABLE_KEY')
expect(page).not_to have_field(with: 'some secret variable')
page.reveal_variables
expect(page).to have_field(with: 'some secret variable')
end
end
end
end
end
require 'pathname'
module QA
describe 'Auto Devops', :orchestrated, :kubernetes do
after do
@cluster&.remove!
end
it 'user creates a new project and runs auto devops' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |p|
p.name = 'project-with-autodevops'
p.description = 'Project with Auto Devops'
end
# Disable code_quality check in Auto DevOps pipeline as it takes
# too long and times out the test
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = 'CODE_QUALITY_DISABLED'
resource.value = '1'
end
# Create Auto Devops compatible repo
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.directory = Pathname
.new(__dir__)
.join('../../../fixtures/auto_devops_rack')
push.commit_message = 'Create Auto DevOps compatible rack application'
end
Page::Project::Show.act { wait_for_push }
# Create and connect K8s cluster
@cluster = Service::KubernetesCluster.new.create!
kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster|
cluster.project = project
cluster.cluster = @cluster
cluster.install_helm_tiller = true
cluster.install_ingress = true
cluster.install_prometheus = true
cluster.install_runner = true
end
project.visit!
Page::Menu::Side.act { click_ci_cd_settings }
Page::Project::Settings::CICD.perform do |p|
p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io")
end
project.visit!
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_build('build', status: :success, wait: 600)
expect(pipeline).to have_build('test', status: :success, wait: 600)
expect(pipeline).to have_build('production', status: :success, wait: 1200)
end
end
end
end
module QA
describe 'creates issue', :smoke do
let(:issue_title) { 'issue title' }
it 'user creates issue' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Issue.fabricate! do |issue|
issue.title = issue_title
end
Page::Menu::Side.act { click_issues }
expect(page).to have_content(issue_title)
end
end
end
module QA
describe 'create a new project', :smoke do
it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
created_project = Factory::Resource::Project.fabricate! do |project|
project.name = 'awesome-project'
project.description = 'create awesome project test'
end
expect(created_project.name).to match /^awesome-project-\h{16}$/
expect(page).to have_content(
/Project \S?awesome-project\S+ was successfully created/
)
expect(page).to have_content('create awesome project test')
expect(page).to have_content('The repository for this project is empty')
end
end
end
require 'digest/sha1'
module QA
describe 'cloning code using a deploy key', :docker do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
before(:all) do
login
@runner_name = "qa-runner-#{Time.now.to_i}"
@project = Factory::Resource::Project.fabricate! do |resource|
resource.name = 'deploy-key-clone-project'
end
@repository_location = @project.repository_ssh_location
Factory::Resource::Runner.fabricate! do |resource|
resource.project = @project
resource.name = @runner_name
resource.tags = %w[qa docker]
resource.image = 'gitlab/gitlab-runner:ubuntu'
end
Page::Menu::Main.act { sign_out }
end
after(:all) do
Service::Runner.new(@runner_name).remove!
end
keys = [
[Runtime::Key::RSA, 8192],
[Runtime::Key::ECDSA, 521],
[Runtime::Key::ED25519]
]
keys.each do |(key_class, bits)|
it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
key = key_class.new(*bits)
login
Factory::Resource::DeployKey.fabricate! do |resource|
resource.project = @project
resource.title = "deploy key #{key.name}(#{key.bits})"
resource.key = key.public_key
end
deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.project = @project
resource.key = deploy_key_name
resource.value = key.private_key
end
gitlab_ci = <<~YAML
cat-config:
script:
- mkdir -p ~/.ssh
- ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s)
- ssh-add -D
- echo "$#{deploy_key_name}" | ssh-add -
- git clone #{@repository_location.git_uri}
- cd #{@project.name}
- git checkout #{deploy_key_name}
- sha1sum .gitlab-ci.yml
tags:
- qa
- docker
YAML
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = @project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
resource.file_content = gitlab_ci
resource.branch_name = deploy_key_name
resource.new_branch = true
end
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Show.act { wait_for_push }
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.act { go_to_first_job }
Page::Project::Job::Show.perform do |job|
job.wait(reload: false) do
job.completed? && !job.trace_loading?
end
expect(job.passed?).to be_truthy, "Job status did not become \"passed\"."
expect(job.output).to include(sha1sum)
end
end
end
end
end
module QA
describe 'File Functionality', :core do
it 'lets a user create, edit and delete a file via WebUI' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# Create
file_name = 'QA Test - File name'
file_content = 'QA Test - File content'
commit_message_for_create = 'QA Test - Create new file'
Factory::Resource::File.fabricate! do |file|
file.name = file_name
file.content = file_content
file.commit_message = commit_message_for_create
end
expect(page).to have_content('The file has been successfully created.')
expect(page).to have_content(file_name)
expect(page).to have_content(file_content)
expect(page).to have_content(commit_message_for_create)
# Edit
updated_file_content = 'QA Test - Updated file content'
commit_message_for_update = 'QA Test - Update file'
Page::File::Show.act { click_edit }
Page::File::Form.act do
remove_content
add_content(updated_file_content)
add_commit_message(commit_message_for_update)
commit_changes
end
expect(page).to have_content('Your changes have been successfully committed.')
expect(page).to have_content(updated_file_content)
expect(page).to have_content(commit_message_for_update)
# Delete
commit_message_for_delete = 'QA Test - Delete file'
Page::File::Show.act do
click_delete
add_commit_message(commit_message_for_delete)
click_delete_file
end
expect(page).to have_content('The file has been successfully deleted.')
expect(page).to have_content(commit_message_for_delete)
expect(page).to have_no_content(file_name)
end
end
end
module QA
describe 'Project fork' do
it 'can submit merge requests to upstream master' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
merge_request.fork_branch = 'feature-branch'
end
Page::Menu::Main.perform { |main| main.sign_out }
Page::Main::Login.perform { |login| login.sign_in_using_credentials }
merge_request.visit!
Page::MergeRequest::Show.perform { |show| show.merge! }
expect(page).to have_content('The changes were merged')
end
end
end
module QA
describe 'user imports a GitHub repo', :orchestrated, :github do
let(:imported_project) do
Factory::Resource::ProjectImportedFromGithub.fabricate! do |project|
project.name = 'imported-project'
project.personal_access_token = Runtime::Env.github_access_token
project.github_repository_path = 'gitlab-qa/test-project'
end
end
after do
# We need to delete the imported project because it's impossible to import
# the same GitHub project twice for a given user.
api_client = Runtime::API::Client.new(:gitlab)
delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
delete delete_project_request.url
expect_status(202)
end
it 'user imports a GitHub repo' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
imported_project # import the project
Page::Menu::Main.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(imported_project.name)
end
Page::Project::Show.act { wait_for_import }
verify_repository_import
verify_issues_import
verify_merge_requests_import
verify_labels_import
verify_milestones_import
verify_wiki_import
end
def verify_repository_import
expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
expect(page).to have_content(imported_project.name)
end
def verify_issues_import
Page::Menu::Side.act { click_issues }
expect(page).to have_content('This is a sample issue')
click_link 'This is a sample issue'
expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
# Comments
expect(page).to have_content('This is a comment from @rymai.')
Page::Issuable::Sidebar.perform do |issuable|
expect(issuable).to have_label('enhancement')
expect(issuable).to have_label('help wanted')
expect(issuable).to have_label('good first issue')
end
end
def verify_merge_requests_import
Page::Menu::Side.act { click_merge_requests }
expect(page).to have_content('Improve README.md')
click_link 'Improve README.md'
expect(page).to have_content('This improves the README file a bit.')
# Review comment are not supported yet
expect(page).not_to have_content('Really nice change.')
# Comments
expect(page).to have_content('Nice work! This is a comment from @rymai.')
# Diff comments
expect(page).to have_content('[Review comment] I like that!')
expect(page).to have_content('[Review comment] Nice blank line.')
expect(page).to have_content('[Single diff comment] Much better without this line!')
Page::Issuable::Sidebar.perform do |issuable|
expect(issuable).to have_label('bug')
expect(issuable).to have_label('enhancement')
end
end
def verify_labels_import
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
# to build upon it.
end
def verify_milestones_import
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
# to build upon it.
end
def verify_wiki_import
Page::Menu::Side.act { click_wiki }
expect(page).to have_content('Welcome to the test-project wiki!')
end
end
end
module QA
describe 'CI/CD Pipelines', :orchestrated, :docker do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
Service::Runner.new(executor).remove!
end
it 'user registers a new specific runner' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Runner.fabricate! do |runner|
runner.name = executor
end
Page::Project::Settings::CICD.perform do |settings|
sleep 5 # Runner should register within 5 seconds
settings.refresh
settings.expand_runners_settings do |page|
expect(page).to have_content(executor)
expect(page).to have_online_runner
end
end
end
it 'users creates a new pipeline' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'project-with-pipelines'
project.description = 'Project with CI/CD Pipelines.'
end
Factory::Resource::Runner.fabricate! do |runner|
runner.project = project
runner.name = executor
runner.tags = %w[qa test]
end
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = '.gitlab-ci.yml'
push.commit_message = 'Add .gitlab-ci.yml'
push.file_content = <<~EOF
test-success:
tags:
- qa
- test
script: echo 'OK'
test-failure:
tags:
- qa
- test
script:
- echo 'FAILURE'
- exit 1
test-tags:
tags:
- qa
- docker
script: echo 'NOOP'
test-artifacts:
tags:
- qa
- test
script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt
artifacts:
paths:
- my-artifacts/
EOF
end
Page::Project::Show.act { wait_for_push }
expect(page).to have_content('Add .gitlab-ci.yml')
Page::Menu::Side.act { click_ci_cd_pipelines }
expect(page).to have_content('All 1')
expect(page).to have_content('Add .gitlab-ci.yml')
puts 'Waiting for the runner to process the pipeline'
sleep 15 # Runner should process all jobs within 15 seconds.
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to be_running
expect(pipeline).to have_build('test-success', status: :success)
expect(pipeline).to have_build('test-failure', status: :failed)
expect(pipeline).to have_build('test-tags', status: :pending)
expect(pipeline).to have_build('test-artifacts', status: :success)
end
end
end
end
module QA
describe 'Wiki Functionality' do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
def validate_content(content)
expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/)
end
before do
login
end
it 'User creates, edits, clones, and pushes to the wiki' do
wiki = Factory::Resource::Wiki.fabricate! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
resource.message = 'Update home'
end
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.act { go_to_edit_page }
Page::Project::Wiki::New.perform do |page|
page.set_content("My Second Wiki Content")
page.save_changes
end
validate_content('My Second Wiki Content')
Factory::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki
push.file_name = 'Home.md'
push.file_content = '# My Third Wiki Content'
push.commit_message = 'Update Home.md'
end
Page::Menu::Side.act { click_wiki }
expect(page).to have_content('My Third Wiki Content')
end
end
end
module QA
describe 'branch protection support' do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
let(:project) do
Factory::Resource::Project.fabricate! do |resource|
resource.name = 'protected-branch-project'
end
end
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
end
context 'when developers and maintainers are allowed to push to a protected branch' do
it 'user with push rights successfully pushes to the protected branch' do
create_protected_branch(allow_to_push: true)
push = push_new_file(branch_name)
expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
context 'when developers and maintainers are not allowed to push to a protected branch' do
it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
push = push_new_file(branch_name)
expect(push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
expect(push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
def create_protected_branch(allow_to_push:)
Factory::Resource::Branch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allow_to_push = allow_to_push
resource.protected = true
end
end
def push_new_file(branch)
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.file_name = 'new_file.md'
resource.file_content = '# This is a new file'
resource.commit_message = 'Add new_file.md'
resource.branch_name = branch_name
resource.new_branch = false
end
end
end
end
......@@ -104,6 +104,8 @@ describe QA::Runtime::Env do
describe '.github_access_token' do
it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do
stub_env('GITHUB_ACCESS_TOKEN', nil)
expect(described_class.github_access_token).to eq('')
end
......@@ -115,6 +117,8 @@ describe QA::Runtime::Env do
describe '.require_github_access_token!' do
it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do
stub_env('GITHUB_ACCESS_TOKEN', nil)
expect { described_class.require_github_access_token! }.to raise_error(ArgumentError)
end
......
describe QA::Scenario::Test::Instance::All do
subject do
Class.new(described_class) do
tags :rspec, :foo
end
end
context '#perform' do
let(:arguments) { spy('Runtime::Scenario') }
let(:release) { spy('Runtime::Release') }
......@@ -24,7 +30,7 @@ describe QA::Scenario::Test::Instance::All do
subject.perform("test")
expect(runner).to have_received(:options=)
.with(::File.expand_path('../../../../qa/specs/features', __dir__))
.with(['--tag', 'rspec,foo', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)])
end
end
......
......@@ -30,7 +30,7 @@ describe QA::Scenario::Test::Instance::Smoke do
subject.perform("test")
expect(runner).to have_received(:options=)
.with(::File.expand_path('../../../../qa/specs/features', __dir__))
.with(['--tag', 'smoke', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)])
end
end
......
......@@ -103,11 +103,11 @@ describe Admin::HooksController, "routing" do
end
end
# admin_hook_hook_log_retry GET /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry
# admin_hook_hook_log_retry POST /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry
# admin_hook_hook_log GET /admin/hooks/:hook_id/hook_logs/:id(.:format) admin/hook_logs#show
describe Admin::HookLogsController, 'routing' do
it 'to #retry' do
expect(get('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1')
expect(post('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1')
end
it 'to #show' do
......
......@@ -381,7 +381,7 @@ describe 'project routing' do
end
end
# test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test
# test_project_hook POST /:project_id/hooks/:id/test(.:format) hooks#test
# project_hooks GET /:project_id/hooks(.:format) hooks#index
# POST /:project_id/hooks(.:format) hooks#create
# edit_project_hook GET /:project_id/hooks/:id/edit(.:format) hooks#edit
......@@ -398,11 +398,11 @@ describe 'project routing' do
end
end
# retry_namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry
# retry_namespace_project_hook_hook_log POST /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry
# namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id(.:format) projects/hook_logs#show
describe Projects::HookLogsController, 'routing' do
it 'to #retry' do
expect(get('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1')
expect(post('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1')
end
it 'to #show' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment