Commit 9bd90688 authored by Dan Davison's avatar Dan Davison

Merge branch 'acunskis-qa-zeitwerk' into 'master'

E2E: Autoload QA classes with zeitwerk

See merge request gitlab-org/gitlab!68478
parents 741c5eb2 d02ccea8
...@@ -8,6 +8,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -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). 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 ## 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/). 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 ...@@ -342,7 +348,7 @@ end
When something requires waiting to be matched, use `eventually_` matchers with clear wait duration definition. 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 ```ruby
expect { async_value }.to eventually_eq(value).within(max_duration: 120, max_attempts: 60, reload_page: page) expect { async_value }.to eventually_eq(value).within(max_duration: 120, max_attempts: 60, reload_page: page)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
source 'https://rubygems.org' 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 'activesupport', '~> 6.1.3.2' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.14.1' gem 'allure-rspec', '~> 2.14.1'
gem 'capybara', '~> 3.35.0' gem 'capybara', '~> 3.35.0'
...@@ -10,10 +10,9 @@ gem 'capybara-screenshot', '~> 1.0.23' ...@@ -10,10 +10,9 @@ gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.3' gem 'rake', '~> 12.3.3'
gem 'rspec', '~> 3.10' gem 'rspec', '~> 3.10'
gem 'selenium-webdriver', '~> 4.0.0.beta4' 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 'rest-client', '~> 2.1.0'
gem 'nokogiri', '~> 1.11.7' gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_junit_formatter', '~> 0.4.1' gem 'rspec_junit_formatter', '~> 0.4.1'
gem 'faker', '~> 1.6', '>= 1.6.6' gem 'faker', '~> 1.6', '>= 1.6.6'
gem 'knapsack', '~> 1.17' gem 'knapsack', '~> 1.17'
...@@ -24,6 +23,7 @@ gem 'parallel', '~> 1.19' ...@@ -24,6 +23,7 @@ gem 'parallel', '~> 1.19'
gem 'rspec-parameterized', '~> 0.4.2' gem 'rspec-parameterized', '~> 0.4.2'
gem 'octokit', '~> 4.21' gem 'octokit', '~> 4.21'
gem 'webdrivers', '~> 4.6' gem 'webdrivers', '~> 4.6'
gem 'zeitwerk', '~> 2.4'
gem 'chemlab', '~> 0.7' gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1' gem 'chemlab-library-www-gitlab-com', '~> 0.1'
......
...@@ -221,7 +221,6 @@ DEPENDENCIES ...@@ -221,7 +221,6 @@ DEPENDENCIES
faker (~> 1.6, >= 1.6.6) faker (~> 1.6, >= 1.6.6)
gitlab-qa gitlab-qa
knapsack (~> 1.17) knapsack (~> 1.17)
nokogiri (~> 1.11.7)
octokit (~> 4.21) octokit (~> 4.21)
parallel (~> 1.19) parallel (~> 1.19)
parallel_tests (~> 2.29) parallel_tests (~> 2.29)
...@@ -237,6 +236,7 @@ DEPENDENCIES ...@@ -237,6 +236,7 @@ DEPENDENCIES
selenium-webdriver (~> 4.0.0.beta4) selenium-webdriver (~> 4.0.0.beta4)
timecop (~> 0.9.1) timecop (~> 0.9.1)
webdrivers (~> 4.6) webdrivers (~> 4.6)
zeitwerk (~> 2.4)
BUNDLED WITH BUNDLED WITH
2.2.22 2.2.22
# frozen_string_literal: true # frozen_string_literal: true
$: << File.expand_path(__dir__)
Encoding.default_external = 'UTF-8' Encoding.default_external = 'UTF-8'
require_relative '../lib/gitlab' require_relative '../lib/gitlab'
...@@ -10,683 +8,49 @@ require_relative '../config/initializers/0_inject_enterprise_edition_module' ...@@ -10,683 +8,49 @@ require_relative '../config/initializers/0_inject_enterprise_edition_module'
require_relative 'lib/gitlab' require_relative 'lib/gitlab'
require 'chemlab' require 'bundler/setup'
Bundler.require(:default)
module QA module QA
## root = "#{__dir__}/qa"
# Helper classes to represent frequently used sequences of actions
# (e.g., login) loader = Zeitwerk::Loader.new
# loader.push_dir(root, namespace: QA)
module Flow
autoload :Login, 'qa/flow/login' loader.ignore("#{root}/specs/features")
autoload :Project, 'qa/flow/project'
autoload :Saml, 'qa/flow/saml' loader.inflector.inflect(
autoload :User, 'qa/flow/user' "ce" => "CE",
autoload :MergeRequest, 'qa/flow/merge_request' "ee" => "EE",
autoload :Pipeline, 'qa/flow/pipeline' "api" => "API",
autoload :SignUp, 'qa/flow/sign_up' "ssh" => "SSH",
end "ssh_key" => "SSHKey",
"ssh_keys" => "SSHKeys",
## "ecdsa" => "ECDSA",
# GitLab QA runtime classes, mostly singletons. "ed25519" => "ED25519",
# "rsa" => "RSA",
module Runtime "ldap" => "LDAP",
autoload :Release, 'qa/runtime/release' "ldap_tls" => "LDAPTLS",
autoload :User, 'qa/runtime/user' "ldap_no_tls" => "LDAPNoTLS",
autoload :Namespace, 'qa/runtime/namespace' "ldap_no_server" => "LDAPNoServer",
autoload :Scenario, 'qa/runtime/scenario' "rspec" => "RSpec",
autoload :Browser, 'qa/runtime/browser' "web_ide" => "WebIDE",
autoload :Env, 'qa/runtime/env' "ci_cd" => "CiCd",
autoload :Address, 'qa/runtime/address' "repo_by_url" => "RepoByURL",
autoload :Path, 'qa/runtime/path' "oauth" => "OAuth",
autoload :Feature, 'qa/runtime/feature' "saml_sso_sign_in" => "SamlSSOSignIn",
autoload :Fixtures, 'qa/runtime/fixtures' "saml_sso_sign_up" => "SamlSSOSignUp",
autoload :Logger, 'qa/runtime/logger' "group_saml" => "GroupSAML",
autoload :GPG, 'qa/runtime/gpg' "instance_saml" => "InstanceSAML",
autoload :MailHog, 'qa/runtime/mail_hog' "saml_sso" => "SamlSSO",
autoload :IPAddress, 'qa/runtime/ip_address' "ldap_sync" => "LDAPSync",
autoload :Search, 'qa/runtime/search' "ip_address" => "IPAddress",
autoload :ApplicationSettings, 'qa/runtime/application_settings' "gpg" => "GPG",
autoload :AllureReport, 'qa/runtime/allure_report' "user_gpg" => "UserGPG",
"smtp" => "SMTP",
module API "otp" => "OTP",
autoload :Client, 'qa/runtime/api/client' "jira_api" => "JiraAPI"
autoload :RepositoryStorageMoves, 'qa/runtime/api/repository_storage_moves' )
autoload :Request, 'qa/runtime/api/request'
end loader.setup
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
end end
QA::Runtime::Release.extend_autoloads!
...@@ -5,10 +5,6 @@ module QA ...@@ -5,10 +5,6 @@ module QA
module Strategy module Strategy
extend self extend self
def extend_autoloads!
# noop
end
def perform_before_hooks def perform_before_hooks
# The login page could take some time to load the first time it is visited. # 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. # 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 ...@@ -5,7 +5,7 @@ module QA
module Page module Page
module Project module Project
module Settings module Settings
module CICD module CiCd
extend QA::Page::PageConcern extend QA::Page::PageConcern
def self.prepended(base) def self.prepended(base)
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
def fabricate! def fabricate!
QA::Page::Project::Menu.perform(&:go_to_ci_cd_settings) 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| setting.expand_pipeline_subscriptions do |page|
page.subscribe(project_path) page.subscribe(project_path)
end end
......
...@@ -5,11 +5,6 @@ module QA ...@@ -5,11 +5,6 @@ module QA
module Strategy module Strategy
extend self extend self
def extend_autoloads!
require 'qa/ce/strategy'
require 'qa/ee'
end
def perform_before_hooks def perform_before_hooks
# Without a license, perform the CE before hooks only. # Without a license, perform the CE before hooks only.
unless ENV['EE_LICENSE'] unless ENV['EE_LICENSE']
......
...@@ -67,7 +67,7 @@ module QA ...@@ -67,7 +67,7 @@ module QA
end end
def login_to_idp_if_required(username, password) 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 end
end end
......
...@@ -4,7 +4,6 @@ require 'cgi' ...@@ -4,7 +4,6 @@ require 'cgi'
require 'uri' require 'uri'
require 'fileutils' require 'fileutils'
require 'tmpdir' require 'tmpdir'
require 'securerandom'
module QA module QA
module Git module Git
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Page module Page
module Project module Project
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
module Page module Page
module Project module Project
module Settings module Settings
class CICD < Page::Base class CiCd < Page::Base
include QA::Page::Settings::Common include QA::Page::Settings::Common
view 'app/views/projects/settings/ci_cd/show.html.haml' do view 'app/views/projects/settings/ci_cd/show.html.haml' do
...@@ -43,4 +43,4 @@ module QA ...@@ -43,4 +43,4 @@ module QA
end end
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 # frozen_string_literal: true
require 'pathname'
module QA module QA
module Page module Page
class View class View
......
...@@ -55,7 +55,7 @@ module QA ...@@ -55,7 +55,7 @@ module QA
end end
end end
include Support::Api include Support::API
attr_writer :api_resource, :api_response attr_writer :api_resource, :api_response
def api_put(body = api_put_body) def api_put(body = api_put_body)
......
...@@ -22,7 +22,7 @@ module QA ...@@ -22,7 +22,7 @@ module QA
Page::Project::Menu.perform(&:go_to_ci_cd_settings) 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| setting.expand_ci_variables do |page|
page.click_add_variable page.click_add_variable
page.fill_variable(key, value, masked) page.fill_variable(key, value, masked)
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class Issue < Base class Issue < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
module KubernetesCluster module KubernetesCluster
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
# Base label class for GroupLabel and ProjectLabel # Base label class for GroupLabel and ProjectLabel
......
...@@ -12,7 +12,7 @@ module QA ...@@ -12,7 +12,7 @@ module QA
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}]) 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 = 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
end end
...@@ -31,7 +31,7 @@ module QA ...@@ -31,7 +31,7 @@ module QA
QA::Runtime::Logger.debug(%Q[Sharing #{self.class.name} with #{group.name}]) 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 = 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
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class MergeRequest < Base class MergeRequest < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class MergeRequestFromFork < MergeRequest class MergeRequestFromFork < MergeRequest
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class Package < Base class Package < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class Project < Base class Project < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'octokit'
module QA module QA
module Resource module Resource
class ProjectImportedFromGithub < Resource::Project class ProjectImportedFromGithub < Resource::Project
...@@ -68,7 +66,7 @@ module QA ...@@ -68,7 +66,7 @@ module QA
response = post(request_url(api_trigger_mirror_pull_path), nil) response = post(request_url(api_trigger_mirror_pull_path), nil)
Runtime::Logger.info "Mirror pull request response: #{response}" Runtime::Logger.info "Mirror pull request response: #{response}"
response.code == Support::Api::HTTP_STATUS_OK response.code == Support::API::HTTP_STATUS_OK
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class ProjectImportedFromURL < Resource::Project class ProjectImportedFromURL < Resource::Project
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class ProjectIssueNote < Base class ProjectIssueNote < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class ProtectedBranch < Base class ProtectedBranch < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class RegistryRepository < Base class RegistryRepository < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
module Repository module Repository
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
require 'securerandom'
module QA module QA
module Resource module Resource
module Repository module Repository
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class Runner < Base class Runner < Base
......
...@@ -72,7 +72,7 @@ module QA ...@@ -72,7 +72,7 @@ module QA
Support::Retrier.retry_until(max_duration: QA::EE::Runtime::Geo.max_db_replication_time, sleep_interval: 3) do 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 = 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) parse_body(response)[:title].include?(title)
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
class User < Base class User < Base
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Resource module Resource
module Wiki module Wiki
......
...@@ -12,8 +12,6 @@ module QA ...@@ -12,8 +12,6 @@ module QA
def configure! def configure!
return unless Env.generate_allure_report? return unless Env.generate_allure_report?
require 'allure-rspec'
configure_allure configure_allure
configure_attachments configure_attachments
configure_rspec configure_rspec
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module API module API
module RepositoryStorageMoves module RepositoryStorageMoves
extend self extend self
extend Support::Api extend Support::API
RepositoryStorageMovesError = Class.new(RuntimeError) RepositoryStorageMovesError = Class.new(RuntimeError)
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
module Runtime module Runtime
class ApplicationSettings class ApplicationSettings
class << self class << self
include Support::Api include Support::API
APPLICATION_SETTINGS_PATH = '/application/settings' APPLICATION_SETTINGS_PATH = '/application/settings'
...@@ -18,7 +18,7 @@ module QA ...@@ -18,7 +18,7 @@ module QA
QA::Runtime::Logger.info("Setting application settings: #{application_settings}") QA::Runtime::Logger.info("Setting application settings: #{application_settings}")
r = put(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url, **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 end
def get_application_settings def get_application_settings
......
...@@ -4,7 +4,6 @@ require 'rspec/core' ...@@ -4,7 +4,6 @@ require 'rspec/core'
require 'rspec/expectations' require 'rspec/expectations'
require 'capybara/rspec' require 'capybara/rspec'
require 'capybara-screenshot/rspec' require 'capybara-screenshot/rspec'
require 'selenium-webdriver'
require 'webdrivers/chromedriver' require 'webdrivers/chromedriver'
require 'webdrivers/geckodriver' require 'webdrivers/geckodriver'
......
# frozen_string_literal: true # frozen_string_literal: true
require 'active_support/deprecation' require 'active_support/deprecation'
require 'gitlab/qa'
require 'uri' require 'uri'
module QA module QA
......
...@@ -8,7 +8,7 @@ module QA ...@@ -8,7 +8,7 @@ module QA
class << self class << self
# Documentation: https://docs.gitlab.com/ee/api/features.html # Documentation: https://docs.gitlab.com/ee/api/features.html
include Support::Api include Support::API
SetFeatureError = Class.new(RuntimeError) SetFeatureError = Class.new(RuntimeError)
AuthorizationError = Class.new(RuntimeError) AuthorizationError = Class.new(RuntimeError)
...@@ -17,7 +17,7 @@ module QA ...@@ -17,7 +17,7 @@ module QA
def remove(key) def remove(key)
request = Runtime::API::Request.new(api_client, "/features/#{key}") request = Runtime::API::Request.new(api_client, "/features/#{key}")
response = delete(request.url) 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}`." raise SetFeatureError, "Deleting feature flag #{key} failed with `#{response}`."
end end
end end
...@@ -100,7 +100,7 @@ module QA ...@@ -100,7 +100,7 @@ module QA
scopes[:user] = scopes[:user].username if scopes.key?(:user) scopes[:user] = scopes[:user].username if scopes.key?(:user)
request = Runtime::API::Request.new(api_client, "/features/#{key}") request = Runtime::API::Request.new(api_client, "/features/#{key}")
response = post(request.url, scopes.merge({ value: value })) 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}`." raise SetFeatureError, "Setting feature flag #{key} to #{value} failed with `#{response}`."
end end
end end
......
...@@ -5,7 +5,7 @@ require 'tmpdir' ...@@ -5,7 +5,7 @@ require 'tmpdir'
module QA module QA
module Runtime module Runtime
module Fixtures module Fixtures
include Support::Api include Support::API
TemplateNotFoundError = Class.new(RuntimeError) TemplateNotFoundError = Class.new(RuntimeError)
......
...@@ -4,7 +4,7 @@ require 'socket' ...@@ -4,7 +4,7 @@ require 'socket'
module QA module QA
module Runtime module Runtime
module IPAddress module IPAddress
include Support::Api include Support::API
HostUnreachableError = Class.new(StandardError) HostUnreachableError = Class.new(StandardError)
LOOPBACK_ADDRESS = '127.0.0.1' LOOPBACK_ADDRESS = '127.0.0.1'
...@@ -15,7 +15,7 @@ module QA ...@@ -15,7 +15,7 @@ module QA
# we use the public facing IP address # we use the public facing IP address
ip_address = if Env.running_in_ci? && !URI.parse(Scenario.gitlab_address).host.include?('test') ip_address = if Env.running_in_ci? && !URI.parse(Scenario.gitlab_address).host.include?('test')
response = get(PUBLIC_IP_ADDRESS_API) 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 response.body
elsif page.current_host.include?('localhost') elsif page.current_host.include?('localhost')
......
...@@ -13,10 +13,6 @@ module QA ...@@ -13,10 +13,6 @@ module QA
# CE to EE. # CE to EE.
# #
class Release class Release
def initialize
require "qa/#{version.downcase}/strategy"
end
def version def version
@version ||= ::File.directory?("#{__dir__}/../ee") ? :EE : :CE @version ||= ::File.directory?("#{__dir__}/../ee") ? :EE : :CE
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
module Runtime module Runtime
module Search module Search
extend self extend self
extend Support::Api extend Support::API
RETRY_MAX_ITERATION = 10 RETRY_MAX_ITERATION = 10
RETRY_SLEEP_INTERVAL = 12 RETRY_SLEEP_INTERVAL = 12
......
# frozen_string_literal: true # frozen_string_literal: true
require 'resolv' require 'resolv'
require 'securerandom'
module QA module QA
module Service module Service
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
require 'mkmf' require 'mkmf'
require 'pathname'
module QA module QA
module Service module Service
......
# frozen_string_literal: true # frozen_string_literal: true
require 'octokit'
# rubocop:disable Rails/Pluck # rubocop:disable Rails/Pluck
module QA module QA
# Only executes in custom job/pipeline # Only executes in custom job/pipeline
......
...@@ -4,7 +4,7 @@ require 'airborne' ...@@ -4,7 +4,7 @@ require 'airborne'
module QA module QA
RSpec.describe 'Plan' do RSpec.describe 'Plan' do
include Support::Api include Support::API
describe 'Issue' do describe 'Issue' do
let(:issue) do let(:issue) do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Create' do RSpec.describe 'Create' do
describe 'Default branch name instance setting', :requires_admin, :skip_live_env do describe 'Default branch name instance setting', :requires_admin, :skip_live_env do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'airborne' require 'airborne'
require 'securerandom'
module QA module QA
RSpec.describe 'API basics' do RSpec.describe 'API basics' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'airborne' require 'airborne'
require 'securerandom'
require 'digest' require 'digest'
module QA module QA
RSpec.describe 'Create' do RSpec.describe 'Create' do
describe 'Compare archives of different user projects with the same name and check they\'re different' 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(:project_name) { "project-archive-download-#{SecureRandom.hex(8)}" }
let(:archive_types) { %w(tar.gz tar.bz2 tar zip) } let(:archive_types) { %w(tar.gz tar.bz2 tar zip) }
......
...@@ -4,7 +4,7 @@ require 'airborne' ...@@ -4,7 +4,7 @@ require 'airborne'
module QA module QA
RSpec.describe 'Package', only: { subdomain: %i[staging pre] } do RSpec.describe 'Package', only: { subdomain: %i[staging pre] } do
include Support::Api include Support::API
describe 'Container Registry' do describe 'Container Registry' do
let(:api_client) { Runtime::API::Client.new(:gitlab) } let(:api_client) { Runtime::API::Client.new(:gitlab) }
......
...@@ -3,12 +3,15 @@ ...@@ -3,12 +3,15 @@
module QA module QA
RSpec.describe 'Manage', :orchestrated, :instance_saml do RSpec.describe 'Manage', :orchestrated, :instance_saml do
describe 'Instance wide SAML SSO' 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) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_with_saml) 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') login_page.login('user1', 'user1pass')
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'nokogiri'
module QA module QA
RSpec.describe 'Manage', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/212145', type: :stale } do RSpec.describe 'Manage', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/212145', type: :stale } do
describe 'Check for broken images', :requires_admin do describe 'Check for broken images', :requires_admin do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
RSpec.describe 'Plan', :orchestrated, :smtp, :requires_admin do RSpec.describe 'Plan', :orchestrated, :smtp, :requires_admin do
describe 'Email Notification' do describe 'Email Notification' do
include Support::Api include Support::API
let!(:user) do let!(:user) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Plan', :reliable do RSpec.describe 'Plan', :reliable do
describe 'Issues list' do describe 'Issues list' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Create', :requires_admin, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/195179', type: :flaky } do 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 @@ ...@@ -2,7 +2,7 @@
module QA module QA
RSpec.describe 'Create' do RSpec.describe 'Create' do
include Support::Api include Support::API
describe 'Jira integration', :jira, :orchestrated, :requires_admin do describe 'Jira integration', :jira, :orchestrated, :requires_admin do
let(:jira_project_key) { 'JITP' } let(:jira_project_key) { 'JITP' }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Create' do RSpec.describe 'Create' do
describe 'File templates' do describe 'File templates' do
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
# tests are run in parallel). # tests are run in parallel).
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705 # See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705
include Support::Api include Support::API
before(:context) do before(:context) do
@project = Resource::Project.fabricate_via_api! do |p| @project = Resource::Project.fabricate_via_api! do |p|
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Create' do RSpec.describe 'Create' do
describe 'Web IDE file templates' do describe 'Web IDE file templates' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
describe 'Pipeline with protected variable' do describe 'Pipeline with protected variable' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
describe 'Include multiple files from a project' do describe 'Include multiple files from a project' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
context 'When pipeline is blocked' do context 'When pipeline is blocked' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
context 'When job is configured to only run on merge_request_events' do context 'When job is configured to only run on merge_request_events' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
describe 'Pass dotenv variables to downstream via bridge' do describe 'Pass dotenv variables to downstream via bridge' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
describe "Trigger child pipeline with 'when:manual'" do describe "Trigger child pipeline with 'when:manual'" do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner do RSpec.describe 'Verify', :runner do
describe 'Trigger matrix' do describe 'Trigger matrix' do
......
...@@ -21,7 +21,7 @@ module QA ...@@ -21,7 +21,7 @@ module QA
runner.project.visit! runner.project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings) 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 sleep 5 # Runner should register within 5 seconds
settings.expand_runners_settings do |page| settings.expand_runners_settings do |page|
......
...@@ -49,7 +49,7 @@ module QA ...@@ -49,7 +49,7 @@ module QA
def configure_code_coverage(coverage_tool_pattern) def configure_code_coverage(coverage_tool_pattern)
Page::Project::Menu.perform(&:go_to_ci_cd_settings) 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| settings.expand_general_pipelines do |coverage|
coverage.configure_coverage_regex(coverage_tool_pattern) coverage.configure_coverage_regex(coverage_tool_pattern)
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Package', :orchestrated, :packages, :object_storage do RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'Composer Repository' do describe 'Composer Repository' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
describe 'Maven Repository' do describe 'Maven Repository' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Package', :orchestrated, :packages, :object_storage do RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'NuGet Repository' do describe 'NuGet Repository' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Configure' do RSpec.describe 'Configure' do
let(:project) do let(:project) do
...@@ -102,7 +100,7 @@ module QA ...@@ -102,7 +100,7 @@ module QA
project.visit! project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings) 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) Page::Project::Settings::AutoDevops.perform(&:enable_autodevops)
# Create AutoDevOps repo # Create AutoDevOps repo
......
# frozen_string_literal: true # frozen_string_literal: true
require 'airborne' require 'airborne'
require 'securerandom'
module QA module QA
RSpec.describe 'Enablement:Search' do RSpec.describe 'Enablement:Search' do
...@@ -52,7 +51,7 @@ module QA ...@@ -52,7 +51,7 @@ module QA
def expect_search_to_find_project(search_term) 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 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 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? raise 'Empty search result returned' if json_body.empty?
......
# frozen_string_literal: true # frozen_string_literal: true
require 'airborne' require 'airborne'
require 'securerandom'
module QA module QA
RSpec.describe 'Enablement:Search' do RSpec.describe 'Enablement:Search' do
...@@ -54,7 +53,7 @@ module QA ...@@ -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 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 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 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 expect(json_body).to be_empty
end end
end end
...@@ -65,7 +64,7 @@ module QA ...@@ -65,7 +64,7 @@ module QA
def successful_search(api_client) 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 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 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? raise 'Empty search result returned' if json_body.empty?
......
# frozen_string_literal: true # frozen_string_literal: true
require 'airborne' require 'airborne'
require 'securerandom'
module QA module QA
RSpec.describe 'Enablement:Search' do RSpec.describe 'Enablement:Search' do
...@@ -44,7 +43,7 @@ module QA ...@@ -44,7 +43,7 @@ module QA
start_time = Time.now start_time = Time.now
while (Time.now - start_time) / 60 < p1_threshold while (Time.now - start_time) / 60 < p1_threshold
get Runtime::Search.create_search_request(api_client, 'blobs', project_file_content).url 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) if !json_body.empty? && json_body[0][:data].match(project_file_content) && json_body[0][:project_id].equal?(project.id)
break break
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
include Support::Api include Support::API
let(:api_client) { Runtime::API::Client.new(:gitlab) } let(:api_client) { Runtime::API::Client.new(:gitlab) }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
describe 'Group file templates', :requires_admin do describe 'Group file templates', :requires_admin do
include Support::Api include Support::API
templates = [ templates = [
{ {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
RSpec.describe 'Manage', :orchestrated, :ldap_tls, :ldap_no_tls, :requires_admin do RSpec.describe 'Manage', :orchestrated, :ldap_tls, :ldap_no_tls, :requires_admin do
describe 'LDAP Group sync' do describe 'LDAP Group sync' do
include Support::Api include Support::API
let(:group) do let(:group) do
Resource::Group.fabricate_via_api! do |resource| Resource::Group.fabricate_via_api! do |resource|
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
# TODO: Remove :requires_admin meta when the `Runtime::Feature.enable` method call is removed # TODO: Remove :requires_admin meta when the `Runtime::Feature.enable` method call is removed
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Enforced SSO' do describe 'Group SAML SSO - Enforced SSO' do
include Support::Api include Support::API
let!(:group) do let!(:group) do
Resource::Sandbox.fabricate_via_api! do |sandbox_group| Resource::Sandbox.fabricate_via_api! do |sandbox_group|
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Enforced SSO' do describe 'Group SAML SSO - Enforced SSO' do
include Support::Api include Support::API
let!(:group) do let!(:group) do
Resource::Sandbox.fabricate_via_api! do |sandbox_group| Resource::Sandbox.fabricate_via_api! do |sandbox_group|
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do RSpec.describe 'Manage', :group_saml, :orchestrated, :requires_admin do
describe 'Group SAML SSO - Non enforced SSO' do describe 'Group SAML SSO - Non enforced SSO' do
include Support::Api include Support::API
let(:user) { Resource::User.fabricate_via_api! } let(:user) { Resource::User.fabricate_via_api! }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
# Issue to enable this test in live environments: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/614 # Issue to enable this test in live environments: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/614
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Manage' do RSpec.describe 'Manage' do
describe 'Project templates' do describe 'Project templates' do
include Support::Api include Support::API
before(:all) do before(:all) do
@files = [ @files = [
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Plan', :reliable do RSpec.describe 'Plan', :reliable do
describe 'Custom email', :requires_admin do describe 'Custom email', :requires_admin do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Plan', :reliable do RSpec.describe 'Plan', :reliable do
describe 'Group issue boards' do describe 'Group issue boards' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Plan', :reliable do RSpec.describe 'Plan', :reliable do
describe 'Group issue boards' do describe 'Group issue boards' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify' do RSpec.describe 'Verify' do
describe 'Cancelling merge request in merge train', :runner, :requires_admin do describe 'Cancelling merge request in merge train', :runner, :requires_admin do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :docker, :runner do RSpec.describe 'Verify', :docker, :runner do
describe 'In merge trains' do describe 'In merge trains' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'octokit'
require 'faker'
require 'base64' require 'base64'
module QA module QA
......
# frozen_string_literal: true # frozen_string_literal: true
require 'faker'
module QA module QA
RSpec.describe 'Verify', :runner, :reliable do RSpec.describe 'Verify', :runner, :reliable do
describe 'Pipelines for merged results and merge trains' do describe 'Pipelines for merged results and merge trains' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Verify', :runner, :transient do RSpec.describe 'Verify', :runner, :transient do
describe 'Merge trains transient bugs' do describe 'Merge trains transient bugs' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Release' do RSpec.describe 'Release' do
describe 'Multi-project pipelines' do describe 'Multi-project pipelines' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
require_relative '../../../browser_ui/8_monitor/cluster_with_prometheus' require_relative '../../../browser_ui/8_monitor/cluster_with_prometheus'
module QA module QA
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
module QA module QA
RSpec.describe 'Fulfillment', :requires_admin, only: { subdomain: :staging } do RSpec.describe 'Fulfillment', :requires_admin, only: { subdomain: :staging } do
describe 'Purchase' do describe 'Purchase' do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
RSpec.describe 'Geo', :orchestrated, :geo do RSpec.describe 'Geo', :orchestrated, :geo do
describe 'GitLab Geo project deletion replication' do describe 'GitLab Geo project deletion replication' do
include Support::Api include Support::API
deleted_project_name = nil deleted_project_name = nil
deleted_project_id = nil deleted_project_id = nil
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
describe 'Security Reports in a Merge Request' do describe 'Security Reports in a Merge Request' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
describe 'Enable SAST from UI' do describe 'Enable SAST from UI' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
let(:approved_license_name) { "MIT License" } let(:approved_license_name) { "MIT License" }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
describe 'License merge request widget' do describe 'License merge request widget' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
let(:number_of_dependencies_in_fixture) { 9 } let(:number_of_dependencies_in_fixture) { 9 }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
module QA module QA
RSpec.describe 'Secure', :runner do RSpec.describe 'Secure', :runner do
describe 'Security Reports in a Merge Request' do describe 'Security Reports in a Merge Request' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'knapsack'
require 'rspec/core' require 'rspec/core'
require 'rspec/expectations' require 'rspec/expectations'
......
# frozen_string_literal: true # frozen_string_literal: true
require 'rest-client'
module QA module QA
module Support module Support
module Api module API
HTTP_STATUS_OK = 200 HTTP_STATUS_OK = 200
HTTP_STATUS_CREATED = 201 HTTP_STATUS_CREATED = 201
HTTP_STATUS_NO_CONTENT = 204 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' ...@@ -10,7 +10,7 @@ require_relative '../../qa'
module QA module QA
module Tools module Tools
class DeleteProjects class DeleteProjects
include Support::Api include Support::API
def initialize def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS'] raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS']
......
...@@ -10,7 +10,7 @@ require_relative '../../qa' ...@@ -10,7 +10,7 @@ require_relative '../../qa'
module QA module QA
module Tools module Tools
class DeleteSubgroups class DeleteSubgroups
include Support::Api include Support::API
def initialize def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS'] raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
......
...@@ -15,7 +15,7 @@ require_relative '../../qa' ...@@ -15,7 +15,7 @@ require_relative '../../qa'
module QA module QA
module Tools module Tools
class DeleteTestSSHKeys class DeleteTestSSHKeys
include Support::Api include Support::API
ITEMS_PER_PAGE = '100' ITEMS_PER_PAGE = '100'
......
# frozen_string_literal: true # frozen_string_literal: true
require 'securerandom'
require 'faker'
require 'yaml' require 'yaml'
require_relative '../../qa' require_relative '../../qa'
# This script generates testdata for Performance Testing. # This script generates testdata for Performance Testing.
...@@ -12,7 +10,7 @@ require_relative '../../qa' ...@@ -12,7 +10,7 @@ require_relative '../../qa'
module QA module QA
module Tools module Tools
class GeneratePerfTestdata class GeneratePerfTestdata
include Support::Api include Support::API
def initialize def initialize
raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS'] raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Jira module Jira
class JiraAPI class JiraAPI
include Scenario::Actable include Scenario::Actable
include Support::Api include Support::API
def base_url def base_url
host = QA::Runtime::Env.jira_hostname || 'localhost' host = QA::Runtime::Env.jira_hostname || 'localhost'
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
module QA module QA
module Vendor module Vendor
module SAMLIdp module SamlIdp
module Page module Page
class Base class Base
include Capybara::DSL include Capybara::DSL
......
...@@ -4,7 +4,7 @@ require 'capybara/dsl' ...@@ -4,7 +4,7 @@ require 'capybara/dsl'
module QA module QA
module Vendor module Vendor
module SAMLIdp module SamlIdp
module Page module Page
class Login < Page::Base class Login < Page::Base
def login(username, password) def login(username, password)
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Git::Repository do RSpec.describe QA::Git::Repository do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
shared_context 'unresolvable git directory' do shared_context 'unresolvable git directory' do
let(:repo_uri) { 'http://foo/bar.git' } let(:repo_uri) { 'http://foo/bar.git' }
......
# frozen_string_literal: true # frozen_string_literal: true
require 'capybara/dsl' require 'capybara/dsl'
require 'logger'
RSpec.describe QA::Support::Page::Logging do RSpec.describe QA::Support::Page::Logging do
let(:page) { double.as_null_object } let(:page) { double.as_null_object }
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Resource::Base do RSpec.describe QA::Resource::Base do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
let(:resource) { spy('resource') } let(:resource) { spy('resource') }
let(:location) { 'http://location' } let(:location) { 'http://location' }
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Runtime::API::Client do RSpec.describe QA::Runtime::API::Client do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
describe 'initialization' do describe 'initialization' do
it 'defaults to :gitlab address' do it 'defaults to :gitlab address' do
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Runtime::Env do RSpec.describe QA::Runtime::Env do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
shared_examples 'boolean method' do |**kwargs| shared_examples 'boolean method' do |**kwargs|
it_behaves_like 'boolean method with parameter', kwargs it_behaves_like 'boolean method with parameter', kwargs
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Runtime::Namespace do RSpec.describe QA::Runtime::Namespace do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
describe '.name' do describe '.name' do
context 'when CACHE_NAMESPACE_NAME is not defined' do context 'when CACHE_NAMESPACE_NAME is not defined' do
......
...@@ -30,23 +30,4 @@ RSpec.describe QA::Runtime::Release do ...@@ -30,23 +30,4 @@ RSpec.describe QA::Runtime::Release do
end end
end 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 end
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../qa' require_relative '../qa'
require 'rspec/retry'
require 'rspec-parameterized' require 'securerandom'
require 'pathname'
require 'active_support/core_ext/hash' require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/blank'
require_relative 'qa_deprecation_toolkit_env' require_relative 'qa_deprecation_toolkit_env'
QaDeprecationToolkitEnv.configure! QaDeprecationToolkitEnv.configure!
if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK'] Knapsack::Adapters::RSpecAdapter.bind if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind
end
QA::Runtime::Browser.configure! QA::Runtime::Browser.configure!
QA::Runtime::AllureReport.configure! QA::Runtime::AllureReport.configure!
...@@ -24,7 +22,8 @@ Dir[::File.join(__dir__, "support/shared_contexts/*.rb")].sort.each { |f| requir ...@@ -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 } Dir[::File.join(__dir__, "support/shared_examples/*.rb")].sort.each { |f| require f }
RSpec.configure do |config| 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::ContextFormatter
config.add_formatter QA::Specs::Helpers::QuarantineFormatter config.add_formatter QA::Specs::Helpers::QuarantineFormatter
......
# frozen_string_literal: true # frozen_string_literal: true
require 'allure-rspec'
describe QA::Runtime::AllureReport do 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) } let(:rspec_config) { double('RSpec::Core::Configuration', 'add_formatter': nil, after: nil) }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'rspec/core/sandbox' require 'rspec/core/sandbox'
RSpec.describe QA::Specs::Helpers::ContextSelector do RSpec.describe QA::Specs::Helpers::ContextSelector do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
include QA::Specs::Helpers::RSpec include QA::Specs::Helpers::RSpec
around do |ex| around do |ex|
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'rspec/core/sandbox' require 'rspec/core/sandbox'
RSpec.describe QA::Specs::Helpers::Quarantine do RSpec.describe QA::Specs::Helpers::Quarantine do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
include QA::Specs::Helpers::RSpec include QA::Specs::Helpers::RSpec
around do |ex| around do |ex|
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Specs::ParallelRunner do RSpec.describe QA::Specs::ParallelRunner do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
before do before do
allow(QA::Runtime::Scenario).to receive(:attributes).and_return(parallel: true) allow(QA::Runtime::Scenario).to receive(:attributes).and_return(parallel: true)
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Support::AllureMetadataFormatter do describe QA::Support::AllureMetadataFormatter do
include Helpers::StubENV include QA::Support::Helpers::StubEnv
let(:formatter) { described_class.new(StringIO.new) } 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 # frozen_string_literal: true
require 'logger'
require 'timecop'
require 'active_support/core_ext/integer/time' require 'active_support/core_ext/integer/time'
RSpec.describe QA::Support::Repeater do RSpec.describe QA::Support::Repeater do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'logger'
require 'timecop'
RSpec.describe QA::Support::Retrier do RSpec.describe QA::Support::Retrier do
before do before do
logger = ::Logger.new $stdout logger = ::Logger.new $stdout
......
# frozen_string_literal: true # frozen_string_literal: true
require 'logger'
RSpec.describe QA::Support::Waiter do RSpec.describe QA::Support::Waiter do
before do before do
logger = ::Logger.new $stdout logger = ::Logger.new $stdout
......
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