Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-22 14:31:16 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-22 14:31:16 +0300
commit905c1110b08f93a19661cf42a276c7ea90d0a0ff (patch)
tree756d138db422392c00471ab06acdff92c5a9b69c /qa
parent50d93f8d1686950fc58dda4823c4835fd0d8c14b (diff)
Add latest changes from gitlab-org/gitlab@12-4-stable-ee
Diffstat (limited to 'qa')
-rw-r--r--qa/Dockerfile9
-rw-r--r--qa/knapsack/gitlab-ce/review-qa-all_master_report.json42
-rw-r--r--qa/knapsack/master_report.json (renamed from qa/qa/ce/knapsack/nightly_master_report.json)8
-rw-r--r--qa/qa.rb28
-rw-r--r--qa/qa/fixtures/ldap/admin/1_add_nodes.ldif7
-rw-r--r--qa/qa/fixtures/ldap/admin/2_add_users.ldif63
-rw-r--r--qa/qa/fixtures/ldap/admin/3_add_groups.ldif16
-rw-r--r--qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif7
-rw-r--r--qa/qa/fixtures/ldap/non_admin/2_add_users.ldif61
-rw-r--r--qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif16
-rw-r--r--qa/qa/git/repository.rb13
-rw-r--r--qa/qa/page/admin/menu.rb46
-rw-r--r--qa/qa/page/admin/overview/users/index.rb35
-rw-r--r--qa/qa/page/admin/overview/users/show.rb21
-rw-r--r--qa/qa/page/base.rb21
-rw-r--r--qa/qa/page/component/ci_badge_link.rb36
-rw-r--r--qa/qa/page/component/note.rb11
-rw-r--r--qa/qa/page/group/new.rb2
-rw-r--r--qa/qa/page/main/login.rb18
-rw-r--r--qa/qa/page/main/menu.rb21
-rw-r--r--qa/qa/page/merge_request/show.rb66
-rw-r--r--qa/qa/page/profile/emails.rb29
-rw-r--r--qa/qa/page/profile/menu.rb14
-rw-r--r--qa/qa/page/profile/password.rb23
-rw-r--r--qa/qa/page/project/issue/index.rb16
-rw-r--r--qa/qa/page/project/issue/show.rb70
-rw-r--r--qa/qa/page/project/job/show.rb29
-rw-r--r--qa/qa/page/project/milestone/index.rb2
-rw-r--r--qa/qa/page/project/pipeline/show.rb20
-rw-r--r--qa/qa/page/project/settings/main.rb9
-rw-r--r--qa/qa/page/project/settings/merge_request.rb13
-rw-r--r--qa/qa/page/project/settings/mirroring_repositories.rb55
-rw-r--r--qa/qa/page/project/settings/repository.rb6
-rw-r--r--qa/qa/page/validator.rb2
-rw-r--r--qa/qa/resource/fork.rb2
-rw-r--r--qa/qa/resource/issue.rb15
-rw-r--r--qa/qa/resource/project.rb9
-rw-r--r--qa/qa/resource/repository/commit.rb31
-rw-r--r--qa/qa/resource/repository/push.rb44
-rw-r--r--qa/qa/resource/repository/wiki_push.rb9
-rw-r--r--qa/qa/resource/runner.rb19
-rw-r--r--qa/qa/resource/sandbox.rb1
-rw-r--r--qa/qa/resource/tag.rb30
-rw-r--r--qa/qa/resource/user.rb7
-rw-r--r--qa/qa/resource/user_gpg.rb46
-rw-r--r--qa/qa/resource/wiki.rb9
-rw-r--r--qa/qa/runtime/api/client.rb2
-rw-r--r--qa/qa/runtime/browser.rb2
-rw-r--r--qa/qa/runtime/env.rb48
-rw-r--r--qa/qa/runtime/fixtures.rb15
-rw-r--r--qa/qa/runtime/gpg.rb37
-rw-r--r--qa/qa/runtime/release.rb2
-rw-r--r--qa/qa/runtime/user.rb4
-rw-r--r--qa/qa/scenario/test/integration/ldap_no_server.rb13
-rw-r--r--qa/qa/service/docker_run/base.rb43
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb58
-rw-r--r--qa/qa/service/docker_run/ldap.rb41
-rw-r--r--qa/qa/service/docker_run/node_js.rb38
-rw-r--r--qa/qa/service/runner.rb73
-rw-r--r--qa/qa/specs/features/api/1_manage/rate_limits_spec.rb7
-rw-r--r--qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb1
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb12
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--qa/qa/support/dates.rb21
-rw-r--r--qa/qa/support/page/logging.rb9
-rw-r--r--qa/qa/vendor/github/page/login.rb6
-rw-r--r--qa/qa/vendor/one_password/cli.rb34
-rwxr-xr-xqa/qa/vendor/one_password/darwin/opbin0 -> 10616336 bytes
-rwxr-xr-xqa/qa/vendor/one_password/linux/opbin0 -> 9003392 bytes
-rw-r--r--qa/spec/page/element_spec.rb1
-rw-r--r--qa/spec/resource/user_spec.rb4
-rw-r--r--qa/spec/scenario/test/integration/ldap_spec.rb8
79 files changed, 1265 insertions, 298 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 84dbfae1008..4845938b57e 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -50,12 +50,13 @@ RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
WORKDIR /home/gitlab/qa
COPY ./qa/Gemfile* /home/gitlab/qa/
COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/
-# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in CE
+# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS
+# The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS)
COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
COPY ./lib/gitlab.rb /home/gitlab/lib/
-COPY ./INSTALLATION_TYPE /home/gitlab/
-COPY ./VERSION /home/gitlab/
-RUN cd /home/gitlab/qa/ && bundle install
+COPY ./lib/gitlab/utils.rb /home/gitlab/lib/gitlab/
+COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
+RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --quiet
COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
diff --git a/qa/knapsack/gitlab-ce/review-qa-all_master_report.json b/qa/knapsack/gitlab-ce/review-qa-all_master_report.json
deleted file mode 100644
index f147346ba0f..00000000000
--- a/qa/knapsack/gitlab-ce/review-qa-all_master_report.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb": 9.697327613830566,
- "qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 46.54227638244629,
- "qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb": 10.214765310287476,
- "qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb": 7.882027864456177,
- "qa/specs/features/api/3_create/repository/files_spec.rb": 5.015859127044678,
- "qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 12.772682905197144,
- "qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 29.76174831390381,
- "qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb": 22.800872802734375,
- "qa/specs/features/browser_ui/1_manage/login/register_spec.rb": 22.320587396621704,
- "qa/specs/features/api/1_manage/users_spec.rb": 0.6089541912078857,
- "qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 0.9618203639984131,
- "qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 13.403101205825806,
- "qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb": 8.810423135757446,
- "qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 7.730542182922363,
- "qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb": 16.18057894706726,
- "qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb": 8.31815505027771,
- "qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 9.48607873916626,
- "qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 19.552733182907104,
- "qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb": 17.273863554000854,
- "qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb": 8.281434059143066,
- "qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb": 18.047621726989746,
- "qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 7.422840595245361,
- "qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb": 3.438166856765747,
- "qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb": 18.679633855819702,
- "qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 27.943300485610962,
- "qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb": 39.17585229873657,
- "qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 40.09336972236633,
- "qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 3.705310821533203,
- "qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb": 5.812374591827393,
- "qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 92.46774697303772,
- "qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb": 100.28881478309631,
- "qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 23.710937023162842,
- "qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb": 20.58603596687317,
- "qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb": 25.460349321365356,
- "qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 19.459370374679565,
- "qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 6.731764793395996,
- "qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 15.342933893203735,
- "qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 11.280649185180664,
- "qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 57.48992609977722,
- "qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 32.5517954826355
-} \ No newline at end of file
diff --git a/qa/qa/ce/knapsack/nightly_master_report.json b/qa/knapsack/master_report.json
index 08694f706de..467150e84c7 100644
--- a/qa/qa/ce/knapsack/nightly_master_report.json
+++ b/qa/knapsack/master_report.json
@@ -1,4 +1,12 @@
{
+ "qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 4.835599899291992,
+ "qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 69.85551619529724,
+ "qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 14.649160623550415,
+ "qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 12.790381908416748,
+ "qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 0.00018262863159179688,
+ "qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 59.73394823074341,
+ "qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 26.39240026473999,
+ "qa/specs/features/ee/browser_ui/secure/create_project_with_secure_spec.rb": 46.76790499687195,
"qa/specs/features/api/1_manage/users_spec.rb": 0.6089541912078857,
"qa/specs/features/api/3_create/repository/files_spec.rb": 5.015859127044678,
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 1.0199065208435059,
diff --git a/qa/qa.rb b/qa/qa.rb
index 6e87129641a..a628c0e0e3f 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -5,6 +5,7 @@ $: << File.expand_path(File.dirname(__FILE__))
Encoding.default_external = 'UTF-8'
require_relative '../lib/gitlab'
+require_relative '../lib/gitlab/utils'
require_relative '../config/initializers/0_inject_enterprise_edition_module'
module QA
@@ -23,6 +24,7 @@ module QA
autoload :Feature, 'qa/runtime/feature'
autoload :Fixtures, 'qa/runtime/fixtures'
autoload :Logger, 'qa/runtime/logger'
+ autoload :GPG, 'qa/runtime/gpg'
module API
autoload :Client, 'qa/runtime/api/client'
@@ -67,7 +69,9 @@ module QA
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 :UserGPG, 'qa/resource/user_gpg'
module Events
autoload :Base, 'qa/resource/events/base'
@@ -111,6 +115,7 @@ module QA
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 :OAuth, 'qa/scenario/test/integration/oauth'
@@ -287,6 +292,8 @@ module QA
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'
end
@@ -329,6 +336,13 @@ module QA
autoload :PerformanceBar, 'qa/page/admin/settings/component/performance_bar'
end
end
+
+ module Overview
+ module Users
+ autoload :Index, 'qa/page/admin/overview/users/index'
+ autoload :Show, 'qa/page/admin/overview/users/show'
+ end
+ end
end
module Mattermost
@@ -344,6 +358,7 @@ module QA
# Classes describing components that are used by several pages.
#
module Component
+ autoload :CiBadgeLink, 'qa/page/component/ci_badge_link'
autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :LazyLoader, 'qa/page/component/lazy_loader'
autoload :LegacyClonePanel, 'qa/page/component/legacy_clone_panel'
@@ -381,7 +396,6 @@ module QA
autoload :Shellout, 'qa/service/shellout'
autoload :KubernetesCluster, 'qa/service/kubernetes_cluster'
autoload :Omnibus, 'qa/service/omnibus'
- autoload :Runner, 'qa/service/runner'
module ClusterProvider
autoload :Base, 'qa/service/cluster_provider/base'
@@ -389,6 +403,13 @@ module QA
autoload :Minikube, 'qa/service/cluster_provider/minikube'
autoload :K3d, 'qa/service/cluster_provider/k3d'
end
+
+ module DockerRun
+ autoload :Base, 'qa/service/docker_run/base'
+ autoload :LDAP, 'qa/service/docker_run/ldap'
+ autoload :NodeJs, 'qa/service/docker_run/node_js'
+ autoload :GitlabRunner, 'qa/service/docker_run/gitlab_runner'
+ end
end
##
@@ -421,6 +442,10 @@ module QA
autoload :Login, 'qa/vendor/github/page/login'
end
end
+
+ module OnePassword
+ autoload :CLI, 'qa/vendor/one_password/cli'
+ end
end
# Classes that provide support to other parts of the framework.
@@ -430,6 +455,7 @@ module QA
autoload :Logging, 'qa/support/page/logging'
end
autoload :Api, 'qa/support/api'
+ autoload :Dates, 'qa/support/dates'
autoload :Waiter, 'qa/support/waiter'
autoload :Retrier, 'qa/support/retrier'
end
diff --git a/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif b/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif
new file mode 100644
index 00000000000..1476f53a0b9
--- /dev/null
+++ b/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif
@@ -0,0 +1,7 @@
+dn: ou=Global Groups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Global Groups
+
+dn: ou=People,ou=Global Groups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: People
diff --git a/qa/qa/fixtures/ldap/admin/2_add_users.ldif b/qa/qa/fixtures/ldap/admin/2_add_users.ldif
new file mode 100644
index 00000000000..00ba2fe8397
--- /dev/null
+++ b/qa/qa/fixtures/ldap/admin/2_add_users.ldif
@@ -0,0 +1,63 @@
+
+# 1. hruser1
+
+dn: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org
+cn: HR User 1
+givenName: HR
+sn: User1
+uid: hruser1
+uidNumber: 5000
+gidNumber: 10000
+homeDirectory: /home/hruser1
+mail: hruser1@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
+
+# 2. adminuser1
+
+dn: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org
+cn: Admin User 1
+givenName: Admin
+sn: User1
+uid: adminuser1
+uidNumber: 5009
+gidNumber: 10009
+homeDirectory: /home/adminuser1
+mail: adminuser1@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
+
+# 2. adminuser2
+
+dn: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org
+cn: Admin User 2
+givenName: Admin
+sn: User1
+uid: adminuser2
+uidNumber: 5010
+gidNumber: 10010
+homeDirectory: /home/adminuser2
+mail: adminuser2@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
diff --git a/qa/qa/fixtures/ldap/admin/3_add_groups.ldif b/qa/qa/fixtures/ldap/admin/3_add_groups.ldif
new file mode 100644
index 00000000000..db8b15bc9ea
--- /dev/null
+++ b/qa/qa/fixtures/ldap/admin/3_add_groups.ldif
@@ -0,0 +1,16 @@
+# 1. Human Resources
+
+dn: cn=Human Resources,ou=Global Groups,dc=example,dc=org
+objectClass: groupofnames
+cn: Human Resources
+description: Human Resources
+member: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org
+
+# 2. Admin
+
+dn: cn=AdminGroup,ou=Global Groups,dc=example,dc=org
+objectClass: groupofnames
+cn: AdminGroup
+description: Human Resources
+member: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org
+member: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org
diff --git a/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif b/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif
new file mode 100644
index 00000000000..1476f53a0b9
--- /dev/null
+++ b/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif
@@ -0,0 +1,7 @@
+dn: ou=Global Groups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Global Groups
+
+dn: ou=People,ou=Global Groups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: People
diff --git a/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif b/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif
new file mode 100644
index 00000000000..cf8accc97e1
--- /dev/null
+++ b/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif
@@ -0,0 +1,61 @@
+
+# 1. Human Resources
+
+dn: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org
+cn: HR User 1
+givenName: HR
+sn: User1
+uid: hruser1
+uidNumber: 5000
+gidNumber: 10000
+homeDirectory: /home/hruser1
+mail: hruser1@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
+
+# 2. Admin
+
+dn: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org
+cn: Admin User 1
+givenName: Admin
+sn: User1
+uid: adminuser1
+uidNumber: 5009
+gidNumber: 10009
+homeDirectory: /home/adminuser1
+mail: adminuser1@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
+
+dn: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org
+cn: Admin User 2
+givenName: Admin
+sn: User1
+uid: adminuser2
+uidNumber: 5010
+gidNumber: 10010
+homeDirectory: /home/adminuser2
+mail: adminuser2@example.org
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+loginShell: /bin/bash
+# hashed value for 'password'
+userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI
diff --git a/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif b/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif
new file mode 100644
index 00000000000..65487a3b2c6
--- /dev/null
+++ b/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif
@@ -0,0 +1,16 @@
+# 1. Human Resources
+
+dn: cn=Human Resources,ou=Global Groups,dc=example,dc=org
+objectClass: groupofnames
+cn: Human Resources
+description: Human Resources
+member: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org
+member: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org
+
+# 2. Admin
+
+dn: cn=AdminGroup,ou=Global Groups,dc=example,dc=org
+objectClass: groupofnames
+cn: AdminGroup
+description: Human Resources
+member: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 24b9fc67dd9..f56fab63198 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -14,7 +14,7 @@ module QA
include Scenario::Actable
RepositoryCommandError = Class.new(StandardError)
- attr_writer :use_lfs
+ attr_writer :use_lfs, :gpg_key_id
attr_accessor :env_vars
InvalidCredentialsError = Class.new(RuntimeError)
@@ -25,6 +25,7 @@ module QA
# .netrc can be utilised
self.env_vars = [%Q{HOME="#{tmp_home_dir}"}]
@use_lfs = false
+ @gpg_key_id = nil
end
def self.perform(*args)
@@ -75,7 +76,7 @@ module QA
end
def configure_identity(name, email)
- run(%Q{git config user.name #{name}})
+ run(%Q{git config user.name "#{name}"})
run(%Q{git config user.email #{email}})
end
@@ -99,10 +100,18 @@ module QA
git_lfs_track_result.to_s + git_add_result.to_s
end
+ def delete_tag(tag_name)
+ run(%Q{git push origin --delete #{tag_name}}).to_s
+ end
+
def commit(message)
run(%Q{git commit -m "#{message}"}).to_s
end
+ def commit_with_gpg(message)
+ run(%Q{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(which gpg) && git commit -S -m "#{message}"}).to_s
+ end
+
def push_changes(branch = 'master')
run("git push #{uri} #{branch}").to_s
end
diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb
index 5a18ebd7af8..2d1af78046d 100644
--- a/qa/qa/page/admin/menu.rb
+++ b/qa/qa/page/admin/menu.rb
@@ -6,12 +6,16 @@ module QA
class Menu < Page::Base
view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
element :admin_sidebar
- element :admin_sidebar_submenu
+ element :admin_sidebar_settings_submenu
element :admin_settings_item
element :admin_settings_repository_item
element :admin_settings_general_item
element :admin_settings_metrics_and_profiling_item
element :admin_settings_preferences_link
+ element :admin_monitoring_link
+ element :admin_sidebar_monitoring_submenu_content
+ element :admin_sidebar_overview_submenu_content
+ element :users_overview_link
end
view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
@@ -19,59 +23,65 @@ module QA
end
def go_to_preferences_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :admin_settings_preferences_link
end
end
end
def go_to_repository_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :admin_settings_repository_item
end
end
end
def go_to_integration_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :integration_settings_link
end
end
end
def go_to_general_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :admin_settings_general_item
end
end
end
def go_to_metrics_and_profiling_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :admin_settings_metrics_and_profiling_item
end
end
end
def go_to_network_settings
- hover_settings do
- within_submenu do
+ hover_element(:admin_settings_item) do
+ within_submenu(:admin_sidebar_settings_submenu) do
click_element :admin_settings_network_item
end
end
end
+ def go_to_users_overview
+ within_submenu(:admin_sidebar_overview_submenu_content) do
+ click_element :users_overview_link
+ end
+ end
+
private
- def hover_settings
+ def hover_element(element)
within_sidebar do
- scroll_to_element(:admin_settings_item)
- find_element(:admin_settings_item).hover
+ scroll_to_element(element)
+ find_element(element).hover
yield
end
@@ -83,8 +93,8 @@ module QA
end
end
- def within_submenu
- within_element(:admin_sidebar_submenu) do
+ def within_submenu(element)
+ within_element(element) do
yield
end
end
diff --git a/qa/qa/page/admin/overview/users/index.rb b/qa/qa/page/admin/overview/users/index.rb
new file mode 100644
index 00000000000..e374c1bf281
--- /dev/null
+++ b/qa/qa/page/admin/overview/users/index.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Overview
+ module Users
+ class Index < QA::Page::Base
+ view 'app/views/admin/users/index.html.haml' do
+ element :user_search_field
+ end
+
+ view 'app/views/admin/users/_user.html.haml' do
+ element :user_row_content
+ end
+
+ view 'app/views/admin/users/_user_detail.html.haml' do
+ element :username_link
+ end
+
+ def search_user(username)
+ find_element(:user_search_field).set(username).send_keys(:return)
+ end
+
+ def click_user(username)
+ within_element(:user_row_content, text: username) do
+ click_element(:username_link)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/overview/users/show.rb b/qa/qa/page/admin/overview/users/show.rb
new file mode 100644
index 00000000000..11ea7bcabc8
--- /dev/null
+++ b/qa/qa/page/admin/overview/users/show.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Overview
+ module Users
+ class Show < QA::Page::Base
+ view 'app/views/admin/users/_head.html.haml' do
+ element :impersonate_user_link
+ end
+
+ def click_impersonate_user
+ click_element(:impersonate_user_link)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 45496c6b245..71df90f2f42 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -15,6 +15,10 @@ module QA
def_delegators :evaluator, :view, :views
+ def assert_no_element(name)
+ assert_no_selector(element_selector_css(name))
+ end
+
def refresh
page.refresh
end
@@ -90,8 +94,8 @@ module QA
end
# replace with (..., page = self.class)
- def click_element(name, page = nil)
- find_element(name).click
+ def click_element(name, page = nil, text: nil)
+ find_element(name, text: text).click
page.validate_elements_present! if page
end
@@ -102,9 +106,9 @@ module QA
def select_element(name, value)
element = find_element(name)
- return if element.text.downcase.to_s == value.to_s
+ return if element.text == value
- element.select value.to_s.capitalize
+ element.select value
end
def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
@@ -127,6 +131,10 @@ module QA
has_no_css?('.fa-spinner', wait: Capybara.default_max_wait_time)
end
+ def finished_loading_block?
+ has_no_css?('.fa-spinner.block-loading', wait: Capybara.default_max_wait_time)
+ end
+
def wait_for_animated_element(name)
# It would be ideal if we could detect when the animation is complete
# but in some cases there's nothing we can easily access via capybara
@@ -143,6 +151,11 @@ module QA
end
def within_element_by_index(name, index)
+ # Finding all elements can be flaky if the elements don't all load
+ # immediately. So we wait for any to appear before trying to find a
+ # specific one.
+ has_element?(name)
+
page.within all_elements(name)[index] do
yield
end
diff --git a/qa/qa/page/component/ci_badge_link.rb b/qa/qa/page/component/ci_badge_link.rb
new file mode 100644
index 00000000000..aad8dc1d3df
--- /dev/null
+++ b/qa/qa/page/component/ci_badge_link.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module CiBadgeLink
+ COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
+ PASSED_STATUS = 'passed'.freeze
+
+ def self.included(base)
+ base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
+ element :status_badge
+ end
+ end
+
+ def status_badge
+ find_element(:status_badge).text
+ end
+
+ def successful?(timeout: 60)
+ raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
+
+ status_badge == PASSED_STATUS
+ end
+
+ private
+
+ def completed?(timeout: 60)
+ wait(reload: false, max: timeout) do
+ COMPLETED_STATUSES.include?(status_badge)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 34fde045091..c85fa690d6c 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -10,6 +10,10 @@ module QA
element :discussion_option
end
+ base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
+ element :discussion_content
+ end
+
base.view 'app/assets/javascripts/notes/components/note_actions.vue' do
element :note_edit_button
end
@@ -21,6 +25,7 @@ module QA
base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
element :discussion_reply_tab
+ element :resolve_discussion_button
end
base.view 'app/assets/javascripts/notes/components/toggle_replies_widget.vue' do
@@ -54,6 +59,12 @@ module QA
click_element :reply_comment_button
end
+ def resolve_discussion_at_index(index)
+ within_element_by_index(:discussion_content, index) do
+ click_element :resolve_discussion_button
+ end
+ end
+
def collapse_replies
click_element :collapse_replies
end
diff --git a/qa/qa/page/group/new.rb b/qa/qa/page/group/new.rb
index 9e37d14fbf3..e01b8754d55 100644
--- a/qa/qa/page/group/new.rb
+++ b/qa/qa/page/group/new.rb
@@ -11,7 +11,7 @@ module QA
end
view 'app/views/groups/new.html.haml' do
- element :create_group_button, "submit 'Create group'" # rubocop:disable QA/ElementWithPattern
+ element :create_group_button, "submit _('Create group')" # rubocop:disable QA/ElementWithPattern
element :visibility_radios, 'visibility_level:' # rubocop:disable QA/ElementWithPattern
end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index ca93663dba2..6e266e26d78 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -42,7 +42,7 @@ module QA
element :login_page, required: true
end
- def sign_in_using_credentials(user = nil)
+ def sign_in_using_credentials(user: nil, skip_page_validation: false)
# Don't try to log-in if we're already logged-in
return if Page::Main::Menu.perform(&:signed_in?)
@@ -52,9 +52,9 @@ module QA
raise NotImplementedError if Runtime::User.ldap_user? && user&.credentials_given?
if Runtime::User.ldap_user?
- sign_in_using_ldap_credentials(user || Runtime::User)
+ sign_in_using_ldap_credentials(user: user || Runtime::User)
else
- sign_in_using_gitlab_credentials(user || Runtime::User)
+ sign_in_using_gitlab_credentials(user: user || Runtime::User, skip_page_validation: skip_page_validation)
end
end
end
@@ -68,13 +68,13 @@ module QA
using_wait_time 0 do
set_initial_password_if_present
- sign_in_using_gitlab_credentials(admin)
+ sign_in_using_gitlab_credentials(user: admin)
end
Page::Main::Menu.perform(&:has_personal_area?)
end
- def sign_in_using_ldap_credentials(user)
+ def sign_in_using_ldap_credentials(user:)
Page::Main::Menu.perform(&:sign_out_if_signed_in)
using_wait_time 0 do
@@ -87,7 +87,7 @@ module QA
click_element :sign_in_button
end
- Page::Main::Menu.perform(&:has_personal_area?)
+ Page::Main::Menu.perform(&:signed_in?)
end
def self.path
@@ -148,18 +148,18 @@ module QA
def sign_out_and_sign_in_as(user:)
Menu.perform(&:sign_out_if_signed_in)
has_sign_in_tab?
- sign_in_using_credentials(user)
+ sign_in_using_credentials(user: user)
end
private
- def sign_in_using_gitlab_credentials(user)
+ def sign_in_using_gitlab_credentials(user:, skip_page_validation: false)
switch_to_sign_in_tab if has_sign_in_tab?
switch_to_standard_tab if has_standard_tab?
fill_element :login_field, user.username
fill_element :password_field, user.password
- click_element :sign_in_button, Page::Main::Menu
+ click_element :sign_in_button, !skip_page_validation && Page::Main::Menu
end
def set_initial_password_if_present
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 751b67d7695..024f56db8e2 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -13,12 +13,14 @@ module QA
element :navbar, required: true
element :user_avatar, required: true
element :user_menu, required: true
+ element :stop_impersonation_link
end
view 'app/views/layouts/nav/_dashboard.html.haml' do
element :admin_area_link
element :projects_dropdown, required: true
element :groups_dropdown, required: true
+ element :more_dropdown, required: true
element :snippets_link
end
@@ -51,6 +53,13 @@ module QA
end
end
+ def go_to_snippets
+ within_top_menu do
+ click_element :more_dropdown
+ click_element :snippets_link
+ end
+ end
+
def click_admin_area
within_top_menu { click_element :admin_area_link }
end
@@ -79,10 +88,6 @@ module QA
end
end
- def click_snippets_link
- click_element :snippets_link
- end
-
def search_for(term)
fill_element :search_term_field, "#{term}\n"
end
@@ -95,6 +100,14 @@ module QA
has_element?(:admin_area_link, wait: wait)
end
+ def has_no_admin_area_link?(wait: Capybara.default_max_wait_time)
+ has_no_element?(:admin_area_link, wait: wait)
+ end
+
+ def click_stop_impersonation_link
+ click_element(:stop_impersonation_link)
+ end
+
private
def within_top_menu
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 6e550805f9f..14b8c420b16 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -6,6 +6,17 @@ module QA
class Show < Page::Base
include Page::Component::Note
+ view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue' do
+ element :dropdown_toggle
+ element :download_email_patches
+ element :download_plain_diff
+ end
+
+ view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue' do
+ element :merge_request_pipeline_info_content
+ element :pipeline_link
+ end
+
view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do
element :merge_button
element :fast_forward_message, 'Fast-forward merge without a merge commit' # rubocop:disable QA/ElementWithPattern
@@ -27,12 +38,6 @@ module QA
element :squash_checkbox
end
- view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue' do
- element :dropdown_toggle
- element :download_email_patches
- element :download_plain_diff
- end
-
view 'app/views/projects/merge_requests/show.html.haml' do
element :notes_tab
element :diffs_tab
@@ -55,6 +60,18 @@ module QA
element :edit_button
end
+ def click_discussions_tab
+ click_element :notes_tab
+ end
+
+ def click_diffs_tab
+ click_element :diffs_tab
+ end
+
+ def click_pipeline_link
+ click_element :pipeline_link
+ end
+
def fast_forward_possible?
has_no_text?('Fast-forward merge is not possible')
end
@@ -111,6 +128,11 @@ module QA
end
end
+ def has_pipeline_status?(text)
+ # Pipelines can be slow, so we wait a bit longer than the usual 10 seconds
+ has_element?(:merge_request_pipeline_info_content, text: text, wait: 30)
+ end
+
def has_title?(title)
has_element?(:title, text: title)
end
@@ -120,17 +142,7 @@ module QA
end
def try_to_merge!
- # The merge button is disabled on load
- wait do
- has_element?(:merge_button)
- end
-
- # The merge button is enabled via JS
- wait(reload: false) do
- !find_element(:merge_button).disabled?
- end
-
- merge_immediately
+ merge_immediately if ready_to_merge?
end
def merge!
@@ -157,14 +169,6 @@ module QA
click_element :squash_checkbox
end
- def click_discussions_tab
- click_element :notes_tab
- end
-
- def click_diffs_tab
- click_element :diffs_tab
- end
-
def add_comment_to_diff(text)
wait(interval: 5) do
has_text?("No newline at end of file")
@@ -178,6 +182,18 @@ module QA
click_element :edit_button
end
+ def ready_to_merge?
+ # The merge button is disabled on load
+ wait do
+ has_element?(:merge_button)
+ end
+
+ # The merge button is enabled via JS
+ wait(reload: false) do
+ !find_element(:merge_button).disabled?
+ end
+ end
+
def view_email_patches
click_element :dropdown_toggle
visit_link_in_element(:download_email_patches)
diff --git a/qa/qa/page/profile/emails.rb b/qa/qa/page/profile/emails.rb
new file mode 100644
index 00000000000..c20bc6a5c57
--- /dev/null
+++ b/qa/qa/page/profile/emails.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Profile
+ class Emails < Page::Base
+ view 'app/views/profiles/emails/index.html.haml' do
+ element :email_address_field
+ element :add_email_address_button
+ element :email_row_content
+ element :delete_email_link
+ end
+
+ def add_email_address(email_address)
+ find_element(:email_address_field).set email_address
+ click_element(:add_email_address_button)
+ end
+
+ def delete_email_address(email_address)
+ page.accept_alert do
+ within_element(:email_row_content, text: email_address) do
+ click_element(:delete_email_link)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/profile/menu.rb b/qa/qa/page/profile/menu.rb
index 99a795a23ef..e7baaf3d40a 100644
--- a/qa/qa/page/profile/menu.rb
+++ b/qa/qa/page/profile/menu.rb
@@ -9,6 +9,8 @@ module QA
element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern
element :top_level_items, '.sidebar-top-level-items' # rubocop:disable QA/ElementWithPattern
element :ssh_keys, 'SSH Keys' # rubocop:disable QA/ElementWithPattern
+ element :profile_emails_link
+ element :profile_password_link
end
def click_access_tokens
@@ -23,6 +25,18 @@ module QA
end
end
+ def click_emails
+ within_sidebar do
+ click_element(:profile_emails_link)
+ end
+ end
+
+ def click_password
+ within_sidebar do
+ click_element(:profile_password_link)
+ end
+ end
+
private
def within_sidebar
diff --git a/qa/qa/page/profile/password.rb b/qa/qa/page/profile/password.rb
new file mode 100644
index 00000000000..ce062f39edb
--- /dev/null
+++ b/qa/qa/page/profile/password.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Profile
+ class Password < Page::Base
+ view 'app/views/profiles/passwords/edit.html.haml' do
+ element :current_password_field
+ element :new_password_field
+ element :confirm_password_field
+ element :save_password_button
+ end
+
+ def update_password(new_password, current_password)
+ find_element(:current_password_field).set current_password
+ find_element(:new_password_field).set new_password
+ find_element(:confirm_password_field).set new_password
+ click_element(:save_password_button)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index f74366f6967..befee25b37a 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -5,14 +5,30 @@ module QA
module Project
module Issue
class Index < Page::Base
+ view 'app/helpers/projects_helper.rb' do
+ element :assignee_link
+ end
+
view 'app/views/projects/issues/_issue.html.haml' do
element :issue_link, 'link_to issue.title' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/shared/issuable/_assignees.html.haml' do
+ element :avatar_counter
+ end
+
view 'app/views/shared/issuable/_nav.html.haml' do
element :closed_issues_link
end
+ def assignee_link_count
+ all_elements(:assignee_link).count
+ end
+
+ def avatar_counter
+ find_element(:avatar_counter)
+ end
+
def click_issue_link(title)
click_link(title)
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index e5e26b1864b..d2732eb7dd2 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -22,30 +22,68 @@ module QA
element :noteable_note_item
end
+ view 'app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue' do
+ element :avatar_image
+ end
+
+ view 'app/assets/javascripts/sidebar/components/assignees/assignee_title.vue' do
+ element :assignee_edit_link
+ element :assignee_title
+ end
+
+ view 'app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue' do
+ element :more_assignees_link
+ end
+
+ view 'app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue' do
+ element :remove_related_issue_button
+ end
+
view 'app/helpers/dropdowns_helper.rb' do
element :dropdown_input_field
end
- view 'app/views/shared/notes/_form.html.haml' do
- element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern
- element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
+ view 'app/views/shared/issuable/_close_reopen_button.html.haml' do
+ element :reopen_issue_button
end
view 'app/views/shared/issuable/_sidebar.html.haml' do
+ element :assignee_block
element :labels_block
element :edit_link_labels
element :dropdown_menu_labels
element :milestone_link
end
- view 'app/views/shared/issuable/_close_reopen_button.html.haml' do
- element :reopen_issue_button
+ view 'app/views/shared/notes/_form.html.haml' do
+ element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern
+ element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
+ end
+
+ def assign(user)
+ click_element(:assignee_edit_link)
+ select_user(user.username)
+ click_body
+ end
+
+ def assignee_title
+ find_element(:assignee_title)
+ end
+
+ def avatar_image_count
+ wait_assignees_block_finish_loading do
+ all_elements(:avatar_image).count
+ end
end
def click_milestone_link
click_element(:milestone_link)
end
+ def click_remove_related_issue_button
+ click_element(:remove_related_issue_button)
+ end
+
# Adds a comment to an issue
# attachment option should be an absolute path
def comment(text, attachment: nil, filter: :all_activities)
@@ -66,6 +104,10 @@ module QA
end
end
+ def more_assignees_link
+ find_element(:more_assignees_link)
+ end
+
def select_all_activities_filter
select_filter_with_text('Show all activity')
end
@@ -103,6 +145,10 @@ module QA
find_element(:labels_block)
end
+ def toggle_more_assignees_link
+ click_element(:more_assignees_link)
+ end
+
private
def select_filter_with_text(text)
@@ -112,6 +158,20 @@ module QA
find_element(:filter_options, text: text).click
end
end
+
+ def select_user(username)
+ find("#{element_selector_css(:assignee_block)} input").set(username)
+ find('.dropdown-menu-user-link', text: "@#{username}").click
+ end
+
+ def wait_assignees_block_finish_loading
+ within_element(:assignee_block) do
+ wait(reload: false, max: 10, interval: 1) do
+ finished_loading_block?
+ yield
+ end
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 5853f487f0b..cf847710024 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -3,17 +3,12 @@
module QA::Page
module Project::Job
class Show < QA::Page::Base
- COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
- PASSED_STATUS = 'passed'.freeze
+ include Component::CiBadgeLink
view 'app/assets/javascripts/jobs/components/job_log.vue' do
element :build_trace
end
- view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
- element :status_badge
- end
-
view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do
element :pipeline_path
end
@@ -26,8 +21,16 @@ module QA::Page
end
# Reminder: You may wish to wait for a particular job status before checking output
- def output
- find_element(:build_trace).text
+ def output(wait: 5)
+ result = ''
+
+ wait(reload: false, max: wait, interval: 1) do
+ result = find_element(:build_trace).text
+
+ !result.empty?
+ end
+
+ result
end
private
@@ -37,16 +40,6 @@ module QA::Page
has_element?(:build_trace, wait: 1)
end
end
-
- def completed?(timeout: 60)
- wait(reload: false, max: timeout) do
- COMPLETED_STATUSES.include?(status_badge)
- end
- end
-
- def status_badge
- find_element(:status_badge).text
- end
end
end
end
diff --git a/qa/qa/page/project/milestone/index.rb b/qa/qa/page/project/milestone/index.rb
index 8ad7689ce70..6895c44f72f 100644
--- a/qa/qa/page/project/milestone/index.rb
+++ b/qa/qa/page/project/milestone/index.rb
@@ -17,5 +17,3 @@ module QA
end
end
end
-
-QA::Page::Project::Milestone::Index.prepend_if_ee('QA::EE::Page::Project::Milestone::Index')
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 3dca47a57e9..fd29c5eacdc 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -3,6 +3,8 @@
module QA::Page
module Project::Pipeline
class Show < QA::Page::Base
+ include Component::CiBadgeLink
+
view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do
element :pipeline_header, /header class.*ci-header-container.*/ # rubocop:disable QA/ElementWithPattern
end
@@ -16,6 +18,10 @@ module QA::Page
element :job_link
end
+ view 'app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue' do
+ element :linked_pipeline_button
+ end
+
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
@@ -38,6 +44,14 @@ module QA::Page
end
end
+ def has_job?(job_name)
+ has_element?(:job_link, text: job_name)
+ end
+
+ def has_no_job?(job_name)
+ has_no_element?(:job_link, text: job_name)
+ end
+
def has_tag?(tag_name)
within_element(:pipeline_badges) do
has_selector?('.badge', text: tag_name)
@@ -45,7 +59,11 @@ module QA::Page
end
def click_job(job_name)
- find_element(:job_link, text: job_name).click
+ click_element(:job_link, text: job_name)
+ end
+
+ def click_linked_job(project_name)
+ click_element(:linked_pipeline_button, text: /#{project_name}/)
end
def click_on_first_job
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
index 6b26c82a46f..18d55598d90 100644
--- a/qa/qa/page/project/settings/main.rb
+++ b/qa/qa/page/project/settings/main.rb
@@ -11,6 +11,7 @@ module QA
view 'app/views/projects/edit.html.haml' do
element :advanced_settings
+ element :merge_request_settings
end
view 'app/views/projects/settings/_general.html.haml' do
@@ -41,6 +42,12 @@ module QA
end
end
+ def expand_merge_requests_settings(&block)
+ expand_section(:merge_request_settings) do
+ MergeRequest.perform(&block)
+ end
+ end
+
def expand_visibility_project_features_permissions(&block)
expand_section(:visibility_features_permissions_content) do
VisibilityFeaturesPermissions.perform(&block)
@@ -51,5 +58,3 @@ module QA
end
end
end
-
-QA::Page::Project::Settings::Main.prepend_if_ee('QA::EE::Page::Project::Settings::Main')
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index f92528c4262..7da2c9d168c 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -8,7 +8,6 @@ module QA
include Common
view 'app/views/projects/edit.html.haml' do
- element :merge_request_settings
element :save_merge_request_changes
end
@@ -16,14 +15,18 @@ module QA
element :radio_button_merge_ff
end
+ def click_save_changes
+ click_element :save_merge_request_changes
+ end
+
def enable_ff_only
- expand_section(:merge_request_settings) do
- click_element :radio_button_merge_ff
- click_element :save_merge_request_changes
- end
+ click_element :radio_button_merge_ff
+ click_save_changes
end
end
end
end
end
end
+
+QA::Page::Project::Settings::MergeRequest.prepend_if_ee("QA::EE::Page::Project::Settings::MergeRequest")
diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb
index 441235afca8..4afe042d9fb 100644
--- a/qa/qa/page/project/settings/mirroring_repositories.rb
+++ b/qa/qa/page/project/settings/mirroring_repositories.rb
@@ -15,7 +15,9 @@ module QA
element :mirror_repository_button
element :mirror_repository_url_cell
element :mirror_last_update_at_cell
+ element :mirror_error_badge
element :mirrored_repository_row
+ element :copy_public_key_button
end
view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do
@@ -24,6 +26,17 @@ module QA
view 'app/views/shared/_remote_mirror_update_button.html.haml' do
element :update_now_button
+ element :updating_button
+ end
+
+ view 'app/views/projects/mirrors/_ssh_host_keys.html.haml' do
+ element :detect_host_keys
+ element :fingerprints_list
+ end
+
+ view 'app/views/projects/mirrors/_authentication_method.html.haml' do
+ element :authentication_method
+ element :password
end
def repository_url=(value)
@@ -35,17 +48,40 @@ module QA
end
def mirror_direction=(value)
- raise ArgumentError, "Mirror direction must be :push or :pull" unless [:push, :pull].include? value
+ raise ArgumentError, "Mirror direction must be 'Push' or 'Pull'" unless %w(Push Pull).include? value
select_element(:mirror_direction, value)
+
+ # Changing the mirror direction causes the fields below to change,
+ # and that change is animated, so we need to wait for the animation
+ # to complete otherwise changes to those fields could fail
+ wait_for_animated_element :authentication_method
end
def authentication_method=(value)
- raise ArgumentError, "Authentication method must be :password or :none" unless [:password, :none].include? value
+ raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'" unless %w(Password None SSH\ public\ key).include? value
select_element(:authentication_method, value)
end
+ def public_key(url)
+ row_index = find_repository_row_index url
+
+ within_element_by_index(:mirrored_repository_row, row_index) do
+ find_element(:copy_public_key_button)['data-clipboard-text']
+ end
+ end
+
+ def detect_host_keys
+ click_element :detect_host_keys
+
+ # The host key detection process is interrupted if we navigate away
+ # from the page before the fingerprint appears.
+ wait(max: 5) do
+ find_element(:fingerprints_list).has_text? /.*/
+ end
+ end
+
def mirror_repository
click_element :mirror_repository_button
end
@@ -54,7 +90,9 @@ module QA
row_index = find_repository_row_index url
within_element_by_index(:mirrored_repository_row, row_index) do
- click_element :update_now_button
+ # When a repository is first mirrored, the update process might
+ # already be started, so the button is already "clicked"
+ click_element :update_now_button unless has_element? :updating_button
end
# Wait a few seconds for the sync to occur and then refresh the page
@@ -72,16 +110,19 @@ module QA
# Fail early if the page still shows that there has been no update
within_element_by_index(:mirrored_repository_row, row_index) do
find_element(:mirror_last_update_at_cell, wait: 0).assert_no_text('Never')
+ assert_no_element(:mirror_error_badge)
end
end
private
def find_repository_row_index(target_url)
- all_elements(:mirror_repository_url_cell).index do |url|
- # The url might be a sanitized url but the target_url won't be so
- # we compare just the paths instead of the full url
- URI.parse(url.text).path == target_url.path
+ wait(max: 5, reload: false) do
+ all_elements(:mirror_repository_url_cell).index do |url|
+ # The url might be a sanitized url but the target_url won't be so
+ # we compare just the paths instead of the full url
+ URI.parse(url.text).path == target_url.path
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index 437a945aceb..58ed37870b7 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -16,7 +16,7 @@ module QA
end
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
- element :mirroring_repositories_settings
+ element :mirroring_repositories_settings_section
end
def expand_deploy_keys(&block)
@@ -38,7 +38,7 @@ module QA
end
def expand_mirroring_repositories(&block)
- expand_section(:mirroring_repositories_settings) do
+ expand_section(:mirroring_repositories_settings_section) do
MirroringRepositories.perform(&block)
end
end
@@ -47,3 +47,5 @@ module QA
end
end
end
+
+QA::Page::Project::Settings::Repository.prepend_if_ee('QA::EE::Page::Project::Settings::Repository')
diff --git a/qa/qa/page/validator.rb b/qa/qa/page/validator.rb
index 9b2d0a1a41d..75e48b5785e 100644
--- a/qa/qa/page/validator.rb
+++ b/qa/qa/page/validator.rb
@@ -17,7 +17,7 @@ module QA
def constants
@consts ||= @module.constants.map do |const|
- @module.const_get(const)
+ @module.const_get(const, false)
end
end
diff --git a/qa/qa/resource/fork.rb b/qa/qa/resource/fork.rb
index 54b0c2d0059..d203e8eb264 100644
--- a/qa/qa/resource/fork.rb
+++ b/qa/qa/resource/fork.rb
@@ -30,7 +30,7 @@ module QA
Page::Main::Menu.perform(&:sign_out)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login|
- login.sign_in_using_credentials(user)
+ login.sign_in_using_credentials(user: user)
end
upstream.project.visit!
diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb
index a894e5c2033..0817a9de06f 100644
--- a/qa/qa/resource/issue.rb
+++ b/qa/qa/resource/issue.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'securerandom'
+
module QA
module Resource
class Issue < Base
@@ -13,11 +15,15 @@ module QA
end
attribute :id
+ attribute :iid
+ attribute :assignee_ids
attribute :labels
attribute :title
def initialize
+ @assignee_ids = []
@labels = []
+ @title = "Issue title #{SecureRandom.hex(8)}"
end
def fabricate!
@@ -25,10 +31,10 @@ module QA
Page::Project::Show.perform(&:go_to_new_issue)
- Page::Project::Issue::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_title(@title)
- page.add_description(@description)
- page.create_new_issue
+ Page::Project::Issue::New.perform do |new_page|
+ new_page.add_title(@title)
+ new_page.add_description(@description)
+ new_page.create_new_issue
end
end
@@ -42,6 +48,7 @@ module QA
def api_post_body
{
+ assignee_ids: assignee_ids,
labels: labels,
title: title
}.tap do |hash|
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index a0389390c83..caaa766e982 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -88,6 +88,10 @@ module QA
"#{api_get_path}/members"
end
+ def api_runners_path
+ "#{api_get_path}/runners"
+ end
+
def api_post_path
'/projects'
end
@@ -108,6 +112,11 @@ module QA
post_body
end
+ def runners
+ response = get Runtime::API::Request.new(api_client, api_runners_path).url
+ parse_body(response)
+ end
+
def share_with_group(invitee, access_level = Resource::Members::AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, "/projects/#{id}/share").url, { group_id: invitee.id, group_access: access_level }
end
diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb
index 61c2ad6bfc0..4b5e8535ade 100644
--- a/qa/qa/resource/repository/commit.rb
+++ b/qa/qa/resource/repository/commit.rb
@@ -21,14 +21,16 @@ module QA
@commit_message = 'QA Test - Commit message'
end
- def files=(files)
- if !files.is_a?(Array) ||
- files.empty? ||
- files.any? { |file| !file.has_key?(:file_path) || !file.has_key?(:content) }
- raise ArgumentError, "Please provide an array of hashes e.g.: [{file_path: 'file1', content: 'foo'}]"
- end
+ def add_files(files)
+ validate_files!(files)
+
+ @add_files = files
+ end
+
+ def update_files(files)
+ validate_files!(files)
- @files = files
+ @update_files = files
end
def resource_web_url(resource)
@@ -56,8 +58,19 @@ module QA
end
def actions
- @files.map do |file|
- file.merge({ action: "create" })
+ pending_actions = []
+ pending_actions << @add_files.map { |file| file.merge({ action: "create" }) } if @add_files
+ pending_actions << @update_files.map { |file| file.merge({ action: "update" }) } if @update_files
+ pending_actions.flatten
+ end
+
+ private
+
+ def validate_files!(files)
+ if !files.is_a?(Array) ||
+ files.empty? ||
+ files.any? { |file| !file.has_key?(:file_path) || !file.has_key?(:content) }
+ raise ArgumentError, "Please provide an array of hashes e.g.: [{file_path: 'file1', content: 'foo'}]"
end
end
end
diff --git a/qa/qa/resource/repository/push.rb b/qa/qa/resource/repository/push.rb
index a5827fb6e73..68674248be2 100644
--- a/qa/qa/resource/repository/push.rb
+++ b/qa/qa/resource/repository/push.rb
@@ -8,9 +8,9 @@ module QA
class Push < Base
attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :output, :repository_http_uri,
- :repository_ssh_uri, :ssh_key, :user, :use_lfs
+ :repository_ssh_uri, :ssh_key, :user, :use_lfs, :tag_name
- attr_writer :remote_branch
+ attr_writer :remote_branch, :gpg_key_id
def initialize
@file_name = 'file.txt'
@@ -21,6 +21,8 @@ module QA
@repository_http_uri = ""
@ssh_key = nil
@use_lfs = false
+ @tag_name = nil
+ @gpg_key_id = nil
end
def remote_branch
@@ -67,29 +69,43 @@ module QA
email = user.email
end
+ unless @gpg_key_id.nil?
+ repository.gpg_key_id = @gpg_key_id
+ end
+
@output += repository.clone
repository.configure_identity(username, email)
@output += repository.checkout(branch_name, new_branch: new_branch)
- if @directory
- @directory.each_child do |f|
- @output += repository.add_file(f.basename, f.read) if f.file?
- end
- elsif @files
- @files.each do |f|
- repository.add_file(f[:name], f[:content])
- end
+ if @tag_name
+ @output += repository.delete_tag(@tag_name)
else
- @output += repository.add_file(file_name, file_content)
- end
+ if @directory
+ @directory.each_child do |f|
+ @output += repository.add_file(f.basename, f.read) if f.file?
+ end
+ elsif @files
+ @files.each do |f|
+ repository.add_file(f[:name], f[:content])
+ end
+ else
+ @output += repository.add_file(file_name, file_content)
+ end
- @output += repository.commit(commit_message)
- @output += repository.push_changes("#{branch_name}:#{remote_branch}")
+ @output += commit_to repository
+ @output += repository.push_changes("#{branch_name}:#{remote_branch}")
+ end
repository.delete_ssh_key
end
end
+
+ private
+
+ def commit_to(repository)
+ @gpg_key_id.nil? ? repository.commit(@commit_message) : repository.commit_with_gpg(@commit_message)
+ end
end
end
end
diff --git a/qa/qa/resource/repository/wiki_push.rb b/qa/qa/resource/repository/wiki_push.rb
index 8edaff70ac6..e926c00d380 100644
--- a/qa/qa/resource/repository/wiki_push.rb
+++ b/qa/qa/resource/repository/wiki_push.rb
@@ -25,14 +25,7 @@ module QA
end
def repository_ssh_uri
- @repository_ssh_uri ||= begin
- wiki.visit!
- Page::Project::Wiki::Show.act do
- click_clone_repository
- choose_repository_clone_ssh
- repository_location.uri
- end
- end
+ @repository_ssh_uri ||= wiki.repository_ssh_location.uri
end
def fabricate!
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index 9c2e138bade..1be2429bc04 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -6,8 +6,9 @@ module QA
module Resource
class Runner < Base
attr_writer :name, :tags, :image
- attr_accessor :config
+ attr_accessor :config, :token
+ attribute :id
attribute :project do
Project.fabricate_via_api! do |resource|
resource.name = 'project-with-ci-cd'
@@ -28,9 +29,9 @@ module QA
end
def fabricate_via_api!
- Service::Runner.new(name).tap do |runner|
+ Service::DockerRun::GitlabRunner.new(name).tap do |runner|
runner.pull
- runner.token = project.runners_token
+ runner.token = @token ||= project.runners_token
runner.address = Runtime::Scenario.gitlab_address
runner.tags = tags
runner.image = image
@@ -40,6 +41,18 @@ module QA
end
end
+ def remove_via_api!
+ @id = project.runners.find { |runner| runner[:description] == name }[:id]
+
+ super
+
+ Service::DockerRun::GitlabRunner.new(name).remove!
+ end
+
+ def api_delete_path
+ "/runners/#{id}"
+ end
+
def api_get_path
end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 47bd86440a0..6ee3dcf350f 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -10,6 +10,7 @@ module QA
attr_accessor :path
attribute :id
+ attribute :runners_token
def initialize
@path = Runtime::Namespace.sandbox_name
diff --git a/qa/qa/resource/tag.rb b/qa/qa/resource/tag.rb
new file mode 100644
index 00000000000..ac4fccec525
--- /dev/null
+++ b/qa/qa/resource/tag.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Tag < Base
+ attr_accessor :project, :name, :ref
+
+ def resource_web_url(resource)
+ super
+ rescue ResourceURLMissingError
+ # this particular resource does not expose a web_url property
+ end
+
+ def api_get_path
+ "/projects/#{project.id}/repository/tags/#{name}"
+ end
+
+ def api_post_path
+ "/projects/#{project.id}/repository/tags"
+ end
+
+ def api_post_body
+ {
+ tag_name: name,
+ ref: ref
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 911d2b2f506..dcf145c9882 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -26,7 +26,7 @@ module QA
end
def name
- @name ||= api_resource&.dig(:name) || username
+ @name ||= api_resource&.dig(:name) || "QA User #{unique_id}"
end
def email
@@ -53,7 +53,7 @@ module QA
if credentials_given?
Page::Main::Login.perform do |login|
- login.sign_in_using_credentials(self)
+ login.sign_in_using_credentials(user: self)
end
else
Page::Main::Login.perform do |login|
@@ -91,9 +91,8 @@ module QA
def self.fabricate_or_use(username = nil, password = nil)
if Runtime::Env.signup_disabled?
- self.new.tap do |user|
+ self.fabricate_via_api! do |user|
user.username = username
- user.password = password
end
else
self.fabricate!
diff --git a/qa/qa/resource/user_gpg.rb b/qa/qa/resource/user_gpg.rb
new file mode 100644
index 00000000000..25d74ad8ce5
--- /dev/null
+++ b/qa/qa/resource/user_gpg.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class UserGPG < Base
+ attr_accessor :id, :gpg
+ attr_reader :key_id
+
+ def initialize
+ @gpg = Runtime::GPG.new
+ @key_id = @gpg.key_id
+ end
+
+ def fabricate_via_api!
+ super
+ @id = self.api_response[:id]
+ rescue ResourceFabricationFailedError => error
+ if error.message.include? 'has already been taken'
+ self
+ else
+ raise ResourceFabricationFailedError error
+ end
+ end
+
+ def resource_web_url(resource)
+ super
+ rescue ResourceURLMissingError
+ # this particular resource does not expose a web_url property
+ end
+
+ def api_get_path
+ "/user/gpg_keys/#{@id}"
+ end
+
+ def api_post_path
+ '/user/gpg_keys'
+ end
+
+ def api_post_body
+ {
+ key: @gpg.key
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/wiki.rb b/qa/qa/resource/wiki.rb
index 6e3648dba0b..45d5da9346d 100644
--- a/qa/qa/resource/wiki.rb
+++ b/qa/qa/resource/wiki.rb
@@ -21,6 +21,15 @@ module QA
end
end
+ attribute :repository_ssh_location do
+ Page::Project::Wiki::Show.perform(&:click_clone_repository)
+
+ Page::Project::Wiki::GitAccess.perform do |git_access|
+ git_access.choose_repository_clone_ssh
+ git_access.repository_location
+ end
+ end
+
def fabricate!
project.visit!
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
index 594913c757a..1b0adbc9053 100644
--- a/qa/qa/runtime/api/client.rb
+++ b/qa/qa/runtime/api/client.rb
@@ -50,7 +50,7 @@ module QA
unless Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
Runtime::Browser.visit(@address, Page::Main::Login)
- Page::Main::Login.perform { |login| login.sign_in_using_credentials(@user) }
+ Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: @user) }
end
token = Resource::PersonalAccessToken.fabricate!.access_token
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 77d04aa7594..4789b380377 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -65,7 +65,7 @@ module QA
# QA::Runtime::Env.browser.capitalize will work for every driver type except PhantomJS.
# We will have no use to use PhantomJS so this shouldn't be a problem.
- options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize)::Options.new
+ options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize, false)::Options.new
if QA::Runtime::Env.browser == :chrome
options.add_argument("window-size=1480,2200")
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 594e5712ab2..b4047ef5088 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -145,6 +145,54 @@ module QA
ENV['GITLAB_QA_PASSWORD_2']
end
+ def gitlab_qa_username_3
+ ENV['GITLAB_QA_USERNAME_3'] || 'gitlab-qa-user3'
+ end
+
+ def gitlab_qa_password_3
+ ENV['GITLAB_QA_PASSWORD_3']
+ end
+
+ def gitlab_qa_username_4
+ ENV['GITLAB_QA_USERNAME_4'] || 'gitlab-qa-user4'
+ end
+
+ def gitlab_qa_password_4
+ ENV['GITLAB_QA_PASSWORD_4']
+ end
+
+ def gitlab_qa_username_5
+ ENV['GITLAB_QA_USERNAME_5'] || 'gitlab-qa-user5'
+ end
+
+ def gitlab_qa_password_5
+ ENV['GITLAB_QA_PASSWORD_5']
+ end
+
+ def gitlab_qa_username_6
+ ENV['GITLAB_QA_USERNAME_6'] || 'gitlab-qa-user6'
+ end
+
+ def gitlab_qa_password_6
+ ENV['GITLAB_QA_PASSWORD_6']
+ end
+
+ def gitlab_qa_1p_email
+ ENV['GITLAB_QA_1P_EMAIL']
+ end
+
+ def gitlab_qa_1p_password
+ ENV['GITLAB_QA_1P_PASSWORD']
+ end
+
+ def gitlab_qa_1p_secret
+ ENV['GITLAB_QA_1P_SECRET']
+ end
+
+ def gitlab_qa_1p_github_uuid
+ ENV['GITLAB_QA_1P_GITHUB_UUID']
+ end
+
def knapsack?
!!(ENV['KNAPSACK_GENERATE_REPORT'] || ENV['KNAPSACK_REPORT_PATH'] || ENV['KNAPSACK_TEST_FILE_PATTERN'])
end
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
index 02cecffd4df..f91218ea0b5 100644
--- a/qa/qa/runtime/fixtures.rb
+++ b/qa/qa/runtime/fixtures.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'tmpdir'
+
module QA
module Runtime
module Fixtures
@@ -18,6 +20,19 @@ module QA
parse_body(response)[:content]
end
+ def with_fixtures(fixtures)
+ dir = Dir.mktmpdir
+ fixtures.each do |file_def|
+ path = File.join(dir, file_def[:file_path])
+ FileUtils.mkdir_p(File.dirname(path))
+ File.write(path, file_def[:content])
+ end
+
+ yield dir
+ ensure
+ FileUtils.remove_entry(dir)
+ end
+
private
def api_client
diff --git a/qa/qa/runtime/gpg.rb b/qa/qa/runtime/gpg.rb
new file mode 100644
index 00000000000..9f8baf7e580
--- /dev/null
+++ b/qa/qa/runtime/gpg.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module QA
+ module Runtime
+ class GPG
+ attr_reader :key, :key_id
+
+ def initialize
+ @key_id = 'B8358D73048DACC4'
+ import_key(File.expand_path('qa/ee/fixtures/gpg/admin.asc'))
+ @key = collect_key.first
+ end
+
+ private
+
+ def import_key(path)
+ import_key = "gpg --import #{path}"
+ execute(import_key)
+ end
+
+ def collect_key
+ get_ascii_format = "gpg --armor --export #{@key_id}"
+ execute(get_ascii_format)
+ end
+
+ def execute(command)
+ Open3.capture2e(*command) do |stdin, out, wait|
+ out.each_char { |char| print char }
+
+ if wait.value.exited? && wait.value.exitstatus.nonzero?
+ raise CommandError, "Command `#{command}` failed!"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/release.rb b/qa/qa/runtime/release.rb
index 4f96e0cf44b..18a6736afcf 100644
--- a/qa/qa/runtime/release.rb
+++ b/qa/qa/runtime/release.rb
@@ -19,7 +19,7 @@ module QA
end
def strategy
- QA.const_get("QA::#{version}::Strategy")
+ Object.const_get("QA::#{version}::Strategy", false)
end
def self.method_missing(name, *args)
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index 011e4a548a5..3c26a3ad691 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -25,6 +25,10 @@ module QA
Runtime::Env.user_password || default_password
end
+ def email
+ default_email
+ end
+
def ldap_user?
Runtime::Env.ldap_username && Runtime::Env.ldap_password
end
diff --git a/qa/qa/scenario/test/integration/ldap_no_server.rb b/qa/qa/scenario/test/integration/ldap_no_server.rb
new file mode 100644
index 00000000000..be71fc0ef6d
--- /dev/null
+++ b/qa/qa/scenario/test/integration/ldap_no_server.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class LDAPNoServer < Test::Instance::All
+ tags :ldap_no_server
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/base.rb b/qa/qa/service/docker_run/base.rb
new file mode 100644
index 00000000000..3f42c09ad2c
--- /dev/null
+++ b/qa/qa/service/docker_run/base.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module DockerRun
+ class Base
+ include Service::Shellout
+
+ def initialize
+ @network = Runtime::Scenario.attributes[:network] || 'test'
+ end
+
+ def network
+ shell "docker network inspect #{@network}"
+ rescue CommandError
+ 'bridge'
+ else
+ @network
+ end
+
+ def pull
+ shell "docker pull #{@image}"
+ end
+
+ def host_name
+ "#{@name}.#{network}"
+ end
+
+ def register!
+ raise NotImplementedError
+ end
+
+ def remove!
+ shell "docker rm -f #{@name}" if running?
+ end
+
+ def running?
+ `docker ps -f name=#{@name}`.include?(@name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
new file mode 100644
index 00000000000..6856a5a8399
--- /dev/null
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Service
+ module DockerRun
+ class GitlabRunner < Base
+ attr_accessor :token, :address, :tags, :image, :run_untagged
+ attr_writer :config
+
+ def initialize(name)
+ @image = 'gitlab/gitlab-runner:alpine'
+ @name = name || "qa-runner-#{SecureRandom.hex(4)}"
+ @tags = %w[qa test]
+ @run_untagged = false
+
+ super()
+ end
+
+ def config
+ @config ||= <<~END
+ concurrent = 1
+ check_interval = 0
+
+ [session_server]
+ session_timeout = 1800
+ END
+ end
+
+ def register!
+ shell <<~CMD.tr("\n", ' ')
+ docker run -d --rm --entrypoint=/bin/sh
+ --network #{network} --name #{@name}
+ -p 8093:8093
+ -e CI_SERVER_URL=#{@address}
+ -e REGISTER_NON_INTERACTIVE=true
+ -e REGISTRATION_TOKEN=#{@token}
+ -e RUNNER_EXECUTOR=shell
+ -e RUNNER_TAG_LIST=#{@tags.join(',')}
+ -e RUNNER_NAME=#{@name}
+ #{@image} -c "#{register_command}"
+ CMD
+ end
+
+ private
+
+ def register_command
+ <<~CMD
+ printf '#{config.chomp.gsub(/\n/, "\\n").gsub('"', '\"')}' > /etc/gitlab-runner/config.toml &&
+ gitlab-runner register --run-untagged=#{@run_untagged} &&
+ gitlab-runner run
+ CMD
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/ldap.rb b/qa/qa/service/docker_run/ldap.rb
new file mode 100644
index 00000000000..c33d75ff640
--- /dev/null
+++ b/qa/qa/service/docker_run/ldap.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module DockerRun
+ class LDAP < Base
+ def initialize(volume)
+ @image = 'osixia/openldap:latest'
+ @name = 'ldap-server'
+ @volume = volume
+
+ super()
+ end
+
+ def register!
+ shell <<~CMD.tr("\n", ' ')
+ docker run -d --rm
+ --network #{network}
+ --hostname #{host_name}
+ --name #{@name}
+ -p 389:389
+ --volume #{volume_or_fixture(@volume)}:/container/service/slapd/assets/config/bootstrap/ldif/custom
+ #{@image} --copy-service
+ CMD
+ end
+
+ def volume_or_fixture(volume_name)
+ if volume_exists?(volume_name)
+ volume_name
+ else
+ File.expand_path("../fixtures/ldap/#{volume_name}", __dir__)
+ end
+ end
+
+ def volume_exists?(volume_name)
+ `docker volume ls -q -f name=#{volume_name}`.include?(volume_name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/node_js.rb b/qa/qa/service/docker_run/node_js.rb
new file mode 100644
index 00000000000..642f1d1a33a
--- /dev/null
+++ b/qa/qa/service/docker_run/node_js.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module DockerRun
+ class NodeJs < Base
+ def initialize(volume_host_path)
+ @image = 'node:12.11.1-alpine'
+ @name = "qa-node-#{SecureRandom.hex(8)}"
+ @volume_host_path = volume_host_path
+
+ super()
+ end
+
+ def publish!
+ # When we run the tests via gitlab-qa, we use docker-in-docker
+ # which means that host of a volume mount would be the host that
+ # started the gitlab-qa QA container (e.g., the CI runner),
+ # not the gitlab-qa container itself. That means we can't
+ # mount a volume from the file system inside the gitlab-qa
+ # container.
+ #
+ # Instead, we copy the files into the container.
+ shell <<~CMD.tr("\n", ' ')
+ docker run -d --rm
+ --network #{network}
+ --hostname #{host_name}
+ --name #{@name}
+ --volume #{@volume_host_path}:/home/node
+ #{@image} sh -c "sleep 60"
+ CMD
+ shell "docker cp #{@volume_host_path}/. #{@name}:/home/node"
+ shell "docker exec -t #{@name} sh -c 'cd /home/node && npm publish'"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/runner.rb b/qa/qa/service/runner.rb
deleted file mode 100644
index 6fc5984b12a..00000000000
--- a/qa/qa/service/runner.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-require 'securerandom'
-
-module QA
- module Service
- class Runner
- include Service::Shellout
-
- attr_accessor :token, :address, :tags, :image, :run_untagged
- attr_writer :config
-
- def initialize(name)
- @image = 'gitlab/gitlab-runner:alpine'
- @name = name || "qa-runner-#{SecureRandom.hex(4)}"
- @network = Runtime::Scenario.attributes[:network] || 'test'
- @tags = %w[qa test]
- @run_untagged = false
- end
-
- def config
- @config ||= <<~END
- concurrent = 1
- check_interval = 0
-
- [session_server]
- session_timeout = 1800
- END
- end
-
- def network
- shell "docker network inspect #{@network}"
- rescue CommandError
- 'bridge'
- else
- @network
- end
-
- def pull
- shell "docker pull #{@image}"
- end
-
- def register!
- shell <<~CMD.tr("\n", ' ')
- docker run -d --rm --entrypoint=/bin/sh
- --network #{network} --name #{@name}
- -p 8093:8093
- -e CI_SERVER_URL=#{@address}
- -e REGISTER_NON_INTERACTIVE=true
- -e REGISTRATION_TOKEN=#{@token}
- -e RUNNER_EXECUTOR=shell
- -e RUNNER_TAG_LIST=#{@tags.join(',')}
- -e RUNNER_NAME=#{@name}
- #{@image} -c "#{register_command}"
- CMD
- end
-
- def remove!
- shell "docker rm -f #{@name}"
- end
-
- private
-
- def register_command
- <<~CMD
- printf '#{config.chomp.gsub(/\n/, "\\n").gsub('"', '\"')}' > /etc/gitlab-runner/config.toml &&
- gitlab-runner register --run-untagged=#{@run_untagged} &&
- gitlab-runner run
- CMD
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
index 44c5e0b4196..819739ac535 100644
--- a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
@@ -3,11 +3,8 @@
module QA
context 'Manage with IP rate limits', :requires_admin do
describe 'Users API' do
- before(:context) do
- @api_client = Runtime::API::Client.new(:gitlab, ip_limits: true)
- end
-
- let(:request) { Runtime::API::Request.new(@api_client, '/users') }
+ let(:api_client) { Runtime::API::Client.new(:gitlab, ip_limits: true) }
+ let(:request) { Runtime::API::Request.new(api_client, '/users') }
it 'GET /users' do
5.times do
diff --git a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
index ce8425cb3d1..5ba434a7781 100644
--- a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
@@ -8,7 +8,7 @@ module QA
describe 'Compare archives of different user projects with the same name and check they\'re different' do
include Support::Api
- before(:all) do
+ before do
@project_name = "project-archive-download-#{SecureRandom.hex(8)}"
@archive_types = %w(tar.gz tar.bz2 tar zip)
@users = {
@@ -46,7 +46,7 @@ module QA
project.standalone = true
project.add_name_uuid = false
project.name = project_name
- project.path_with_namespace = "#{user.name}/#{project_name}"
+ project.path_with_namespace = "#{user.username}/#{project_name}"
project.user = user
project.api_client = api_client
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
index db99488160b..a118176eb8a 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/121
- context 'Manage', :orchestrated, :oauth, :quarantine do
+ context 'Manage', :orchestrated, :oauth do
describe 'OAuth login' do
it 'User logs in to GitLab with GitHub OAuth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
index e3fd835bb01..45c14d0537c 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
@@ -11,6 +11,7 @@ module QA
project = Resource::Project.fabricate_via_api! do |resource|
resource.name = 'project-to-test-mention'
+ resource.visibility = 'private'
end
project.visit!
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index 3ce291bf8bc..c7b5e40d0be 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -13,8 +13,12 @@ module QA
end
project.visit!
- Page::Project::Menu.perform(&:click_settings)
- Page::Project::Settings::MergeRequest.perform(&:enable_ff_only)
+ Page::Project::Menu.perform(&:go_to_general_settings)
+ Page::Project::Settings::Main.perform do |main|
+ main.expand_merge_requests_settings do |settings|
+ settings.enable_ff_only
+ end
+ end
merge_request = Resource::MergeRequest.fabricate! do |merge_request|
merge_request.project = project
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
index c4a6ce13f4c..e42d538fdf8 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -6,8 +6,10 @@ module QA
include Runtime::Fixtures
def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ unless Page::Main::Menu.perform(&:signed_in?)
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ end
end
before(:all) do
@@ -57,12 +59,10 @@ module QA
@project.visit!
Page::Project::Show.perform(&:create_new_file!)
- Page::File::Form.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.select_template template[:file_name], template[:name]
+ Page::File::Form.perform do |form|
+ form.select_template template[:file_name], template[:name]
end
- expect(page).to have_content('Template applied')
- expect(page).to have_button('Undo')
expect(page).to have_content(content[0..100])
Page::File::Form.perform(&:commit_changes)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index 448d4980727..059362704b4 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -25,8 +25,8 @@ module QA
settings.expand_mirroring_repositories do |mirror_settings|
# Configure the source project to push to the target project
mirror_settings.repository_url = target_project_uri
- mirror_settings.mirror_direction = :push
- mirror_settings.authentication_method = :password
+ mirror_settings.mirror_direction = 'Push'
+ mirror_settings.authentication_method = 'Password'
mirror_settings.password = Runtime::User.password
mirror_settings.mirror_repository
mirror_settings.update target_project_uri
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb
index 796de44a012..cbc9f63f772 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb
@@ -7,7 +7,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
- Page::Main::Menu.perform(&:click_snippets_link)
+ Page::Main::Menu.perform(&:go_to_snippets)
Resource::Snippet.fabricate_via_browser_ui! do |snippet|
snippet.title = 'Snippet title'
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
index 2952a54ff5d..5d91b70082c 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -6,7 +6,7 @@ module QA
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
- Service::Runner.new(executor).remove!
+ Service::DockerRun::GitlabRunner.new(executor).remove!
end
it 'users creates a pipeline which gets processed' do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
index 900ddcb7f59..58f129b846d 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -6,7 +6,7 @@ module QA
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
- Service::Runner.new(executor).remove!
+ Service::DockerRun::GitlabRunner.new(executor).remove!
end
it 'user registers a new specific runner' do
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index 141166f6971..e45ce438fc2 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -26,7 +26,7 @@ module QA
end
after do
- Service::Runner.new(@runner_name).remove!
+ Service::DockerRun::GitlabRunner.new(@runner_name).remove!
end
keys = [
diff --git a/qa/qa/support/dates.rb b/qa/qa/support/dates.rb
new file mode 100644
index 00000000000..47fc721afc1
--- /dev/null
+++ b/qa/qa/support/dates.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Dates
+ def current_date_yyyy_mm_dd
+ current_date.strftime("%Y/%m/%d")
+ end
+
+ def next_month_yyyy_mm_dd
+ current_date.next_month.strftime("%Y/%m/%d")
+ end
+
+ private
+
+ def current_date
+ DateTime.now
+ end
+ end
+ end
+end
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index 93d8fa99c0a..6b6e12f86de 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -4,6 +4,12 @@ module QA
module Support
module Page
module Logging
+ def assert_no_element(name)
+ log("asserting no element :#{name}")
+
+ super
+ end
+
def refresh
log("refreshing #{current_url}")
@@ -53,9 +59,10 @@ module QA
elements
end
- def click_element(name, page = nil)
+ def click_element(name, page = nil, **kwargs)
msg = ["clicking :#{name}"]
msg << ", expecting to be at #{page.class}" if page
+ msg << "with args #{kwargs}"
log(msg.compact.join(' '))
diff --git a/qa/qa/vendor/github/page/login.rb b/qa/qa/vendor/github/page/login.rb
index 120ba6e6c06..f6e72bb01f9 100644
--- a/qa/qa/vendor/github/page/login.rb
+++ b/qa/qa/vendor/github/page/login.rb
@@ -12,6 +12,12 @@ module QA
fill_in 'password', with: QA::Runtime::Env.github_password
click_on 'Sign in'
+ otp = OnePassword::CLI.new.otp
+
+ fill_in 'otp', with: otp
+
+ click_on 'Verify'
+
click_on 'Authorize gitlab-qa' if has_button?('Authorize gitlab-qa')
end
end
diff --git a/qa/qa/vendor/one_password/cli.rb b/qa/qa/vendor/one_password/cli.rb
new file mode 100644
index 00000000000..3cb69391783
--- /dev/null
+++ b/qa/qa/vendor/one_password/cli.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module QA
+ module Vendor
+ module OnePassword
+ class CLI
+ def initialize
+ @email = QA::Runtime::Env.gitlab_qa_1p_email
+ @password = QA::Runtime::Env.gitlab_qa_1p_password
+ @secret = QA::Runtime::Env.gitlab_qa_1p_secret
+ @github_uuid = QA::Runtime::Env.gitlab_qa_1p_github_uuid
+ end
+
+ def otp
+ `#{op_path} get totp #{@github_uuid} --session=#{session_token}`.to_i
+ end
+
+ private
+
+ def session_token
+ `echo '#{@password}' | #{op_path} signin gitlab.1password.com #{@email} #{@secret} --output=raw --shorthand=gitlab_qa`
+ end
+
+ def op_path
+ File.expand_path(File.join(%W[qa vendor one_password #{os} op]))
+ end
+
+ def os
+ RUBY_PLATFORM.include?("darwin") ? "darwin" : "linux"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/one_password/darwin/op b/qa/qa/vendor/one_password/darwin/op
new file mode 100755
index 00000000000..0f646522834
--- /dev/null
+++ b/qa/qa/vendor/one_password/darwin/op
Binary files differ
diff --git a/qa/qa/vendor/one_password/linux/op b/qa/qa/vendor/one_password/linux/op
new file mode 100755
index 00000000000..47ce87731be
--- /dev/null
+++ b/qa/qa/vendor/one_password/linux/op
Binary files differ
diff --git a/qa/spec/page/element_spec.rb b/qa/spec/page/element_spec.rb
index 20d4a00c020..ff5e118cefa 100644
--- a/qa/spec/page/element_spec.rb
+++ b/qa/spec/page/element_spec.rb
@@ -113,6 +113,7 @@ describe QA::Page::Element do
describe 'data-qa selectors' do
subject { described_class.new(:my_element) }
+
it 'properly translates to a data-qa-selector' do
expect(subject.selector_css).to include(%q([data-qa-selector="my_element"]))
end
diff --git a/qa/spec/resource/user_spec.rb b/qa/spec/resource/user_spec.rb
index d612dfc530e..5845f7996a3 100644
--- a/qa/spec/resource/user_spec.rb
+++ b/qa/spec/resource/user_spec.rb
@@ -35,8 +35,8 @@ describe QA::Resource::User do
end
describe '#name' do
- it 'defaults to the username' do
- expect(subject.name).to eq(subject.username)
+ it 'defaults to a name based on the username' do
+ expect(subject.name).to match(/#{subject.username.tr('-', ' ')}/i)
end
it 'retrieves the name from the api_resource if present' do
diff --git a/qa/spec/scenario/test/integration/ldap_spec.rb b/qa/spec/scenario/test/integration/ldap_spec.rb
index b6d798bf504..86747cd8eb7 100644
--- a/qa/spec/scenario/test/integration/ldap_spec.rb
+++ b/qa/spec/scenario/test/integration/ldap_spec.rb
@@ -8,6 +8,14 @@ describe QA::Scenario::Test::Integration::LDAPNoTLS do
end
end
+describe QA::Scenario::Test::Integration::LDAPNoServer do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:ldap_no_server] }
+ end
+ end
+end
+
describe QA::Scenario::Test::Integration::LDAPTLS do
context '#perform' do
it_behaves_like 'a QA scenario class' do