Commit 056ad1c8 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 9be5ae87 4e503205
......@@ -315,9 +315,9 @@ fieldset:disabled a.btn {
-moz-appearance: none;
}
.gl-form-input:disabled,
.gl-form-input:not(.form-control-plaintext):read-only,
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):read-only {
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
background-color: #fafafa;
color: #868686;
box-shadow: inset 0 0 0 1px #dbdbdb;
......
......@@ -8,6 +8,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This is a tailored extension of the Best Practices [found in the testing guide](../best_practices.md).
## Class and module naming
The QA framework uses [Zeitwerk](https://github.com/fxn/zeitwerk) for class and module autoloading. The default Zeitwerk [inflector](https://github.com/fxn/zeitwerk#zeitwerkinflector) simply converts snake_cased file names to PascalCased module or class names. It is advised to stick to this pattern to avoid manual maintenance of inflections.
In case custom inflection logic is needed, custom inflectors are added in the [qa.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa.rb) file in the `loader.inflector.inflect` method invocation.
## Link a test to its test-case issue
Every test should have a corresponding issue in the [Quality Test Cases project](https://gitlab.com/gitlab-org/quality/testcases/).
......@@ -342,7 +348,7 @@ end
When something requires waiting to be matched, use `eventually_` matchers with clear wait duration definition.
`Eventually` matchers use the following naming pattern: `eventually_${rspec_matcher_name}`. They are defined in [eventually_matcher.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/spec/support/matchers/eventually_matcher.rb).
`Eventually` matchers use the following naming pattern: `eventually_${rspec_matcher_name}`. They are defined in [eventually_matcher.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/support/matchers/eventually_matcher.rb).
```ruby
expect { async_value }.to eventually_eq(value).within(max_duration: 120, max_attempts: 60, reload_page: page)
......
......@@ -315,9 +315,9 @@ fieldset:disabled a.btn {
-moz-appearance: none;
}
.gl-form-input:disabled,
.gl-form-input:not(.form-control-plaintext):read-only,
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):read-only {
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
background-color: #fafafa;
color: #868686;
box-shadow: inset 0 0 0 1px #dbdbdb;
......
......@@ -35,10 +35,9 @@ module API
not_found!('Commit') unless @commit
@repo = user_project.repository
@blob = @repo.blob_at(@commit.sha, params[:file_path])
@blob = @repo.blob_at(@commit.sha, params[:file_path], limit: Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE)
not_found!('File') unless @blob
@blob.load_all_data!
end
def commit_response(attrs)
......@@ -48,13 +47,21 @@ module API
}
end
def content_sha
Rails.cache.fetch("blob_content_sha256:#{user_project.full_path}:#{@blob.id}") do
@blob.load_all_data!
Digest::SHA256.hexdigest(@blob.data)
end
end
def blob_data
{
file_name: @blob.name,
file_path: @blob.path,
size: @blob.size,
encoding: "base64",
content_sha256: Digest::SHA256.hexdigest(@blob.data),
content_sha256: content_sha,
ref: params[:ref],
blob_id: @blob.id,
commit_id: @commit.id,
......@@ -154,6 +161,8 @@ module API
get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
@blob.load_all_data!
data = blob_data
set_http_headers(data)
......
......@@ -2,7 +2,7 @@
source 'https://rubygems.org'
gem 'gitlab-qa'
gem 'gitlab-qa', require: 'gitlab/qa'
gem 'activesupport', '~> 6.1.3.2' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.14.1'
gem 'capybara', '~> 3.35.0'
......@@ -10,10 +10,9 @@ gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.3'
gem 'rspec', '~> 3.10'
gem 'selenium-webdriver', '~> 4.0.0.beta4'
gem 'airborne', '~> 0.3.4'
gem 'airborne', '~> 0.3.4', require: false # airborne is messing with rspec sandboxed mode so not requiring by default
gem 'rest-client', '~> 2.1.0'
gem 'nokogiri', '~> 1.11.7'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry'
gem 'rspec_junit_formatter', '~> 0.4.1'
gem 'faker', '~> 1.6', '>= 1.6.6'
gem 'knapsack', '~> 1.17'
......@@ -24,6 +23,7 @@ gem 'parallel', '~> 1.19'
gem 'rspec-parameterized', '~> 0.4.2'
gem 'octokit', '~> 4.21'
gem 'webdrivers', '~> 4.6'
gem 'zeitwerk', '~> 2.4'
gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
......
......@@ -221,7 +221,6 @@ DEPENDENCIES
faker (~> 1.6, >= 1.6.6)
gitlab-qa
knapsack (~> 1.17)
nokogiri (~> 1.11.7)
octokit (~> 4.21)
parallel (~> 1.19)
parallel_tests (~> 2.29)
......@@ -237,6 +236,7 @@ DEPENDENCIES
selenium-webdriver (~> 4.0.0.beta4)
timecop (~> 0.9.1)
webdrivers (~> 4.6)
zeitwerk (~> 2.4)
BUNDLED WITH
2.2.22
# frozen_string_literal: true
$: << File.expand_path(__dir__)
Encoding.default_external = 'UTF-8'
require_relative '../lib/gitlab'
......@@ -10,683 +8,49 @@ require_relative '../config/initializers/0_inject_enterprise_edition_module'
require_relative 'lib/gitlab'
require 'chemlab'
require 'bundler/setup'
Bundler.require(:default)
module QA
##
# Helper classes to represent frequently used sequences of actions
# (e.g., login)
#
module Flow
autoload :Login, 'qa/flow/login'
autoload :Project, 'qa/flow/project'
autoload :Saml, 'qa/flow/saml'
autoload :User, 'qa/flow/user'
autoload :MergeRequest, 'qa/flow/merge_request'
autoload :Pipeline, 'qa/flow/pipeline'
autoload :SignUp, 'qa/flow/sign_up'
end
##
# GitLab QA runtime classes, mostly singletons.
#
module Runtime
autoload :Release, 'qa/runtime/release'
autoload :User, 'qa/runtime/user'
autoload :Namespace, 'qa/runtime/namespace'
autoload :Scenario, 'qa/runtime/scenario'
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
autoload :Address, 'qa/runtime/address'
autoload :Path, 'qa/runtime/path'
autoload :Feature, 'qa/runtime/feature'
autoload :Fixtures, 'qa/runtime/fixtures'
autoload :Logger, 'qa/runtime/logger'
autoload :GPG, 'qa/runtime/gpg'
autoload :MailHog, 'qa/runtime/mail_hog'
autoload :IPAddress, 'qa/runtime/ip_address'
autoload :Search, 'qa/runtime/search'
autoload :ApplicationSettings, 'qa/runtime/application_settings'
autoload :AllureReport, 'qa/runtime/allure_report'
module API
autoload :Client, 'qa/runtime/api/client'
autoload :RepositoryStorageMoves, 'qa/runtime/api/repository_storage_moves'
autoload :Request, 'qa/runtime/api/request'
end
module Key
autoload :Base, 'qa/runtime/key/base'
autoload :RSA, 'qa/runtime/key/rsa'
autoload :ECDSA, 'qa/runtime/key/ecdsa'
autoload :ED25519, 'qa/runtime/key/ed25519'
end
end
##
# GitLab QA fabrication mechanisms
#
module Resource
autoload :ApiFabricator, 'qa/resource/api_fabricator'
autoload :Base, 'qa/resource/base'
autoload :GroupBase, 'qa/resource/group_base'
autoload :Sandbox, 'qa/resource/sandbox'
autoload :Group, 'qa/resource/group'
autoload :BulkImportGroup, 'qa/resource/bulk_import_group'
autoload :Issue, 'qa/resource/issue'
autoload :ProjectIssueNote, 'qa/resource/project_issue_note'
autoload :Project, 'qa/resource/project'
autoload :LabelBase, 'qa/resource/label_base'
autoload :ProjectLabel, 'qa/resource/project_label'
autoload :GroupLabel, 'qa/resource/group_label'
autoload :MergeRequest, 'qa/resource/merge_request'
autoload :ProjectImportedFromGithub, 'qa/resource/project_imported_from_github'
autoload :ProjectImportedFromURL, 'qa/resource/project_imported_from_url'
autoload :MergeRequestFromFork, 'qa/resource/merge_request_from_fork'
autoload :DeployKey, 'qa/resource/deploy_key'
autoload :DeployToken, 'qa/resource/deploy_token'
autoload :ProtectedBranch, 'qa/resource/protected_branch'
autoload :Pipeline, 'qa/resource/pipeline'
autoload :CiVariable, 'qa/resource/ci_variable'
autoload :Runner, 'qa/resource/runner'
autoload :PersonalAccessToken, 'qa/resource/personal_access_token'
autoload :PersonalAccessTokenCache, 'qa/resource/personal_access_token_cache'
autoload :ProjectAccessToken, 'qa/resource/project_access_token'
autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone'
autoload :GroupMilestone, 'qa/resource/group_milestone'
autoload :Members, 'qa/resource/members'
autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key'
autoload :Snippet, 'qa/resource/snippet'
autoload :Tag, 'qa/resource/tag'
autoload :ProjectMember, 'qa/resource/project_member'
autoload :ProjectSnippet, 'qa/resource/project_snippet'
autoload :UserGPG, 'qa/resource/user_gpg'
autoload :Visibility, 'qa/resource/visibility'
autoload :ProjectSnippet, 'qa/resource/project_snippet'
autoload :Design, 'qa/resource/design'
autoload :RegistryRepository, 'qa/resource/registry_repository'
autoload :Package, 'qa/resource/package'
autoload :PipelineSchedules, 'qa/resource/pipeline_schedules'
autoload :ImportProject, 'qa/resource/import_project'
module KubernetesCluster
autoload :Base, 'qa/resource/kubernetes_cluster/base'
autoload :ProjectCluster, 'qa/resource/kubernetes_cluster/project_cluster'
end
module Clusters
autoload :Agent, 'qa/resource/clusters/agent.rb'
autoload :AgentToken, 'qa/resource/clusters/agent_token.rb'
end
module Events
autoload :Base, 'qa/resource/events/base'
autoload :Project, 'qa/resource/events/project'
end
module Repository
autoload :Commit, 'qa/resource/repository/commit'
autoload :Push, 'qa/resource/repository/push'
autoload :ProjectPush, 'qa/resource/repository/project_push'
autoload :WikiPush, 'qa/resource/repository/wiki_push'
end
module Wiki
autoload :ProjectPage, 'qa/resource/wiki/project_page'
autoload :GroupPage, 'qa/resource/wiki/group_page'
end
end
##
# GitLab QA Scenarios
#
module Scenario
##
# Support files
#
autoload :Bootable, 'qa/scenario/bootable'
autoload :Actable, 'qa/scenario/actable'
autoload :Template, 'qa/scenario/template'
autoload :SharedAttributes, 'qa/scenario/shared_attributes'
##
# Test scenario entrypoints.
#
module Test
autoload :Instance, 'qa/scenario/test/instance'
module Instance
autoload :All, 'qa/scenario/test/instance/all'
autoload :Smoke, 'qa/scenario/test/instance/smoke'
autoload :Airgapped, 'qa/scenario/test/instance/airgapped'
end
module Integration
autoload :Github, 'qa/scenario/test/integration/github'
autoload :LDAPNoTLS, 'qa/scenario/test/integration/ldap_no_tls'
autoload :LDAPNoServer, 'qa/scenario/test/integration/ldap_no_server'
autoload :LDAPTLS, 'qa/scenario/test/integration/ldap_tls'
autoload :InstanceSAML, 'qa/scenario/test/integration/instance_saml'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
autoload :ObjectStorage, 'qa/scenario/test/integration/object_storage'
autoload :SMTP, 'qa/scenario/test/integration/smtp'
autoload :SSHTunnel, 'qa/scenario/test/integration/ssh_tunnel'
autoload :Registry, 'qa/scenario/test/integration/registry'
end
module Sanity
autoload :Framework, 'qa/scenario/test/sanity/framework'
autoload :Selectors, 'qa/scenario/test/sanity/selectors'
end
end
end
##
# Classes describing structure of GitLab, pages, menus etc.
#
# Needed to execute click-driven-only black-box tests.
#
module Page
autoload :Base, 'qa/page/base'
autoload :View, 'qa/page/view'
autoload :Element, 'qa/page/element'
autoload :PageConcern, 'qa/page/page_concern'
autoload :Validator, 'qa/page/validator'
autoload :Validatable, 'qa/page/validatable'
module SubMenus
autoload :Common, 'qa/page/sub_menus/common'
end
module Main
autoload :Login, 'qa/page/main/login'
autoload :Menu, 'qa/page/main/menu'
autoload :OAuth, 'qa/page/main/oauth'
autoload :TwoFactorAuth, 'qa/page/main/two_factor_auth'
autoload :Terms, 'qa/page/main/terms'
end
module Registration
autoload :SignUp, 'qa/page/registration/sign_up'
autoload :Welcome, 'qa/page/registration/welcome'
end
module Settings
autoload :Common, 'qa/page/settings/common'
end
module Dashboard
autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups'
autoload :Welcome, 'qa/page/dashboard/welcome'
autoload :Todos, 'qa/page/dashboard/todos'
module Snippet
autoload :New, 'qa/page/dashboard/snippet/new'
autoload :Index, 'qa/page/dashboard/snippet/index'
autoload :Show, 'qa/page/dashboard/snippet/show'
autoload :Edit, 'qa/page/dashboard/snippet/edit'
end
end
module Group
autoload :New, 'qa/page/group/new'
autoload :Show, 'qa/page/group/show'
autoload :Menu, 'qa/page/group/menu'
autoload :Members, 'qa/page/group/members'
autoload :BulkImport, 'qa/page/group/bulk_import'
autoload :DependencyProxy, 'qa/page/group/dependency_proxy'
module Milestone
autoload :Index, 'qa/page/group/milestone/index'
autoload :New, 'qa/page/group/milestone/new'
end
module SubMenus
autoload :Common, 'qa/page/group/sub_menus/common'
end
module Settings
autoload :General, 'qa/page/group/settings/general'
autoload :PackageRegistries, 'qa/page/group/settings/package_registries'
end
end
module Milestone
autoload :Index, 'qa/page/milestone/index'
autoload :New, 'qa/page/milestone/new'
autoload :Show, 'qa/page/milestone/show'
end
module File
autoload :Form, 'qa/page/file/form'
autoload :Show, 'qa/page/file/show'
autoload :Edit, 'qa/page/file/edit'
module Shared
autoload :CommitMessage, 'qa/page/file/shared/commit_message'
autoload :CommitButton, 'qa/page/file/shared/commit_button'
autoload :Editor, 'qa/page/file/shared/editor'
end
end
module Project
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
autoload :Activity, 'qa/page/project/activity'
autoload :Menu, 'qa/page/project/menu'
autoload :Members, 'qa/page/project/members'
module Artifact
autoload :Show, 'qa/page/project/artifact/show'
end
module Branches
autoload :Show, 'qa/page/project/branches/show'
end
module Commit
autoload :Show, 'qa/page/project/commit/show'
end
module Import
autoload :Github, 'qa/page/project/import/github'
autoload :RepoByURL, 'qa/page/project/import/repo_by_url'
end
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
autoload :New, 'qa/page/project/pipeline/new'
end
module PipelineEditor
autoload :Show, 'qa/page/project/pipeline_editor/show'
end
module Tag
autoload :Index, 'qa/page/project/tag/index'
autoload :New, 'qa/page/project/tag/new'
autoload :Show, 'qa/page/project/tag/show'
end
module Job
autoload :Show, 'qa/page/project/job/show'
end
module Packages
autoload :Index, 'qa/page/project/packages/index'
autoload :Show, 'qa/page/project/packages/show'
end
module Registry
autoload :Show, 'qa/page/project/registry/show'
end
module Settings
autoload :Advanced, 'qa/page/project/settings/advanced'
autoload :Main, 'qa/page/project/settings/main'
autoload :Repository, 'qa/page/project/settings/repository'
autoload :CICD, 'qa/page/project/settings/ci_cd'
autoload :Integrations, 'qa/page/project/settings/integrations'
autoload :GeneralPipelines, 'qa/page/project/settings/general_pipelines'
autoload :AutoDevops, 'qa/page/project/settings/auto_devops'
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
autoload :DeployTokens, 'qa/page/project/settings/deploy_tokens'
autoload :ProtectedBranches, 'qa/page/project/settings/protected_branches'
autoload :CiVariables, 'qa/page/project/settings/ci_variables'
autoload :Runners, 'qa/page/project/settings/runners'
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
autoload :MirroringRepositories, 'qa/page/project/settings/mirroring_repositories'
autoload :ProtectedTags, 'qa/page/project/settings/protected_tags'
autoload :DefaultBranch, 'qa/page/project/settings/default_branch'
autoload :VisibilityFeaturesPermissions, 'qa/page/project/settings/visibility_features_permissions'
autoload :AccessTokens, 'qa/page/project/settings/access_tokens'
module Services
autoload :Jira, 'qa/page/project/settings/services/jira'
autoload :Jenkins, 'qa/page/project/settings/services/jenkins'
autoload :Prometheus, 'qa/page/project/settings/services/prometheus'
end
autoload :Monitor, 'qa/page/project/settings/monitor'
autoload :Alerts, 'qa/page/project/settings/alerts'
autoload :Integrations, 'qa/page/project/settings/integrations'
end
module SubMenus
autoload :CiCd, 'qa/page/project/sub_menus/ci_cd'
autoload :Common, 'qa/page/project/sub_menus/common'
autoload :Issues, 'qa/page/project/sub_menus/issues'
autoload :Monitor, 'qa/page/project/sub_menus/monitor'
autoload :Deployments, 'qa/page/project/sub_menus/deployments'
autoload :Infrastructure, 'qa/page/project/sub_menus/infrastructure'
autoload :Repository, 'qa/page/project/sub_menus/repository'
autoload :Settings, 'qa/page/project/sub_menus/settings'
autoload :Project, 'qa/page/project/sub_menus/project'
autoload :Packages, 'qa/page/project/sub_menus/packages'
end
module Issue
autoload :New, 'qa/page/project/issue/new'
autoload :Show, 'qa/page/project/issue/show'
autoload :Index, 'qa/page/project/issue/index'
autoload :JiraImport, 'qa/page/project/issue/jira_import'
end
module Fork
autoload :New, 'qa/page/project/fork/new'
end
module Milestone
autoload :New, 'qa/page/project/milestone/new'
autoload :Index, 'qa/page/project/milestone/index'
end
module Deployments
module Environments
autoload :Index, 'qa/page/project/deployments/environments/index'
end
end
module Infrastructure
module Kubernetes
autoload :Index, 'qa/page/project/infrastructure/kubernetes/index'
autoload :Add, 'qa/page/project/infrastructure/kubernetes/add'
autoload :AddExisting, 'qa/page/project/infrastructure/kubernetes/add_existing'
autoload :Show, 'qa/page/project/infrastructure/kubernetes/show'
end
end
module Monitor
module Metrics
autoload :Show, 'qa/page/project/monitor/metrics/show'
end
module Incidents
autoload :Index, 'qa/page/project/monitor/incidents/index'
end
end
module Wiki
autoload :Edit, 'qa/page/project/wiki/edit'
autoload :Show, 'qa/page/project/wiki/show'
autoload :GitAccess, 'qa/page/project/wiki/git_access'
autoload :List, 'qa/page/project/wiki/list'
end
module WebIDE
autoload :Edit, 'qa/page/project/web_ide/edit'
end
module Snippet
autoload :New, 'qa/page/project/snippet/new'
autoload :Show, 'qa/page/project/snippet/show'
autoload :Index, 'qa/page/project/snippet/index'
end
module Secure
autoload :ConfigurationForm, 'qa/page/project/secure/configuration_form'
end
end
module Profile
autoload :Menu, 'qa/page/profile/menu'
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
autoload :SSHKeys, 'qa/page/profile/ssh_keys'
autoload :Emails, 'qa/page/profile/emails'
autoload :Password, 'qa/page/profile/password'
autoload :TwoFactorAuth, 'qa/page/profile/two_factor_auth'
module Accounts
autoload :Show, 'qa/page/profile/accounts/show'
end
end
module User
autoload :Show, 'qa/page/user/show'
end
module Issuable
autoload :New, 'qa/page/issuable/new'
end
module Alert
autoload :AutoDevopsAlert, 'qa/page/alert/auto_devops_alert'
autoload :FreeTrial, 'qa/page/alert/free_trial'
end
module Layout
autoload :Banner, 'qa/page/layout/banner'
autoload :Flash, 'qa/page/layout/flash'
autoload :PerformanceBar, 'qa/page/layout/performance_bar'
end
module Label
autoload :New, 'qa/page/label/new'
autoload :Index, 'qa/page/label/index'
end
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
end
module Admin
autoload :Menu, 'qa/page/admin/menu'
autoload :NewSession, 'qa/page/admin/new_session'
module Settings
autoload :General, 'qa/page/admin/settings/general'
autoload :MetricsAndProfiling, 'qa/page/admin/settings/metrics_and_profiling'
autoload :Network, 'qa/page/admin/settings/network'
module Component
autoload :IpLimits, 'qa/page/admin/settings/component/ip_limits'
autoload :OutboundRequests, 'qa/page/admin/settings/component/outbound_requests'
autoload :AccountAndLimit, 'qa/page/admin/settings/component/account_and_limit'
autoload :PerformanceBar, 'qa/page/admin/settings/component/performance_bar'
autoload :SignUpRestrictions, 'qa/page/admin/settings/component/sign_up_restrictions'
end
end
module Overview
module Users
autoload :Index, 'qa/page/admin/overview/users/index'
autoload :Show, 'qa/page/admin/overview/users/show'
end
module Groups
autoload :Index, 'qa/page/admin/overview/groups/index'
autoload :Show, 'qa/page/admin/overview/groups/show'
autoload :Edit, 'qa/page/admin/overview/groups/edit'
end
end
end
module Mattermost
autoload :Main, 'qa/page/mattermost/main'
autoload :Login, 'qa/page/mattermost/login'
end
module Search
autoload :Results, 'qa/page/search/results'
end
##
# Classes describing components that are used by several pages.
#
module Component
autoload :Breadcrumbs, 'qa/page/component/breadcrumbs'
autoload :CiBadgeLink, 'qa/page/component/ci_badge_link'
autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :DesignManagement, 'qa/page/component/design_management'
autoload :LazyLoader, 'qa/page/component/lazy_loader'
autoload :LegacyClonePanel, 'qa/page/component/legacy_clone_panel'
autoload :Dropzone, 'qa/page/component/dropzone'
autoload :GroupsFilter, 'qa/page/component/groups_filter'
autoload :Select2, 'qa/page/component/select2'
autoload :DropdownFilter, 'qa/page/component/dropdown_filter'
autoload :UsersSelect, 'qa/page/component/users_select'
autoload :Note, 'qa/page/component/note'
autoload :ConfirmModal, 'qa/page/component/confirm_modal'
autoload :CustomMetric, 'qa/page/component/custom_metric'
autoload :DesignManagement, 'qa/page/component/design_management'
autoload :ProjectSelector, 'qa/page/component/project_selector'
autoload :Snippet, 'qa/page/component/snippet'
autoload :NewSnippet, 'qa/page/component/new_snippet'
autoload :InviteMembersModal, 'qa/page/component/invite_members_modal'
autoload :Wiki, 'qa/page/component/wiki'
autoload :WikiSidebar, 'qa/page/component/wiki_sidebar'
autoload :WikiPageForm, 'qa/page/component/wiki_page_form'
autoload :AccessTokens, 'qa/page/component/access_tokens'
autoload :CommitModal, 'qa/page/component/commit_modal'
autoload :VisibilitySetting, 'qa/page/component/visibility_setting'
autoload :ContentEditor, 'qa/page/component/content_editor'
module Import
autoload :Gitlab, 'qa/page/component/import/gitlab'
autoload :Selection, 'qa/page/component/import/selection'
end
module Issuable
autoload :Common, 'qa/page/component/issuable/common'
autoload :Sidebar, 'qa/page/component/issuable/sidebar'
end
module IssueBoard
autoload :Show, 'qa/page/component/issue_board/show'
end
module WebIDE
autoload :Alert, 'qa/page/component/web_ide/alert'
module Modal
autoload :CreateNewFile, 'qa/page/component/web_ide/modal/create_new_file'
end
end
module Project
autoload :Templates, 'qa/page/component/project/templates'
end
end
module Trials
autoload :New, 'qa/page/trials/new'
autoload :Select, 'qa/page/trials/select'
end
module Modal
autoload :DeleteWiki, 'qa/page/modal/delete_wiki'
end
end
##
# Classes describing operations on Git repositories.
#
module Git
autoload :Repository, 'qa/git/repository'
autoload :Location, 'qa/git/location'
end
##
# Classes describing services being part of GitLab and how we can interact
# with these services, like through the shell.
#
module Service
autoload :Shellout, 'qa/service/shellout'
autoload :KubernetesCluster, 'qa/service/kubernetes_cluster'
autoload :Omnibus, 'qa/service/omnibus'
autoload :PraefectManager, 'qa/service/praefect_manager'
module ClusterProvider
autoload :Base, 'qa/service/cluster_provider/base'
autoload :Gcloud, 'qa/service/cluster_provider/gcloud'
autoload :Minikube, 'qa/service/cluster_provider/minikube'
autoload :K3d, 'qa/service/cluster_provider/k3d'
autoload :K3s, 'qa/service/cluster_provider/k3s'
autoload :K3sCilium, 'qa/service/cluster_provider/k3s_cilium'
end
module DockerRun
autoload :Base, 'qa/service/docker_run/base'
autoload :Jenkins, 'qa/service/docker_run/jenkins'
autoload :LDAP, 'qa/service/docker_run/ldap'
autoload :Maven, 'qa/service/docker_run/maven'
autoload :NodeJs, 'qa/service/docker_run/node_js'
autoload :GitlabRunner, 'qa/service/docker_run/gitlab_runner'
autoload :MailHog, 'qa/service/docker_run/mail_hog'
autoload :SamlIdp, 'qa/service/docker_run/saml_idp'
autoload :K3s, 'qa/service/docker_run/k3s'
end
end
##
# Classes that make it possible to execute features tests.
#
module Specs
autoload :Config, 'qa/specs/config'
autoload :Runner, 'qa/specs/runner'
autoload :ParallelRunner, 'qa/specs/parallel_runner'
autoload :LoopRunner, 'qa/specs/loop_runner'
module Helpers
autoload :ContextSelector, 'qa/specs/helpers/context_selector'
autoload :ContextFormatter, 'qa/specs/helpers/context_formatter'
autoload :Quarantine, 'qa/specs/helpers/quarantine'
autoload :QuarantineFormatter, 'qa/specs/helpers/quarantine_formatter'
autoload :RSpec, 'qa/specs/helpers/rspec'
end
end
##
# Classes that describe the structure of vendor/third party application pages
#
module Vendor
module SAMLIdp
module Page
autoload :Base, 'qa/vendor/saml_idp/page/base'
autoload :Login, 'qa/vendor/saml_idp/page/login'
end
end
module Jenkins
module Page
autoload :Base, 'qa/vendor/jenkins/page/base'
autoload :Login, 'qa/vendor/jenkins/page/login'
autoload :Configure, 'qa/vendor/jenkins/page/configure'
autoload :NewCredentials, 'qa/vendor/jenkins/page/new_credentials'
autoload :NewJob, 'qa/vendor/jenkins/page/new_job'
autoload :LastJobConsole, 'qa/vendor/jenkins/page/last_job_console'
autoload :ConfigureJob, 'qa/vendor/jenkins/page/configure_job'
end
end
module Jira
autoload :JiraAPI, 'qa/vendor/jira/jira_api'
end
end
# Classes that provide support to other parts of the framework.
#
module Support
module Page
autoload :Logging, 'qa/support/page/logging'
end
autoload :Api, 'qa/support/api'
autoload :Dates, 'qa/support/dates'
autoload :Repeater, 'qa/support/repeater'
autoload :Run, 'qa/support/run'
autoload :Retrier, 'qa/support/retrier'
autoload :Waiter, 'qa/support/waiter'
autoload :WaitForRequests, 'qa/support/wait_for_requests'
autoload :OTP, 'qa/support/otp'
autoload :SSH, 'qa/support/ssh'
autoload :AllureMetadataFormatter, 'qa/support/allure_metadata_formatter.rb'
end
root = "#{__dir__}/qa"
loader = Zeitwerk::Loader.new
loader.push_dir(root, namespace: QA)
loader.ignore("#{root}/specs/features")
loader.inflector.inflect(
"ce" => "CE",
"ee" => "EE",
"api" => "API",
"ssh" => "SSH",
"ssh_key" => "SSHKey",
"ssh_keys" => "SSHKeys",
"ecdsa" => "ECDSA",
"ed25519" => "ED25519",
"rsa" => "RSA",
"ldap" => "LDAP",
"ldap_tls" => "LDAPTLS",
"ldap_no_tls" => "LDAPNoTLS",
"ldap_no_server" => "LDAPNoServer",
"rspec" => "RSpec",
"web_ide" => "WebIDE",
"ci_cd" => "CiCd",
"repo_by_url" => "RepoByURL",
"oauth" => "OAuth",
"saml_sso_sign_in" => "SamlSSOSignIn",
"saml_sso_sign_up" => "SamlSSOSignUp",
"group_saml" => "GroupSAML",
"instance_saml" => "InstanceSAML",
"saml_sso" => "SamlSSO",
"ldap_sync" => "LDAPSync",
"ip_address" => "IPAddress",
"gpg" => "GPG",
"user_gpg" => "UserGPG",
"smtp" => "SMTP",
"otp" => "OTP",
"jira_api" => "JiraAPI"
)
loader.setup
end
QA::Runtime::Release.extend_autoloads!
......@@ -5,10 +5,6 @@ module QA
module Strategy
extend self
def extend_autoloads!
# noop
end
def perform_before_hooks
# The login page could take some time to load the first time it is visited.
# We visit the login page and wait for it to properly load only once before the tests.
......
# frozen_string_literal: true
module QA
##
# GitLab EE extensions
#
module EE
module Runtime
autoload :Env, 'qa/ee/runtime/env'
autoload :Geo, 'qa/ee/runtime/geo'
autoload :Saml, 'qa/ee/runtime/saml'
end
module Page
autoload :OperationsDashboard, 'qa/ee/page/operations_dashboard'
module Component
autoload :LicenseManagement, 'qa/ee/page/component/license_management'
autoload :SecureReport, 'qa/ee/page/component/secure_report'
module IssueBoard
autoload :Show, 'qa/ee/page/component/issue_board/show'
end
module WebIDE
autoload :WebTerminalPanel, 'qa/ee/page/component/web_ide/web_terminal_panel'
end
end
module Dashboard
autoload :Projects, 'qa/ee/page/dashboard/projects'
end
module Group
autoload :Menu, 'qa/ee/page/group/menu'
autoload :SamlSSOSignIn, 'qa/ee/page/group/saml_sso_sign_in'
autoload :SamlSSOSignUp, 'qa/ee/page/group/saml_sso_sign_up'
autoload :Members, 'qa/ee/page/group/members'
autoload :ContributionAnalytics, 'qa/ee/page/group/contribution_analytics'
module Iteration
autoload :Index, 'qa/ee/page/group/iteration/index'
autoload :New, 'qa/ee/page/group/iteration/new'
autoload :Show, 'qa/ee/page/group/iteration/show'
end
module Settings
autoload :SamlSSO, 'qa/ee/page/group/settings/saml_sso'
autoload :LDAPSync, 'qa/ee/page/group/settings/ldap_sync'
autoload :General, 'qa/ee/page/group/settings/general'
end
module Wiki
autoload :Show, 'qa/ee/page/group/wiki/show'
autoload :Edit, 'qa/ee/page/group/wiki/edit'
end
end
module File
autoload :Show, 'qa/ee/page/file/show'
end
module Main
autoload :Banner, 'qa/ee/page/main/banner'
end
module Registration
autoload :Welcome, 'qa/ee/page/registration/welcome'
end
module Admin
autoload :Menu, 'qa/ee/page/admin/menu'
autoload :License, 'qa/ee/page/admin/license'
module Geo
module Nodes
autoload :Show, 'qa/ee/page/admin/geo/nodes/show'
autoload :New, 'qa/ee/page/admin/geo/nodes/new'
end
end
module Monitoring
autoload :AuditLog, 'qa/ee/page/admin/monitoring/audit_log.rb'
end
module Settings
autoload :Templates, 'qa/ee/page/admin/settings/templates'
autoload :Preferences, 'qa/ee/page/admin/settings/preferences'
module Component
autoload :Email, 'qa/ee/page/admin/settings/component/email'
autoload :Elasticsearch, 'qa/ee/page/admin/settings/component/elasticsearch'
end
end
module Overview
module Groups
autoload :Edit, 'qa/ee/page/admin/overview/groups/edit'
end
end
end
module Milestone
autoload :Show, 'qa/ee/page/milestone/show'
end
module Profile
autoload :Menu, 'qa/ee/page/profile/menu'
end
module Project
autoload :New, 'qa/ee/page/project/new'
autoload :Show, 'qa/ee/page/project/show'
autoload :Menu, 'qa/ee/page/project/menu'
module SubMenus
autoload :SecurityCompliance, 'qa/ee/page/project/sub_menus/security_compliance'
autoload :Repository, 'qa/ee/page/project/sub_menus/repository'
autoload :Settings, 'qa/ee/page/project/sub_menus/settings'
autoload :Analytics, 'qa/ee/page/project/sub_menus/analytics'
autoload :LicenseCompliance, 'qa/ee/page/project/sub_menus/license_compliance'
end
module Issue
autoload :Index, 'qa/ee/page/project/issue/index'
autoload :Show, 'qa/ee/page/project/issue/show'
end
module Wiki
autoload :Show, 'qa/ee/page/project/wiki/show'
end
module Settings
autoload :ProtectedBranches, 'qa/ee/page/project/settings/protected_branches'
autoload :Main, 'qa/ee/page/project/settings/main'
autoload :MirroringRepositories, 'qa/ee/page/project/settings/mirroring_repositories'
autoload :ProtectedTags, 'qa/ee/page/project/settings/protected_tags'
autoload :MergeRequest, 'qa/ee/page/project/settings/merge_request'
autoload :MergeRequestApprovals, 'qa/ee/page/project/settings/merge_request_approvals'
autoload :Integrations, 'qa/ee/page/project/settings/integrations'
autoload :Repository, 'qa/ee/page/project/settings/repository'
autoload :PushRules, 'qa/ee/page/project/settings/push_rules'
autoload :IssueTemplateDefault, 'qa/ee/page/project/settings/issue_template_default.rb'
autoload :CICD, 'qa/ee/page/project/settings/ci_cd'
autoload :PipelineSubscriptions, 'qa/ee/page/project/settings/pipeline_subscriptions'
end
module Monitor
module Metrics
autoload :Show, 'qa/ee/page/project/monitor/metrics/show'
end
end
module Pipeline
autoload :Show, 'qa/ee/page/project/pipeline/show'
autoload :Index, 'qa/ee/page/project/pipeline/index'
end
module Secure
autoload :Show, 'qa/ee/page/project/secure/show'
autoload :DependencyList, 'qa/ee/page/project/secure/dependency_list'
autoload :SecurityDashboard, 'qa/ee/page/project/secure/security_dashboard'
autoload :VulnerabilityDetails, 'qa/ee/page/project/secure/vulnerability_details'
autoload :LicenseCompliance, 'qa/ee/page/project/secure/license_compliance'
autoload :ConfigurationForm, 'qa/ee/page/project/secure/configuration_form'
end
module PathLocks
autoload :Index, 'qa/ee/page/project/path_locks/index'
end
module Packages
autoload :Index, 'qa/ee/page/project/packages/index'
end
module Snippet
autoload :Index, 'qa/ee/page/project/snippet/index'
end
module Job
autoload :Show, 'qa/ee/page/project/job/show'
end
module ThreatMonitoring
autoload :Index, 'qa/ee/page/project/threat_monitoring/index'
autoload :AlertsList, 'qa/ee/page/project/threat_monitoring/alerts_list'
end
end
module MergeRequest
autoload :New, 'qa/ee/page/merge_request/new'
autoload :Show, 'qa/ee/page/merge_request/show'
end
module Group
autoload :IssuesAnalytics, 'qa/ee/page/group/issues_analytics'
autoload :Roadmap, 'qa/ee/page/group/roadmap'
module Epic
autoload :Index, 'qa/ee/page/group/epic/index'
autoload :New, 'qa/ee/page/group/epic/new'
autoload :Show, 'qa/ee/page/group/epic/show'
end
module Secure
autoload :Show, 'qa/ee/page/group/secure/show'
end
end
module Insights
autoload :Show, 'qa/ee/page/insights/show'
end
end
module Resource
autoload :License, 'qa/ee/resource/license'
autoload :Epic, 'qa/ee/resource/epic'
autoload :GroupIteration, 'qa/ee/resource/group_iteration'
autoload :ImportRepoWithCiCd, 'qa/ee/resource/import_repo_with_ci_cd'
autoload :PipelineSubscriptions, 'qa/ee/resource/pipeline_subscriptions'
autoload :GroupBase, 'qa/ee/resource/group_base'
module Board
autoload :BaseBoard, 'qa/ee/resource/board/base_board'
autoload :GroupBoard, 'qa/ee/resource/board/group_board'
autoload :ProjectBoard, 'qa/ee/resource/board/project_board'
module BoardList
module Group
autoload :BoardList, 'qa/ee/resource/board/board_list/group/board_list'
end
module Project
autoload :BaseBoardList, 'qa/ee/resource/board/board_list/project/base_board_list'
autoload :AssigneeBoardList, 'qa/ee/resource/board/board_list/project/assignee_board_list'
autoload :LabelBoardList, 'qa/ee/resource/board/board_list/project/label_board_list'
autoload :MilestoneBoardList, 'qa/ee/resource/board/board_list/project/milestone_board_list'
end
end
end
module Geo
autoload :Node, 'qa/ee/resource/geo/node'
end
module Settings
autoload :Elasticsearch, 'qa/ee/resource/settings/elasticsearch'
end
end
module Scenario
module Test
autoload :Geo, 'qa/ee/scenario/test/geo'
module Integration
autoload :GroupSAML, 'qa/ee/scenario/test/integration/group_saml'
autoload :Elasticsearch, 'qa/ee/scenario/test/integration/elasticsearch'
end
module Sanity
autoload :Selectors, 'qa/ee/scenario/test/sanity/selectors'
end
end
end
end
end
......@@ -5,7 +5,7 @@ module QA
module Page
module Project
module Settings
module CICD
module CiCd
extend QA::Page::PageConcern
def self.prepended(base)
......
......@@ -9,7 +9,7 @@ module QA
def fabricate!
QA::Page::Project::Menu.perform(&:go_to_ci_cd_settings)
QA::Page::Project::Settings::CICD.perform do |setting|
QA::Page::Project::Settings::CiCd.perform do |setting|
setting.expand_pipeline_subscriptions do |page|
page.subscribe(project_path)
end
......
......@@ -5,11 +5,6 @@ module QA
module Strategy
extend self
def extend_autoloads!
require 'qa/ce/strategy'
require 'qa/ee'
end
def perform_before_hooks
# Without a license, perform the CE before hooks only.
unless ENV['EE_LICENSE']
......
......@@ -67,7 +67,7 @@ module QA
end
def login_to_idp_if_required(username, password)
Vendor::SAMLIdp::Page::Login.perform { |login_page| login_page.login_if_required(username, password) }
Vendor::SamlIdp::Page::Login.perform { |login_page| login_page.login_if_required(username, password) }
end
end
end
......
......@@ -4,7 +4,6 @@ require 'cgi'
require 'uri'
require 'fileutils'
require 'tmpdir'
require 'securerandom'
module QA
module Git
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Page
module Project
......
......@@ -4,7 +4,7 @@ module QA
module Page
module Project
module Settings
class CICD < Page::Base
class CiCd < Page::Base
include QA::Page::Settings::Common
view 'app/views/projects/settings/ci_cd/show.html.haml' do
......@@ -43,4 +43,4 @@ module QA
end
end
QA::Page::Project::Settings::CICD.prepend_mod_with("Page::Project::Settings::CICD", namespace: QA)
QA::Page::Project::Settings::CiCd.prepend_mod_with("Page::Project::Settings::CiCd", namespace: QA)
# frozen_string_literal: true
require 'pathname'
module QA
module Page
class View
......
......@@ -55,7 +55,7 @@ module QA
end
end
include Support::Api
include Support::API
attr_writer :api_resource, :api_response
def api_put(body = api_put_body)
......
......@@ -22,7 +22,7 @@ module QA
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CICD.perform do |setting|
Page::Project::Settings::CiCd.perform do |setting|
setting.expand_ci_variables do |page|
page.click_add_variable
page.fill_variable(key, value, masked)
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class Issue < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
module KubernetesCluster
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
# Base label class for GroupLabel and ProjectLabel
......
......@@ -12,7 +12,7 @@ module QA
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}])
response = post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
response.code == QA::Support::Api::HTTP_STATUS_CREATED
response.code == QA::Support::API::HTTP_STATUS_CREATED
end
end
......@@ -31,7 +31,7 @@ module QA
QA::Runtime::Logger.debug(%Q[Sharing #{self.class.name} with #{group.name}])
response = post Runtime::API::Request.new(api_client, api_share_path).url, { group_id: group.id, group_access: access_level }
response.code == QA::Support::Api::HTTP_STATUS_CREATED
response.code == QA::Support::API::HTTP_STATUS_CREATED
end
end
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class MergeRequest < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class MergeRequestFromFork < MergeRequest
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class Package < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class Project < Base
......
# frozen_string_literal: true
require 'octokit'
module QA
module Resource
class ProjectImportedFromGithub < Resource::Project
......@@ -68,7 +66,7 @@ module QA
response = post(request_url(api_trigger_mirror_pull_path), nil)
Runtime::Logger.info "Mirror pull request response: #{response}"
response.code == Support::Api::HTTP_STATUS_OK
response.code == Support::API::HTTP_STATUS_OK
end
end
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class ProjectImportedFromURL < Resource::Project
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class ProjectIssueNote < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class ProtectedBranch < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class RegistryRepository < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
module Repository
......
# frozen_string_literal: true
require 'pathname'
require 'securerandom'
module QA
module Resource
module Repository
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class Runner < Base
......
......@@ -72,7 +72,7 @@ module QA
Support::Retrier.retry_until(max_duration: QA::EE::Runtime::Geo.max_db_replication_time, sleep_interval: 3) do
response = get Runtime::API::Request.new(api_client, api_get_path).url
response.code == QA::Support::Api::HTTP_STATUS_OK &&
response.code == QA::Support::API::HTTP_STATUS_OK &&
parse_body(response)[:title].include?(title)
end
end
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
class User < Base
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Resource
module Wiki
......
......@@ -12,8 +12,6 @@ module QA
def configure!
return unless Env.generate_allure_report?
require 'allure-rspec'
configure_allure
configure_attachments
configure_rspec
......
......@@ -5,7 +5,7 @@ module QA
module API
module RepositoryStorageMoves
extend self
extend Support::Api
extend Support::API
RepositoryStorageMovesError = Class.new(RuntimeError)
......
......@@ -4,7 +4,7 @@ module QA
module Runtime
class ApplicationSettings
class << self
include Support::Api
include Support::API
APPLICATION_SETTINGS_PATH = '/application/settings'
......@@ -18,7 +18,7 @@ module QA
QA::Runtime::Logger.info("Setting application settings: #{application_settings}")
r = put(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url, **application_settings)
raise "Couldn't set application settings #{application_settings.inspect}" unless r.code == QA::Support::Api::HTTP_STATUS_OK
raise "Couldn't set application settings #{application_settings.inspect}" unless r.code == QA::Support::API::HTTP_STATUS_OK
end
def get_application_settings
......
......@@ -4,7 +4,6 @@ require 'rspec/core'
require 'rspec/expectations'
require 'capybara/rspec'
require 'capybara-screenshot/rspec'
require 'selenium-webdriver'
require 'webdrivers/chromedriver'
require 'webdrivers/geckodriver'
......
# frozen_string_literal: true
require 'active_support/deprecation'
require 'gitlab/qa'
require 'uri'
module QA
......
......@@ -8,7 +8,7 @@ module QA
class << self
# Documentation: https://docs.gitlab.com/ee/api/features.html
include Support::Api
include Support::API
SetFeatureError = Class.new(RuntimeError)
AuthorizationError = Class.new(RuntimeError)
......@@ -17,7 +17,7 @@ module QA
def remove(key)
request = Runtime::API::Request.new(api_client, "/features/#{key}")
response = delete(request.url)
unless response.code == QA::Support::Api::HTTP_STATUS_NO_CONTENT
unless response.code == QA::Support::API::HTTP_STATUS_NO_CONTENT
raise SetFeatureError, "Deleting feature flag #{key} failed with `#{response}`."
end
end
......@@ -100,7 +100,7 @@ module QA
scopes[:user] = scopes[:user].username if scopes.key?(:user)
request = Runtime::API::Request.new(api_client, "/features/#{key}")
response = post(request.url, scopes.merge({ value: value }))
unless response.code == QA::Support::Api::HTTP_STATUS_CREATED
unless response.code == QA::Support::API::HTTP_STATUS_CREATED
raise SetFeatureError, "Setting feature flag #{key} to #{value} failed with `#{response}`."
end
end
......
......@@ -5,7 +5,7 @@ require 'tmpdir'
module QA
module Runtime
module Fixtures
include Support::Api
include Support::API
TemplateNotFoundError = Class.new(RuntimeError)
......
......@@ -4,7 +4,7 @@ require 'socket'
module QA
module Runtime
module IPAddress
include Support::Api
include Support::API
HostUnreachableError = Class.new(StandardError)
LOOPBACK_ADDRESS = '127.0.0.1'
......@@ -15,7 +15,7 @@ module QA
# we use the public facing IP address
ip_address = if Env.running_in_ci? && !URI.parse(Scenario.gitlab_address).host.include?('test')
response = get(PUBLIC_IP_ADDRESS_API)
raise HostUnreachableError, "#{PUBLIC_IP_ADDRESS_API} is unreachable" unless response.code == Support::Api::HTTP_STATUS_OK
raise HostUnreachableError, "#{PUBLIC_IP_ADDRESS_API} is unreachable" unless response.code == Support::API::HTTP_STATUS_OK
response.body
elsif page.current_host.include?('localhost')
......
......@@ -13,10 +13,6 @@ module QA
# CE to EE.
#
class Release
def initialize
require "qa/#{version.downcase}/strategy"
end
def version
@version ||= ::File.directory?("#{__dir__}/../ee") ? :EE : :CE
end
......
# frozen_string_literal: true
require 'securerandom'
module QA
module Runtime
module Search
extend self
extend Support::Api
extend Support::API
RETRY_MAX_ITERATION = 10
RETRY_SLEEP_INTERVAL = 12
......
# frozen_string_literal: true
require 'resolv'
require 'securerandom'
module QA
module Service
......
# frozen_string_literal: true
require 'securerandom'
require 'mkmf'
require 'pathname'
module QA
module Service
......
# frozen_string_literal: true
require 'octokit'
# rubocop:disable Rails/Pluck
module QA
# Only executes in custom job/pipeline
......
......@@ -4,7 +4,7 @@ require 'airborne'
module QA
RSpec.describe 'Plan' do
include Support::Api
include Support::API
describe 'Issue' do
let(:issue) do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Create' do
describe 'Default branch name instance setting', :requires_admin, :skip_live_env do
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
module QA
RSpec.describe 'API basics' do
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
require 'digest'
module QA
RSpec.describe 'Create' do
describe 'Compare archives of different user projects with the same name and check they\'re different' do
include Support::Api
include Support::API
let(:project_name) { "project-archive-download-#{SecureRandom.hex(8)}" }
let(:archive_types) { %w(tar.gz tar.bz2 tar zip) }
......
......@@ -4,7 +4,7 @@ require 'airborne'
module QA
RSpec.describe 'Package', only: { subdomain: %i[staging pre] } do
include Support::Api
include Support::API
describe 'Container Registry' do
let(:api_client) { Runtime::API::Client.new(:gitlab) }
......
......@@ -3,12 +3,15 @@
module QA
RSpec.describe 'Manage', :orchestrated, :instance_saml do
describe 'Instance wide SAML SSO' do
it 'user logs in to gitlab with SAML SSO', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671' do
it(
'user logs in to gitlab with SAML SSO',
testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671'
) do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_with_saml)
Vendor::SAMLIdp::Page::Login.perform do |login_page|
Vendor::SamlIdp::Page::Login.perform do |login_page|
login_page.login('user1', 'user1pass')
end
......
# frozen_string_literal: true
require 'nokogiri'
module QA
RSpec.describe 'Manage', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/212145', type: :stale } do
describe 'Check for broken images', :requires_admin do
......
......@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Plan', :orchestrated, :smtp, :requires_admin do
describe 'Email Notification' do
include Support::Api
include Support::API
let!(:user) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Plan', :reliable do
describe 'Issues list' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Create', :requires_admin, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/195179', type: :flaky } do
......
......@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
include Support::Api
include Support::API
describe 'Jira integration', :jira, :orchestrated, :requires_admin do
let(:jira_project_key) { 'JITP' }
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Create' do
describe 'File templates' do
......
......@@ -9,7 +9,7 @@ module QA
# tests are run in parallel).
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705
include Support::Api
include Support::API
before(:context) do
@project = Resource::Project.fabricate_via_api! do |p|
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Create' do
describe 'Web IDE file templates' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
describe 'Pipeline with protected variable' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
describe 'Include multiple files from a project' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
context 'When pipeline is blocked' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
context 'When job is configured to only run on merge_request_events' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
describe 'Pass dotenv variables to downstream via bridge' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
describe "Trigger child pipeline with 'when:manual'" do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner do
describe 'Trigger matrix' do
......
......@@ -21,7 +21,7 @@ module QA
runner.project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CICD.perform do |settings|
Page::Project::Settings::CiCd.perform do |settings|
sleep 5 # Runner should register within 5 seconds
settings.expand_runners_settings do |page|
......
......@@ -49,7 +49,7 @@ module QA
def configure_code_coverage(coverage_tool_pattern)
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CICD.perform do |settings|
Page::Project::Settings::CiCd.perform do |settings|
settings.expand_general_pipelines do |coverage|
coverage.configure_coverage_regex(coverage_tool_pattern)
end
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'Composer Repository' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
describe 'Maven Repository' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'NuGet Repository' do
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Configure' do
let(:project) do
......@@ -102,7 +100,7 @@ module QA
project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CICD.perform(&:expand_auto_devops)
Page::Project::Settings::CiCd.perform(&:expand_auto_devops)
Page::Project::Settings::AutoDevops.perform(&:enable_autodevops)
# Create AutoDevOps repo
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
module QA
RSpec.describe 'Enablement:Search' do
......@@ -52,7 +51,7 @@ module QA
def expect_search_to_find_project(search_term)
QA::Support::Retrier.retry_on_exception(max_attempts: Runtime::Search::RETRY_MAX_ITERATION, sleep_interval: Runtime::Search::RETRY_SLEEP_INTERVAL) do
get Runtime::Search.create_search_request(api_client, 'projects', search_term).url
expect_status(QA::Support::Api::HTTP_STATUS_OK)
expect_status(QA::Support::API::HTTP_STATUS_OK)
raise 'Empty search result returned' if json_body.empty?
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
module QA
RSpec.describe 'Enablement:Search' do
......@@ -54,7 +53,7 @@ module QA
it 'does not find a blob as an non-member user', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/746' do
QA::Support::Retrier.retry_on_exception(max_attempts: Runtime::Search::RETRY_MAX_ITERATION, sleep_interval: Runtime::Search::RETRY_SLEEP_INTERVAL) do
get Runtime::Search.create_search_request(non_member_api_client, 'blobs', project_file_content).url
expect_status(QA::Support::Api::HTTP_STATUS_OK)
expect_status(QA::Support::API::HTTP_STATUS_OK)
expect(json_body).to be_empty
end
end
......@@ -65,7 +64,7 @@ module QA
def successful_search(api_client)
QA::Support::Retrier.retry_on_exception(max_attempts: Runtime::Search::RETRY_MAX_ITERATION, sleep_interval: Runtime::Search::RETRY_SLEEP_INTERVAL) do
get Runtime::Search.create_search_request(api_client, 'blobs', project_file_content).url
expect_status(QA::Support::Api::HTTP_STATUS_OK)
expect_status(QA::Support::API::HTTP_STATUS_OK)
raise 'Empty search result returned' if json_body.empty?
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
module QA
RSpec.describe 'Enablement:Search' do
......@@ -44,7 +43,7 @@ module QA
start_time = Time.now
while (Time.now - start_time) / 60 < p1_threshold
get Runtime::Search.create_search_request(api_client, 'blobs', project_file_content).url
expect_status(QA::Support::Api::HTTP_STATUS_OK)
expect_status(QA::Support::API::HTTP_STATUS_OK)
if !json_body.empty? && json_body[0][:data].match(project_file_content) && json_body[0][:project_id].equal?(project.id)
break
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Manage' do
include Support::Api
include Support::API
let(:api_client) { Runtime::API::Client.new(:gitlab) }
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Manage' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Manage' do
describe 'Group file templates', :requires_admin do
include Support::Api
include Support::API
templates = [
{
......
......@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Manage', :orchestrated, :ldap_tls, :ldap_no_tls, :requires_admin do
describe 'LDAP Group sync' do
include Support::Api
include Support::API
let(:group) do
Resource::Group.fabricate_via_api! do |resource|
......
......@@ -4,7 +4,7 @@ module QA
# TODO: Remove :requires_admin meta when the `Runtime::Feature.enable` method call is removed
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Enforced SSO' do
include Support::Api
include Support::API
let!(:group) do
Resource::Sandbox.fabricate_via_api! do |sandbox_group|
......
......@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Enforced SSO' do
include Support::Api
include Support::API
let!(:group) do
Resource::Sandbox.fabricate_via_api! do |sandbox_group|
......
......@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Non enforced SSO' do
include Support::Api
include Support::API
let(:user) { Resource::User.fabricate_via_api! }
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Manage' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
# Issue to enable this test in live environments: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/614
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Manage' do
describe 'Project templates' do
include Support::Api
include Support::API
before(:all) do
@files = [
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Plan', :reliable do
describe 'Custom email', :requires_admin do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Plan', :reliable do
describe 'Group issue boards' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Plan', :reliable do
describe 'Group issue boards' do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify' do
describe 'Cancelling merge request in merge train', :runner, :requires_admin do
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :docker, :runner do
describe 'In merge trains' do
......
# frozen_string_literal: true
require 'octokit'
require 'faker'
require 'base64'
module QA
......
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner, :reliable do
describe 'Pipelines for merged results and merge trains' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Verify', :runner, :transient do
describe 'Merge trains transient bugs' do
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Release' do
describe 'Multi-project pipelines' do
......
# frozen_string_literal: true
require 'pathname'
require_relative '../../../browser_ui/8_monitor/cluster_with_prometheus'
module QA
......
# frozen_string_literal: true
require 'securerandom'
module QA
RSpec.describe 'Fulfillment', :requires_admin, only: { subdomain: :staging } do
describe 'Purchase' do
......
......@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Geo', :orchestrated, :geo do
describe 'GitLab Geo project deletion replication' do
include Support::Api
include Support::API
deleted_project_name = nil
deleted_project_id = nil
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
describe 'Security Reports in a Merge Request' do
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
describe 'Enable SAST from UI' do
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
let(:approved_license_name) { "MIT License" }
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
describe 'License merge request widget' do
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
let(:number_of_dependencies_in_fixture) { 9 }
......
# frozen_string_literal: true
require 'pathname'
module QA
RSpec.describe 'Secure', :runner do
describe 'Security Reports in a Merge Request' do
......
# frozen_string_literal: true
require 'knapsack'
require 'rspec/core'
require 'rspec/expectations'
......
# frozen_string_literal: true
require 'rest-client'
module QA
module Support
module Api
module API
HTTP_STATUS_OK = 200
HTTP_STATUS_CREATED = 201
HTTP_STATUS_NO_CONTENT = 204
......
# frozen_string_literal: true
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module QA
module Support
module Helpers
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__'
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
# Prevent secrets from leaking in CI
allow(ENV).to receive(:inspect).and_return([])
add_stubbed_value(STUBBED_KEY, true)
end
end
end
end
end
# frozen_string_literal: true
# Rspec matcher with build in retry logic
#
# USAGE:
#
# Basic
# expect { Something.that.takes.time.to_appear }.to eventually_eq(expected_result)
# expect { Something.that.takes.time.to_appear }.not_to eventually_eq(expected_result)
#
# With duration and attempts override
# expect { Something.that.takes.time.to_appear }.to(
# eventually_eq(expected_result).within(max_duration: 10, max_attempts: 5)
# )
module QA
module Support
module Matchers
module EventuallyMatcher
%w[
eq
be
include
be_truthy
be_falsey
be_empty
].each do |op|
RSpec::Matchers.define(:"eventually_#{op}") do |*expected|
chain(:within) do |kwargs = {}|
@retry_args = kwargs
@retry_args[:sleep_interval] = 0.5 unless @retry_args[:sleep_interval]
end
def supports_block_expectations?
true
end
match { |actual| wait_and_check(actual, :default_expectation) }
match_when_negated { |actual| wait_and_check(actual, :when_negated_expectation) }
description do
"eventually #{operator_msg} #{expected.inspect}"
end
failure_message do
"#{e}:\nexpected to #{description}, last attempt was #{@result.nil? ? 'nil' : @result}"
end
failure_message_when_negated do
"#{e}:\nexpected not to #{description}, last attempt was #{@result.nil? ? 'nil' : @result}"
end
# Execute rspec expectation within retrier
#
# @param [Proc] actual
# @param [Symbol] expectation_name
# @return [Boolean]
def wait_and_check(actual, expectation_name)
attempt = 0
QA::Runtime::Logger.debug("Running eventually matcher with '#{operator_msg}' operator")
QA::Support::Retrier.retry_until(**@retry_args) do
QA::Runtime::Logger.debug("evaluating expectation, attempt: #{attempt += 1}")
public_send(expectation_name, actual)
rescue RSpec::Expectations::ExpectationNotMetError, QA::Resource::ApiFabricator::ResourceNotFoundError
false
end
rescue QA::Support::Repeater::RetriesExceededError, QA::Support::Repeater::WaitExceededError => e
@e = e
false
end
# Execute rspec expectation
#
# @param [Proc] actual
# @return [void]
def default_expectation(actual)
expect(result(&actual)).to public_send(*expectation_args)
end
# Execute negated rspec expectation
#
# @param [Proc] actual
# @return [void]
def when_negated_expectation(actual)
expect(result(&actual)).not_to public_send(*expectation_args)
end
# Result of actual block
#
# @return [Object]
def result
@result = yield
end
# Error message placeholder to indicate waiter did not fail properly
# This message should not appear under normal circumstances since it should
# always be assigned from repeater
#
# @return [String]
def e
@e ||= 'Waiter did not fail!'
end
# Operator message
#
# @return [String]
def operator_msg
case operator
when 'eq' then 'equal'
else operator
end
end
# Expect operator
#
# @return [String]
def operator
@operator ||= name.to_s.match(/eventually_(.+?)$/).to_a[1].to_s
end
# Expectation args
#
# @return [String, Array]
def expectation_args
if operator.include?('truthy') || operator.include?('falsey') || operator.include?('empty')
operator
elsif operator == "include" && expected.is_a?(Array)
[operator, *expected]
else
[operator, expected]
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Support
module Matchers
module HaveMatcher
PREDICATE_TARGETS = %w[
element
file_content
assignee
child_pipeline
content
design
file
issue
job
package
pipeline
related_issue_item
snippet_description
tag
].each do |predicate|
RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs|
match do |page_object|
page_object.public_send("has_#{predicate}?", *args, **kwargs)
end
match_when_negated do |page_object|
page_object.public_send("has_no_#{predicate}?", *args, **kwargs)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Support
module Matchers
class HaveText
def initialize(expected_text, **kwargs)
@expected_text = expected_text
@kwargs = kwargs
end
def matches?(actual)
@actual = wrap(actual)
@actual.has_text?(@expected_text, **@kwargs)
end
def does_not_match?(actual)
@actual = wrap(actual)
@actual.has_no_text?(@expected_text, **@kwargs)
end
def failure_message
"expected to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
end
def failure_message_when_negated
"expected not to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
end
def normalized_actual_text
@actual.text.gsub(/\s+/, " ")
end
# From https://github.com/teamcapybara/capybara/blob/fe5940c6afbfe32152df936ce03ad1371ae05354/lib/capybara/rspec/matchers/base.rb#L66
def wrap(actual)
actual = actual.to_capybara_node if actual.respond_to?(:to_capybara_node)
@context_el = if actual.respond_to?(:has_selector?)
actual
else
Capybara.string(actual.to_s)
end
end
end
def have_text(text, **kwargs) # rubocop:disable Naming/PredicateName
HaveText.new(text, **kwargs)
end
alias_method :have_content, :have_text
end
end
end
......@@ -10,7 +10,7 @@ require_relative '../../qa'
module QA
module Tools
class DeleteProjects
include Support::Api
include Support::API
def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS']
......
......@@ -10,7 +10,7 @@ require_relative '../../qa'
module QA
module Tools
class DeleteSubgroups
include Support::Api
include Support::API
def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
......
......@@ -15,7 +15,7 @@ require_relative '../../qa'
module QA
module Tools
class DeleteTestSSHKeys
include Support::Api
include Support::API
ITEMS_PER_PAGE = '100'
......
# frozen_string_literal: true
require 'securerandom'
require 'faker'
require 'yaml'
require_relative '../../qa'
# This script generates testdata for Performance Testing.
......@@ -12,7 +10,7 @@ require_relative '../../qa'
module QA
module Tools
class GeneratePerfTestdata
include Support::Api
include Support::API
def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
......
......@@ -5,7 +5,7 @@ module QA
module Jira
class JiraAPI
include Scenario::Actable
include Support::Api
include Support::API
def base_url
host = QA::Runtime::Env.jira_hostname || 'localhost'
......
......@@ -2,7 +2,7 @@
module QA
module Vendor
module SAMLIdp
module SamlIdp
module Page
class Base
include Capybara::DSL
......
......@@ -4,7 +4,7 @@ require 'capybara/dsl'
module QA
module Vendor
module SAMLIdp
module SamlIdp
module Page
class Login < Page::Base
def login(username, password)
......
# frozen_string_literal: true
RSpec.describe QA::Git::Repository do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
shared_context 'unresolvable git directory' do
let(:repo_uri) { 'http://foo/bar.git' }
......
# frozen_string_literal: true
require 'capybara/dsl'
require 'logger'
RSpec.describe QA::Support::Page::Logging do
let(:page) { double.as_null_object }
......
# frozen_string_literal: true
RSpec.describe QA::Resource::Base do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
let(:resource) { spy('resource') }
let(:location) { 'http://location' }
......
# frozen_string_literal: true
RSpec.describe QA::Runtime::API::Client do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
describe 'initialization' do
it 'defaults to :gitlab address' do
......
# frozen_string_literal: true
RSpec.describe QA::Runtime::Env do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
shared_examples 'boolean method' do |**kwargs|
it_behaves_like 'boolean method with parameter', kwargs
......
# frozen_string_literal: true
RSpec.describe QA::Runtime::Namespace do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
describe '.name' do
context 'when CACHE_NAMESPACE_NAME is not defined' do
......
......@@ -30,23 +30,4 @@ RSpec.describe QA::Runtime::Release do
end
end
end
context 'when release version does not have extension strategy' do
before do
allow_any_instance_of(described_class)
.to receive(:version).and_return('something')
end
describe '#strategy' do
it 'raises error' do
expect { subject.strategy }.to raise_error(LoadError)
end
end
describe 'delegated class methods' do
it 'raises error' do
expect { described_class.some_method(2, 3) }.to raise_error(LoadError)
end
end
end
end
# frozen_string_literal: true
require_relative '../qa'
require 'rspec/retry'
require 'rspec-parameterized'
require 'securerandom'
require 'pathname'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank'
require_relative 'qa_deprecation_toolkit_env'
QaDeprecationToolkitEnv.configure!
if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind
end
Knapsack::Adapters::RSpecAdapter.bind if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
QA::Runtime::Browser.configure!
QA::Runtime::AllureReport.configure!
......@@ -24,7 +22,8 @@ Dir[::File.join(__dir__, "support/shared_contexts/*.rb")].sort.each { |f| requir
Dir[::File.join(__dir__, "support/shared_examples/*.rb")].sort.each { |f| require f }
RSpec.configure do |config|
config.include ::Matchers
config.include QA::Support::Matchers::EventuallyMatcher
config.include QA::Support::Matchers::HaveMatcher
config.add_formatter QA::Specs::Helpers::ContextFormatter
config.add_formatter QA::Specs::Helpers::QuarantineFormatter
......
# frozen_string_literal: true
require 'allure-rspec'
describe QA::Runtime::AllureReport do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
let(:rspec_config) { double('RSpec::Core::Configuration', 'add_formatter': nil, after: nil) }
......
......@@ -3,7 +3,7 @@
require 'rspec/core/sandbox'
RSpec.describe QA::Specs::Helpers::ContextSelector do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
include QA::Specs::Helpers::RSpec
around do |ex|
......
......@@ -3,7 +3,7 @@
require 'rspec/core/sandbox'
RSpec.describe QA::Specs::Helpers::Quarantine do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
include QA::Specs::Helpers::RSpec
around do |ex|
......
# frozen_string_literal: true
RSpec.describe QA::Specs::ParallelRunner do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
before do
allow(QA::Runtime::Scenario).to receive(:attributes).and_return(parallel: true)
......
# frozen_string_literal: true
describe QA::Support::AllureMetadataFormatter do
include Helpers::StubENV
include QA::Support::Helpers::StubEnv
let(:formatter) { described_class.new(StringIO.new) }
......
# frozen_string_literal: true
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module Helpers
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__'
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
# Prevent secrets from leaking in CI
allow(ENV).to receive(:inspect).and_return([])
add_stubbed_value(STUBBED_KEY, true)
end
end
end
# frozen_string_literal: true
# Rspec matcher with build in retry logic
#
# USAGE:
#
# Basic
# expect { Something.that.takes.time.to_appear }.to eventually_eq(expected_result)
# expect { Something.that.takes.time.to_appear }.not_to eventually_eq(expected_result)
#
# With duration and attempts override
# expect { Something.that.takes.time.to_appear }.to eventually_eq(expected_result).within(max_duration: 10, max_attempts: 5)
module Matchers
%w[
eq
be
include
be_truthy
be_falsey
be_empty
].each do |op|
RSpec::Matchers.define(:"eventually_#{op}") do |*expected|
chain(:within) do |kwargs = {}|
@retry_args = kwargs
@retry_args[:sleep_interval] = 0.5 unless @retry_args[:sleep_interval]
end
def supports_block_expectations?
true
end
match { |actual| wait_and_check(actual, :default_expectation) }
match_when_negated { |actual| wait_and_check(actual, :when_negated_expectation) }
description do
"eventually #{operator_msg} #{expected.inspect}"
end
failure_message do
"#{e}:\nexpected to #{description}, last attempt was #{@result.nil? ? 'nil' : @result}"
end
failure_message_when_negated do
"#{e}:\nexpected not to #{description}, last attempt was #{@result.nil? ? 'nil' : @result}"
end
# Execute rspec expectation within retrier
#
# @param [Proc] actual
# @param [Symbol] expectation_name
# @return [Boolean]
def wait_and_check(actual, expectation_name)
attempt = 0
QA::Runtime::Logger.debug("Running eventually matcher with '#{operator_msg}' operator")
QA::Support::Retrier.retry_until(**@retry_args) do
QA::Runtime::Logger.debug("evaluating expectation, attempt: #{attempt += 1}")
public_send(expectation_name, actual)
rescue RSpec::Expectations::ExpectationNotMetError, QA::Resource::ApiFabricator::ResourceNotFoundError
false
end
rescue QA::Support::Repeater::RetriesExceededError, QA::Support::Repeater::WaitExceededError => e
@e = e
false
end
# Execute rspec expectation
#
# @param [Proc] actual
# @return [void]
def default_expectation(actual)
expect(result(&actual)).to public_send(*expectation_args)
end
# Execute negated rspec expectation
#
# @param [Proc] actual
# @return [void]
def when_negated_expectation(actual)
expect(result(&actual)).not_to public_send(*expectation_args)
end
# Result of actual block
#
# @return [Object]
def result
@result = yield
end
# Error message placeholder to indicate waiter did not fail properly
# This message should not appear under normal circumstances since it should
# always be assigned from repeater
#
# @return [String]
def e
@e ||= 'Waiter did not fail!'
end
# Operator message
#
# @return [String]
def operator_msg
case operator
when 'eq' then 'equal'
else operator
end
end
# Expect operator
#
# @return [String]
def operator
@operator ||= name.to_s.match(/eventually_(.+?)$/).to_a[1].to_s
end
# Expectation args
#
# @return [String, Array]
def expectation_args
if operator.include?('truthy') || operator.include?('falsey') || operator.include?('empty')
operator
elsif operator == "include" && expected.is_a?(Array)
[operator, *expected]
else
[operator, expected]
end
end
end
end
end
# frozen_string_literal: true
module Matchers
PREDICATE_TARGETS = %w[
element
file_content
assignee
child_pipeline
content
design
file
issue
job
package
pipeline
related_issue_item
snippet_description
tag
].each do |predicate|
RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs|
match do |page_object|
page_object.public_send("has_#{predicate}?", *args, **kwargs) # rubocop:disable GitlabSecurity/PublicSend
end
match_when_negated do |page_object|
page_object.public_send("has_no_#{predicate}?", *args, **kwargs) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
end
# frozen_string_literal: true
module Matchers
class HaveText
def initialize(expected_text, **kwargs)
@expected_text = expected_text
@kwargs = kwargs
end
def matches?(actual)
@actual = wrap(actual)
@actual.has_text?(@expected_text, **@kwargs)
end
def does_not_match?(actual)
@actual = wrap(actual)
@actual.has_no_text?(@expected_text, **@kwargs)
end
def failure_message
"expected to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
end
def failure_message_when_negated
"expected not to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
end
def normalized_actual_text
@actual.text.gsub(/\s+/, " ")
end
# From https://github.com/teamcapybara/capybara/blob/fe5940c6afbfe32152df936ce03ad1371ae05354/lib/capybara/rspec/matchers/base.rb#L66
def wrap(actual)
actual = actual.to_capybara_node if actual.respond_to?(:to_capybara_node)
@context_el = if actual.respond_to?(:has_selector?)
actual
else
Capybara.string(actual.to_s)
end
end
end
def have_text(text, **kwargs) # rubocop:disable Naming/PredicateName
HaveText.new(text, **kwargs)
end
alias_method :have_content, :have_text
end
# frozen_string_literal: true
require 'logger'
require 'timecop'
require 'active_support/core_ext/integer/time'
RSpec.describe QA::Support::Repeater do
......
# frozen_string_literal: true
require 'logger'
require 'timecop'
RSpec.describe QA::Support::Retrier do
before do
logger = ::Logger.new $stdout
......
# frozen_string_literal: true
require 'logger'
RSpec.describe QA::Support::Waiter do
before do
logger = ::Logger.new $stdout
......
......@@ -9,7 +9,6 @@ exports[`MemoryGraph Render chart should draw container with chart 1`] = `
data="Nov 12 2019 19:17:33,2.87,Nov 12 2019 19:18:33,2.78,Nov 12 2019 19:19:33,2.78,Nov 12 2019 19:20:33,3.01"
height="25"
tooltiplabel="MB"
variant="gray900"
/>
</div>
`;
......@@ -95,6 +95,19 @@ RSpec.describe API::Files do
expect(response.headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
end
it 'caches sha256 of the content', :use_clean_rails_redis_caching do
head api(route(file_path), current_user, **options), params: params
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
.to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
expect_next_instance_of(Gitlab::Git::Blob) do |instance|
expect(instance).not_to receive(:load_all_data!)
end
head api(route(file_path), current_user, **options), params: params
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
......
......@@ -1233,10 +1233,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@32.2.1":
version "32.2.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.2.1.tgz#e019124af981e8ceffd39f30cf08d315c53d4ac8"
integrity sha512-19qe30gHtBG7g7wJy36bvS+ji1puJwVzAwqJOFXAh3axSa0Getyjyl9t5gmfwmZ2HehFTWLlThXppclnJVCEGA==
"@gitlab/ui@32.2.3":
version "32.2.3"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.2.3.tgz#1ac6169e370c21ec71ea3b18c5d077ab08e122b5"
integrity sha512-kT7XTawdXOzKkQeTjbzRawrpRLCeZj/xqQH6BECJ/IkporJle8CLR955HdvrS67lj8cYAOg4uOL7JjTYtltoPA==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.18.1"
......
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