Add ability to drive the API in QA specs
This commit is contained in:
parent
fcfda3ef82
commit
b8b9e9eb8e
18 changed files with 420 additions and 24 deletions
|
@ -6,3 +6,4 @@ gem 'capybara-screenshot', '~> 1.0.18'
|
||||||
gem 'rake', '~> 12.3.0'
|
gem 'rake', '~> 12.3.0'
|
||||||
gem 'rspec', '~> 3.7'
|
gem 'rspec', '~> 3.7'
|
||||||
gem 'selenium-webdriver', '~> 3.8.0'
|
gem 'selenium-webdriver', '~> 3.8.0'
|
||||||
|
gem 'airborne', '~> 0.2.13'
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
activesupport (5.1.4)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (~> 0.7)
|
||||||
|
minitest (~> 5.1)
|
||||||
|
tzinfo (~> 1.1)
|
||||||
addressable (2.5.2)
|
addressable (2.5.2)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
|
airborne (0.2.13)
|
||||||
|
activesupport
|
||||||
|
rack
|
||||||
|
rack-test (~> 0.6, >= 0.6.2)
|
||||||
|
rest-client (>= 1.7.3, < 3.0)
|
||||||
|
rspec (~> 3.1)
|
||||||
byebug (9.1.0)
|
byebug (9.1.0)
|
||||||
capybara (2.16.1)
|
capybara (2.16.1)
|
||||||
addressable
|
addressable
|
||||||
|
@ -17,13 +28,25 @@ GEM
|
||||||
childprocess (0.8.0)
|
childprocess (0.8.0)
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
ffi (~> 1.0, >= 1.0.11)
|
||||||
coderay (1.1.2)
|
coderay (1.1.2)
|
||||||
|
concurrent-ruby (1.0.5)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
|
domain_name (0.5.20170404)
|
||||||
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
ffi (1.9.18)
|
ffi (1.9.18)
|
||||||
|
http-cookie (1.0.3)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
i18n (0.9.1)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
launchy (2.4.3)
|
launchy (2.4.3)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
method_source (0.9.0)
|
method_source (0.9.0)
|
||||||
|
mime-types (3.1)
|
||||||
|
mime-types-data (~> 3.2015)
|
||||||
|
mime-types-data (3.2016.0521)
|
||||||
mini_mime (1.0.0)
|
mini_mime (1.0.0)
|
||||||
mini_portile2 (2.3.0)
|
mini_portile2 (2.3.0)
|
||||||
|
minitest (5.11.1)
|
||||||
|
netrc (0.11.0)
|
||||||
nokogiri (1.8.1)
|
nokogiri (1.8.1)
|
||||||
mini_portile2 (~> 2.3.0)
|
mini_portile2 (~> 2.3.0)
|
||||||
pry (0.11.3)
|
pry (0.11.3)
|
||||||
|
@ -37,11 +60,15 @@ GEM
|
||||||
rack-test (0.8.2)
|
rack-test (0.8.2)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rake (12.3.0)
|
rake (12.3.0)
|
||||||
|
rest-client (2.0.2)
|
||||||
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
|
mime-types (>= 1.16, < 4.0)
|
||||||
|
netrc (~> 0.8)
|
||||||
rspec (3.7.0)
|
rspec (3.7.0)
|
||||||
rspec-core (~> 3.7.0)
|
rspec-core (~> 3.7.0)
|
||||||
rspec-expectations (~> 3.7.0)
|
rspec-expectations (~> 3.7.0)
|
||||||
rspec-mocks (~> 3.7.0)
|
rspec-mocks (~> 3.7.0)
|
||||||
rspec-core (3.7.0)
|
rspec-core (3.7.1)
|
||||||
rspec-support (~> 3.7.0)
|
rspec-support (~> 3.7.0)
|
||||||
rspec-expectations (3.7.0)
|
rspec-expectations (3.7.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
@ -54,6 +81,12 @@ GEM
|
||||||
selenium-webdriver (3.8.0)
|
selenium-webdriver (3.8.0)
|
||||||
childprocess (~> 0.5)
|
childprocess (~> 0.5)
|
||||||
rubyzip (~> 1.0)
|
rubyzip (~> 1.0)
|
||||||
|
thread_safe (0.3.6)
|
||||||
|
tzinfo (1.2.4)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.7.4)
|
||||||
xpath (2.1.0)
|
xpath (2.1.0)
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
|
|
||||||
|
@ -61,6 +94,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
airborne (~> 0.2.13)
|
||||||
capybara (~> 2.16.1)
|
capybara (~> 2.16.1)
|
||||||
capybara-screenshot (~> 1.0.18)
|
capybara-screenshot (~> 1.0.18)
|
||||||
pry-byebug (~> 3.5.1)
|
pry-byebug (~> 3.5.1)
|
||||||
|
@ -69,4 +103,4 @@ DEPENDENCIES
|
||||||
selenium-webdriver (~> 3.8.0)
|
selenium-webdriver (~> 3.8.0)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.16.0
|
1.16.1
|
||||||
|
|
8
qa/qa.rb
8
qa/qa.rb
|
@ -11,6 +11,8 @@ module QA
|
||||||
autoload :Scenario, 'qa/runtime/scenario'
|
autoload :Scenario, 'qa/runtime/scenario'
|
||||||
autoload :Browser, 'qa/runtime/browser'
|
autoload :Browser, 'qa/runtime/browser'
|
||||||
autoload :Env, 'qa/runtime/env'
|
autoload :Env, 'qa/runtime/env'
|
||||||
|
autoload :Address, 'qa/runtime/address'
|
||||||
|
autoload :API, 'qa/runtime/api'
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -26,6 +28,7 @@ module QA
|
||||||
autoload :Group, 'qa/factory/resource/group'
|
autoload :Group, 'qa/factory/resource/group'
|
||||||
autoload :Project, 'qa/factory/resource/project'
|
autoload :Project, 'qa/factory/resource/project'
|
||||||
autoload :DeployKey, 'qa/factory/resource/deploy_key'
|
autoload :DeployKey, 'qa/factory/resource/deploy_key'
|
||||||
|
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
|
||||||
end
|
end
|
||||||
|
|
||||||
module Repository
|
module Repository
|
||||||
|
@ -85,6 +88,7 @@ module QA
|
||||||
autoload :Main, 'qa/page/menu/main'
|
autoload :Main, 'qa/page/menu/main'
|
||||||
autoload :Side, 'qa/page/menu/side'
|
autoload :Side, 'qa/page/menu/side'
|
||||||
autoload :Admin, 'qa/page/menu/admin'
|
autoload :Admin, 'qa/page/menu/admin'
|
||||||
|
autoload :Profile, 'qa/page/menu/profile'
|
||||||
end
|
end
|
||||||
|
|
||||||
module Dashboard
|
module Dashboard
|
||||||
|
@ -108,6 +112,10 @@ module QA
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Profile
|
||||||
|
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
|
||||||
|
end
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
autoload :Settings, 'qa/page/admin/settings'
|
autoload :Settings, 'qa/page/admin/settings'
|
||||||
end
|
end
|
||||||
|
|
27
qa/qa/factory/resource/personal_access_token.rb
Normal file
27
qa/qa/factory/resource/personal_access_token.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module QA
|
||||||
|
module Factory
|
||||||
|
module Resource
|
||||||
|
##
|
||||||
|
# Create a personal access token that can be used by the api
|
||||||
|
#
|
||||||
|
class PersonalAccessToken < Factory::Base
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
product :access_token do
|
||||||
|
Page::Profile::PersonalAccessTokens.act { created_access_token }
|
||||||
|
end
|
||||||
|
|
||||||
|
def fabricate!
|
||||||
|
Page::Menu::Main.act { go_to_profile_settings }
|
||||||
|
Page::Menu::Profile.act { click_access_tokens }
|
||||||
|
|
||||||
|
Page::Profile::PersonalAccessTokens.perform do |page|
|
||||||
|
page.fill_token_name(name || 'api-test-token')
|
||||||
|
page.check_api
|
||||||
|
page.create_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -77,7 +77,7 @@ module Page
|
||||||
|
|
||||||
view 'app/views/devise/sessions/_new_base.html.haml' do
|
view 'app/views/devise/sessions/_new_base.html.haml' do
|
||||||
element :login_field, 'text_field :login'
|
element :login_field, 'text_field :login'
|
||||||
element :passowrd_field, 'password_field :password'
|
element :password_field, 'password_field :password'
|
||||||
element :sign_in_button, 'submit "Sign in"'
|
element :sign_in_button, 'submit "Sign in"'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,6 +103,16 @@ view 'app/views/my/view.html.haml' do
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Running the test locally
|
||||||
|
|
||||||
|
During development, you can run the `qa:selectors` test by running
|
||||||
|
|
||||||
|
```shell
|
||||||
|
bin/qa Test::Sanity::Selectors
|
||||||
|
```
|
||||||
|
|
||||||
|
from within the `qa` directory.
|
||||||
|
|
||||||
## Where to ask for help?
|
## Where to ask for help?
|
||||||
|
|
||||||
If you need more information, ask for help on `#qa` channel on Slack (GitLab
|
If you need more information, ask for help on `#qa` channel on Slack (GitLab
|
||||||
|
|
|
@ -7,6 +7,7 @@ module QA
|
||||||
element :user_avatar
|
element :user_avatar
|
||||||
element :user_menu, '.dropdown-menu-nav'
|
element :user_menu, '.dropdown-menu-nav'
|
||||||
element :user_sign_out_link, 'link_to "Sign out"'
|
element :user_sign_out_link, 'link_to "Sign out"'
|
||||||
|
element :settings_link, 'link_to "Settings"'
|
||||||
end
|
end
|
||||||
|
|
||||||
view 'app/views/layouts/nav/_dashboard.html.haml' do
|
view 'app/views/layouts/nav/_dashboard.html.haml' do
|
||||||
|
@ -40,7 +41,13 @@ module QA
|
||||||
|
|
||||||
def sign_out
|
def sign_out
|
||||||
within_user_menu do
|
within_user_menu do
|
||||||
click_link('Sign out')
|
click_link 'Sign out'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def go_to_profile_settings
|
||||||
|
within_user_menu do
|
||||||
|
click_link 'Settings'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
27
qa/qa/page/menu/profile.rb
Normal file
27
qa/qa/page/menu/profile.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module QA
|
||||||
|
module Page
|
||||||
|
module Menu
|
||||||
|
class Profile < Page::Base
|
||||||
|
view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
|
||||||
|
element :access_token_link, 'link_to profile_personal_access_tokens_path'
|
||||||
|
element :access_token_title, 'Access Tokens'
|
||||||
|
element :top_level_items, '.sidebar-top-level-items'
|
||||||
|
end
|
||||||
|
|
||||||
|
def click_access_tokens
|
||||||
|
within_sidebar do
|
||||||
|
click_link('Access Tokens')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def within_sidebar
|
||||||
|
page.within('.sidebar-top-level-items') do
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
qa/qa/page/profile/personal_access_tokens.rb
Normal file
33
qa/qa/page/profile/personal_access_tokens.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module QA
|
||||||
|
module Page
|
||||||
|
module Profile
|
||||||
|
class PersonalAccessTokens < Page::Base
|
||||||
|
view 'app/views/shared/_personal_access_tokens_form.html.haml' do
|
||||||
|
element :personal_access_token_name_field, 'text_field :name'
|
||||||
|
element :create_token_button, 'submit "Create #{type} token"' # rubocop:disable Lint/InterpolationCheck
|
||||||
|
element :scopes_api_radios, "label :scopes"
|
||||||
|
end
|
||||||
|
|
||||||
|
view 'app/views/profiles/personal_access_tokens/index.html.haml' do
|
||||||
|
element :create_token_field, "text_field_tag 'created-personal-access-token'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_token_name(name)
|
||||||
|
fill_in 'personal_access_token_name', with: name
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_api
|
||||||
|
check 'personal_access_token_scopes_api'
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_token
|
||||||
|
click_on 'Create personal access token'
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_access_token
|
||||||
|
page.find('#created-personal-access-token').value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
20
qa/qa/runtime/address.rb
Normal file
20
qa/qa/runtime/address.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module QA
|
||||||
|
module Runtime
|
||||||
|
class Address
|
||||||
|
attr_reader :address
|
||||||
|
|
||||||
|
def initialize(instance, page = nil)
|
||||||
|
@instance = instance
|
||||||
|
@address = host + (page.is_a?(String) ? page : page&.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def host
|
||||||
|
if @instance.is_a?(Symbol)
|
||||||
|
Runtime::Scenario.send("#{@instance}_address")
|
||||||
|
else
|
||||||
|
@instance.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
82
qa/qa/runtime/api.rb
Normal file
82
qa/qa/runtime/api.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
require 'airborne'
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Runtime
|
||||||
|
module API
|
||||||
|
class Client
|
||||||
|
attr_reader :address
|
||||||
|
|
||||||
|
def initialize(address = :gitlab)
|
||||||
|
@address = address
|
||||||
|
end
|
||||||
|
|
||||||
|
def personal_access_token
|
||||||
|
@personal_access_token ||= get_personal_access_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_personal_access_token
|
||||||
|
# you can set the environment variable PERSONAL_ACCESS_TOKEN
|
||||||
|
# to use a specific access token rather than create one from the UI
|
||||||
|
if Runtime::Env.personal_access_token
|
||||||
|
Runtime::Env.personal_access_token
|
||||||
|
else
|
||||||
|
create_personal_access_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_personal_access_token
|
||||||
|
Runtime::Browser.visit(@address, Page::Main::Login) do
|
||||||
|
Page::Main::Login.act { sign_in_using_credentials }
|
||||||
|
Factory::Resource::PersonalAccessToken.fabricate!.access_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Request
|
||||||
|
API_VERSION = 'v4'.freeze
|
||||||
|
|
||||||
|
def initialize(api_client, path, personal_access_token: nil)
|
||||||
|
personal_access_token ||= api_client.personal_access_token
|
||||||
|
request_path = request_path(path, personal_access_token: personal_access_token)
|
||||||
|
@session_address = Runtime::Address.new(api_client.address, request_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
@session_address.address
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prepend a request path with the path to the API
|
||||||
|
#
|
||||||
|
# path - Path to append
|
||||||
|
#
|
||||||
|
# Examples
|
||||||
|
#
|
||||||
|
# >> request_path('/issues')
|
||||||
|
# => "/api/v4/issues"
|
||||||
|
#
|
||||||
|
# >> request_path('/issues', personal_access_token: 'sometoken)
|
||||||
|
# => "/api/v4/issues?private_token=..."
|
||||||
|
#
|
||||||
|
# Returns the relative path to the requested API resource
|
||||||
|
def request_path(path, version: API_VERSION, personal_access_token: nil, oauth_access_token: nil)
|
||||||
|
full_path = File.join('/api', version, path)
|
||||||
|
|
||||||
|
if oauth_access_token
|
||||||
|
query_string = "access_token=#{oauth_access_token}"
|
||||||
|
elsif personal_access_token
|
||||||
|
query_string = "private_token=#{personal_access_token}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if query_string
|
||||||
|
full_path << (path.include?('?') ? '&' : '?')
|
||||||
|
full_path << query_string
|
||||||
|
end
|
||||||
|
|
||||||
|
full_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -24,9 +24,7 @@ module QA
|
||||||
# based on `Runtime::Scenario#something_address`.
|
# based on `Runtime::Scenario#something_address`.
|
||||||
#
|
#
|
||||||
def visit(address, page, &block)
|
def visit(address, page, &block)
|
||||||
Browser::Session.new(address, page).tap do |session|
|
Browser::Session.new(address, page).perform(&block)
|
||||||
session.perform(&block)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.visit(address, page, &block)
|
def self.visit(address, page, &block)
|
||||||
|
@ -94,20 +92,15 @@ module QA
|
||||||
include Capybara::DSL
|
include Capybara::DSL
|
||||||
|
|
||||||
def initialize(instance, page = nil)
|
def initialize(instance, page = nil)
|
||||||
@instance = instance
|
@session_address = Runtime::Address.new(instance, page)
|
||||||
@address = host + page&.path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def host
|
def url
|
||||||
if @instance.is_a?(Symbol)
|
@session_address.address
|
||||||
Runtime::Scenario.send("#{@instance}_address")
|
|
||||||
else
|
|
||||||
@instance.to_s
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(&block)
|
def perform(&block)
|
||||||
visit(@address)
|
visit(url)
|
||||||
|
|
||||||
yield if block_given?
|
yield if block_given?
|
||||||
rescue
|
rescue
|
||||||
|
@ -130,7 +123,7 @@ module QA
|
||||||
# See gitlab-org/gitlab-qa#102
|
# See gitlab-org/gitlab-qa#102
|
||||||
#
|
#
|
||||||
def clear!
|
def clear!
|
||||||
visit(@address)
|
visit(url)
|
||||||
reset_session!
|
reset_session!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ module QA
|
||||||
module Env
|
module Env
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
|
# set to 'false' to have Chrome run visibly instead of headless
|
||||||
def chrome_headless?
|
def chrome_headless?
|
||||||
(ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
|
(ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
|
||||||
end
|
end
|
||||||
|
@ -10,6 +11,11 @@ module QA
|
||||||
def running_in_ci?
|
def running_in_ci?
|
||||||
ENV['CI'] || ENV['CI_SERVER']
|
ENV['CI'] || ENV['CI_SERVER']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# specifies token that can be used for the api
|
||||||
|
def personal_access_token
|
||||||
|
ENV['PERSONAL_ACCESS_TOKEN']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
42
qa/qa/specs/features/api/users_spec.rb
Normal file
42
qa/qa/specs/features/api/users_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module QA
|
||||||
|
feature 'API users', :core 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') }
|
||||||
|
|
||||||
|
scenario 'get list of users' do
|
||||||
|
get request.url
|
||||||
|
|
||||||
|
expect_status(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'submit request with a valid user name' do
|
||||||
|
get request.url, { params: { username: 'root' } }
|
||||||
|
|
||||||
|
expect_status(200)
|
||||||
|
expect(json_body).to be_an Array
|
||||||
|
expect(json_body.size).to eq(1)
|
||||||
|
expect(json_body.first[:username]).to eq Runtime::User.name
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'submit request with an invalid user name' do
|
||||||
|
get request.url, { params: { username: 'invalid' } }
|
||||||
|
|
||||||
|
expect_status(200)
|
||||||
|
expect(json_body).to be_an Array
|
||||||
|
expect(json_body.size).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'submit request with an invalid token' do
|
||||||
|
request = Runtime::API::Request.new(@api_client, '/users', personal_access_token: 'invalid')
|
||||||
|
|
||||||
|
get request.url
|
||||||
|
|
||||||
|
expect_status(401)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
30
qa/spec/runtime/api_client_spec.rb
Normal file
30
qa/spec/runtime/api_client_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
describe QA::Runtime::API::Client do
|
||||||
|
include Support::StubENV
|
||||||
|
|
||||||
|
describe 'initialization' do
|
||||||
|
it 'defaults to :gitlab address' do
|
||||||
|
expect(described_class.new.address).to eq :gitlab
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses specified address' do
|
||||||
|
client = described_class.new('http:///example.com')
|
||||||
|
|
||||||
|
expect(client.address).to eq 'http:///example.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#get_personal_access_token' do
|
||||||
|
it 'returns specified token from env' do
|
||||||
|
stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
|
||||||
|
|
||||||
|
expect(described_class.new.get_personal_access_token).to eq 'a_token'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a created token' do
|
||||||
|
allow_any_instance_of(described_class)
|
||||||
|
.to receive(:create_personal_access_token).and_return('created_token')
|
||||||
|
|
||||||
|
expect(described_class.new.get_personal_access_token).to eq 'created_token'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
42
qa/spec/runtime/api_request_spec.rb
Normal file
42
qa/spec/runtime/api_request_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
describe QA::Runtime::API::Request do
|
||||||
|
include Support::StubENV
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:client) { QA::Runtime::API::Client.new('http://example.com') }
|
||||||
|
let(:request) { described_class.new(client, '/users') }
|
||||||
|
|
||||||
|
describe '#url' do
|
||||||
|
it 'returns the full api request url' do
|
||||||
|
expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#request_path' do
|
||||||
|
it 'prepends the api path' do
|
||||||
|
expect(request.request_path('/users')).to eq '/api/v4/users'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the personal access token' do
|
||||||
|
expect(request.request_path('/users', personal_access_token: 'token'))
|
||||||
|
.to eq '/api/v4/users?private_token=token'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the oauth access token' do
|
||||||
|
expect(request.request_path('/users', oauth_access_token: 'otoken'))
|
||||||
|
.to eq '/api/v4/users?access_token=otoken'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'respects query parameters' do
|
||||||
|
expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1'
|
||||||
|
expect(request.request_path('/users?page=1', personal_access_token: 'token'))
|
||||||
|
.to eq '/api/v4/users?page=1&private_token=token'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses a different api version' do
|
||||||
|
expect(request.request_path('/users', version: 'v3')).to eq '/api/v3/users'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,5 @@
|
||||||
describe QA::Runtime::Env do
|
describe QA::Runtime::Env do
|
||||||
before do
|
include Support::StubENV
|
||||||
allow(ENV).to receive(:[]).and_call_original
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.chrome_headless?' do
|
describe '.chrome_headless?' do
|
||||||
context 'when there is an env variable set' do
|
context 'when there is an env variable set' do
|
||||||
|
@ -57,8 +55,4 @@ describe QA::Runtime::Env do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_env(name, value)
|
|
||||||
allow(ENV).to receive(:[]).with(name).and_return(value)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
require_relative '../qa'
|
require_relative '../qa'
|
||||||
|
|
||||||
|
Dir[File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.expect_with :rspec do |expectations|
|
config.expect_with :rspec do |expectations|
|
||||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||||
|
|
38
qa/spec/support/stub_env.rb
Normal file
38
qa/spec/support/stub_env.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
|
||||||
|
module Support
|
||||||
|
module StubENV
|
||||||
|
def stub_env(key_or_hash, value = nil)
|
||||||
|
init_stub unless env_stubbed?
|
||||||
|
|
||||||
|
if key_or_hash.is_a? Hash
|
||||||
|
key_or_hash.each { |k, v| add_stubbed_value(k, v) }
|
||||||
|
else
|
||||||
|
add_stubbed_value key_or_hash, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
STUBBED_KEY = '__STUBBED__'.freeze
|
||||||
|
|
||||||
|
def add_stubbed_value(key, value)
|
||||||
|
allow(ENV).to receive(:[]).with(key).and_return(value)
|
||||||
|
allow(ENV).to receive(:key?).with(key).and_return(true)
|
||||||
|
allow(ENV).to receive(:fetch).with(key).and_return(value)
|
||||||
|
allow(ENV).to receive(:fetch).with(key, anything()) do |_, default_val|
|
||||||
|
value || default_val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def env_stubbed?
|
||||||
|
ENV[STUBBED_KEY]
|
||||||
|
end
|
||||||
|
|
||||||
|
def init_stub
|
||||||
|
allow(ENV).to receive(:[]).and_call_original
|
||||||
|
allow(ENV).to receive(:key?).and_call_original
|
||||||
|
allow(ENV).to receive(:fetch).and_call_original
|
||||||
|
add_stubbed_value(STUBBED_KEY, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue