diff options
Diffstat (limited to 'qa')
25 files changed, 300 insertions, 112 deletions
diff --git a/qa/README.md b/qa/README.md index 002ad4c65f5..f75205133e6 100644 --- a/qa/README.md +++ b/qa/README.md @@ -49,10 +49,10 @@ will need to [modify your GDK setup](https://gitlab.com/gitlab-org/gitlab-qa/blo ### Writing tests -- [Writing tests from scratch tutorial](docs/WRITING_TESTS_FROM_SCRATCH.md) - - [Best practices](docs/BEST_PRACTICES.md) +- [Writing tests from scratch tutorial](docs/writing_tests_from_scratch.md) + - [Best practices](docs/best_practices.md) - [Using page objects](qa/page/README.md) - - [Guidelines](docs/GUIDELINES.md) + - [Guidelines](docs/guidelines.md) ### Running specific tests diff --git a/qa/docs/GUIDELINES.md b/qa/docs/GUIDELINES.md deleted file mode 100644 index 9db52cd07e6..00000000000 --- a/qa/docs/GUIDELINES.md +++ /dev/null @@ -1,46 +0,0 @@ -# Style guide for writing GUI tests - -This document describes the conventions used at GitLab for writing GUI tests using the GitLab QA project. - -## `click_` versus `go_to_` - -### When to use `click_`? - -When clicking in a single link to navigate, use `click_`. - -E.g.: - -```ruby -def click_ci_cd_pipelines - within_sidebar do - click_element :link_pipelines - end -end -``` - -From a testing perspective, if we want to check that clicking a link, or a button (a single interaction) is working as intended, we would want the test to read as: - -- Click a certain element -- Verify the action took place - -### When to use `go_to_`? - -When interacting with multiple elements to go to a page, use `go_to_`. - -E.g.: - -```ruby -def go_to_operations_environments - hover_operations do - within_submenu do - click_element(:operations_environments_link) - end - end -end -``` - -`go_to_` fits the definition of interacting with multiple elements very well given it's more of a meta-navigation action that includes multiple interactions. - -Notice that in the above example, before clicking the `:operations_environments_link`, another element is hovered over. - -> We can create these methods as helpers to abstract multi-step navigation. diff --git a/qa/docs/BEST_PRACTICES.md b/qa/docs/best_practices.md index 3a2640607e4..d6e5350b0c8 100644 --- a/qa/docs/BEST_PRACTICES.md +++ b/qa/docs/best_practices.md @@ -35,4 +35,4 @@ Finally, interacting with the application only by its GUI generates a higher rat - Building state through the GUI is time consuming and it's not sustainable as the test suite grows. - When depending only on the GUI to create the application's state and tests fail due to front-end issues, we can't rely on the test failures rate, and we generates a higher rate of test flakiness. -Now that we are aware of all of it, [let's go create some tests](./WRITING_TESTS_FROM_SCRATCH.md). +Now that we are aware of all of it, [let's go create some tests](writing_tests_from_scratch.md). diff --git a/qa/docs/guidelines.md b/qa/docs/guidelines.md new file mode 100644 index 00000000000..cd4b939fd71 --- /dev/null +++ b/qa/docs/guidelines.md @@ -0,0 +1,97 @@ +# Style guide for writing E2E tests + +This document describes the conventions used at GitLab for writing E2E tests using the GitLab QA project. + +## `click_` versus `go_to_` + +### When to use `click_`? + +When clicking in a single link to navigate, use `click_`. + +E.g.: + +```ruby +def click_ci_cd_pipelines + within_sidebar do + click_element :link_pipelines + end +end +``` + +From a testing perspective, if we want to check that clicking a link, or a button (a single interaction) is working as intended, we would want the test to read as: + +- Click a certain element +- Verify the action took place + +### When to use `go_to_`? + +When interacting with multiple elements to go to a page, use `go_to_`. + +E.g.: + +```ruby +def go_to_operations_environments + hover_operations do + within_submenu do + click_element(:operations_environments_link) + end + end +end +``` + +`go_to_` fits the definition of interacting with multiple elements very well given it's more of a meta-navigation action that includes multiple interactions. + +Notice that in the above example, before clicking the `:operations_environments_link`, another element is hovered over. + +> We can create these methods as helpers to abstract multi-step navigation. + +### Element Naming Convention + +When adding new elements to a page, it's important that we have a uniform element naming convention. + +We follow a simple formula roughly based on hungarian notation. + +*Formula*: `element :<descriptor>_<type>` + +- `descriptor`: The natural-language description of what the element is. On the login page, this could be `username`, or `password`. +- `type`: A physical control on the page that can be seen by a user. + - `_button` + - `_link` + - `_tab` + - `_dropdown` + - `_field` + - `_checkbox` + - `_radio` + - `_content` + +*Note: This list is a work in progress. This list will eventually be the end-all enumeration of all available types. + I.e., any element that does not end with something in this list is bad form.* + +#### Examples + +**Good** +```ruby +view '...' do + element :edit_button + element :notes_tab + element :squash_checkbox + element :username_field + element :issue_title_content +end +``` + +**Bad** +```ruby +view '...' do + # `_confirmation` should be `_field`. what sort of confirmation? a checkbox confirmation? no real way to disambiguate. + # an appropriate replacement would be `element :password_confirmation_field` + element :password_confirmation + + # `clone_options` is too vague. If it's a dropdown menu, it should be `clone_dropdown`. + # If it's a checkbox, it should be `clone_checkbox` + element :clone_options + + # how is this url being displayed? is it a textbox? a simple span? + element :ssh_clone_url +end +``` diff --git a/qa/docs/WRITING_TESTS_FROM_SCRATCH.md b/qa/docs/writing_tests_from_scratch.md index 309fcc4064c..65e7a78a8b5 100644 --- a/qa/docs/WRITING_TESTS_FROM_SCRATCH.md +++ b/qa/docs/writing_tests_from_scratch.md @@ -8,7 +8,7 @@ In this tutorial, you will find different examples, and the steps involved, in t It's important to understand that end-to-end tests of isolated features, such as the ones described in the above note, doesn't mean that everything needs to happen through the GUI. -If you don't exactly understand what we mean by **not everything needs to happen through the GUI,** please make sure you've read the [best practices](./BEST_PRACTICES.md) before moving on. +If you don't exactly understand what we mean by **not everything needs to happen through the GUI,** please make sure you've read the [best practices](best_practices.md) before moving on. ## This document covers the following items: @@ -367,7 +367,7 @@ With that in mind, resources can be a project, an epic, an issue, a label, a com As you saw in the tests' pre-conditions and the optimization sections, we're already creating some of these resources, and we are doing that by calling the `fabricate_via_api!` method. -> We could be using the `fabricate!` method instead, which would use the `fabricate_via_api!` method if it exists, and fallback to GUI fabrication otherwise, but we recommend being explicit to make it clear what the test does. Also, we recommend fabricating resources via API since this makes tests faster and more reliable, unless the test is focusing on the GUI itself, or there's no GUI coverage for that specific part in any other test. +> We could be using the `fabricate!` method instead, which would use the `fabricate_via_api!` method if it exists, and fallback to GUI fabrication otherwise, but we recommend being explicit to make it clear what the test does. Also, we always recommend fabricating resources via API since this makes tests faster and more reliable. For our test suite example, the [project resource](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/project.rb#L55) already had a `fabricate_via_api!` method available, while other resources don't have it, so we will have to create them, like for the issue and label resources. Also, we will have to make a small change in the project resource to expose its `id` attribute so that we can refer to it when fabricating the issue. @@ -130,6 +130,7 @@ module QA autoload :View, 'qa/page/view' autoload :Element, 'qa/page/element' autoload :Validator, 'qa/page/validator' + autoload :Validatable, 'qa/page/validatable' module Main autoload :Login, 'qa/page/main/login' diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb index d7748a976f0..7e2d02424fe 100644 --- a/qa/qa/ce/strategy.rb +++ b/qa/qa/ce/strategy.rb @@ -13,7 +13,6 @@ module QA # 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. QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login) - QA::Page::Main::Login.perform(&:assert_page_loaded) end end end diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index c395e5f6011..389f4e0032e 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -8,6 +8,7 @@ module QA prepend Support::Page::Logging if Runtime::Env.debug? include Capybara::DSL include Scenario::Actable + extend Validatable extend SingleForwardable ElementNotFound = Class.new(RuntimeError) @@ -93,8 +94,10 @@ module QA find_element(name).set(false) end - def click_element(name) + # replace with (..., page = self.class) + def click_element(name, page = nil) find_element(name).click + page.validate_elements_present! if page end def fill_element(name, content) diff --git a/qa/qa/page/element.rb b/qa/qa/page/element.rb index d92e71467fe..7a01320901d 100644 --- a/qa/qa/page/element.rb +++ b/qa/qa/page/element.rb @@ -1,28 +1,41 @@ # frozen_string_literal: true +require 'active_support/core_ext/array/extract_options' + module QA module Page class Element - attr_reader :name + attr_reader :name, :attributes - def initialize(name, pattern = nil) + def initialize(name, *options) @name = name - @pattern = pattern || selector + @attributes = options.extract_options! + @attributes[:pattern] ||= selector + + options.each do |option| + if option.is_a?(String) || option.is_a?(Regexp) + @attributes[:pattern] = option + end + end end def selector "qa-#{@name.to_s.tr('_', '-')}" end + def required? + !!@attributes[:required] + end + def selector_css ".#{selector}" end def expression - if @pattern.is_a?(String) - @_regexp ||= Regexp.new(Regexp.escape(@pattern)) + if @attributes[:pattern].is_a?(String) + @_regexp ||= Regexp.new(Regexp.escape(@attributes[:pattern])) else - @pattern + @attributes[:pattern] end end diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index 99b3d1b83d3..8970eeb6678 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -39,19 +39,7 @@ module QA end view 'app/views/layouts/devise.html.haml' do - element :login_page - end - - def assert_page_loaded - unless page_loaded? - raise QA::Runtime::Browser::NotRespondingError, "Login page did not load at #{QA::Page::Main::Login.perform(&:current_url)}" - end - end - - def page_loaded? - wait(max: 60) do - has_element?(:login_page) - end + element :login_page, required: true end def sign_in_using_credentials(user = nil) @@ -159,7 +147,7 @@ module QA fill_element :login_field, user.username fill_element :password_field, user.password - click_element :sign_in_button + click_element :sign_in_button, 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 e98d531c86e..5eb24d2d2ba 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -10,15 +10,15 @@ module QA end view 'app/views/layouts/header/_default.html.haml' do - element :navbar - element :user_avatar + element :navbar, required: true + element :user_avatar, required: true element :user_menu, '.dropdown-menu' # rubocop:disable QA/ElementWithPattern end view 'app/views/layouts/nav/_dashboard.html.haml' do element :admin_area_link - element :projects_dropdown - element :groups_dropdown + element :projects_dropdown, required: true + element :groups_dropdown, required: true element :snippets_link end diff --git a/qa/qa/page/project/settings/ci_variables.rb b/qa/qa/page/project/settings/ci_variables.rb index 567fe6f83c8..3621e618bf2 100644 --- a/qa/qa/page/project/settings/ci_variables.rb +++ b/qa/qa/page/project/settings/ci_variables.rb @@ -11,6 +11,7 @@ module QA element :variable_row, '.ci-variable-row-body' # rubocop:disable QA/ElementWithPattern element :variable_key, '.qa-ci-variable-input-key' # rubocop:disable QA/ElementWithPattern element :variable_value, '.qa-ci-variable-input-value' # rubocop:disable QA/ElementWithPattern + element :variable_masked end view 'app/views/ci/variables/_index.html.haml' do @@ -18,7 +19,7 @@ module QA element :reveal_values, '.js-secret-value-reveal-button' # rubocop:disable QA/ElementWithPattern end - def fill_variable(key, value) + def fill_variable(key, value, masked) keys = all_elements(:ci_variable_input_key) index = keys.size - 1 @@ -32,6 +33,9 @@ module QA # The code was inspired from: # https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217 execute_script("arguments[0].value = #{value.to_json}", node) + + masked_node = all_elements(:variable_masked)[index] + toggle_masked(masked_node, masked) end def save_variables @@ -47,6 +51,24 @@ module QA find('.qa-ci-variable-input-value').value end end + + private + + def toggle_masked(masked_node, masked) + wait(reload: false) do + masked_node.click + + masked ? masked_enabled?(masked_node) : masked_disabled?(masked_node) + end + end + + def masked_enabled?(masked_node) + masked_node[:class].include?('is-checked') + end + + def masked_disabled?(masked_node) + !masked_enabled?(masked_node) + end end end end diff --git a/qa/qa/page/validatable.rb b/qa/qa/page/validatable.rb new file mode 100644 index 00000000000..8467d261285 --- /dev/null +++ b/qa/qa/page/validatable.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module QA + module Page + module Validatable + PageValidationError = Class.new(StandardError) + + def validate_elements_present! + base_page = self.new + + elements.each do |element| + next unless element.required? + + # TODO: this wait needs to be replaced by the wait class + unless base_page.has_element?(element.name, wait: 60) + raise Validatable::PageValidationError, "#{element.name} did not appear on #{self.name} as expected" + end + end + end + end + end +end diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb index 96f3917a8ab..613059b2d32 100644 --- a/qa/qa/page/view.rb +++ b/qa/qa/page/view.rb @@ -50,8 +50,8 @@ module QA @elements = [] end - def element(name, pattern = nil) - @elements.push(Page::Element.new(name, pattern)) + def element(name, *args) + @elements.push(Page::Element.new(name, *args)) end end end diff --git a/qa/qa/resource/ci_variable.rb b/qa/qa/resource/ci_variable.rb index 341d3c1ed7e..b178a64b72d 100644 --- a/qa/qa/resource/ci_variable.rb +++ b/qa/qa/resource/ci_variable.rb @@ -3,7 +3,7 @@ module QA module Resource class CiVariable < Base - attr_accessor :key, :value + attr_accessor :key, :value, :masked attribute :project do Project.fabricate! do |resource| @@ -19,7 +19,7 @@ module QA Page::Project::Settings::CICD.perform do |setting| setting.expand_ci_variables do |page| - page.fill_variable(key, value) + page.fill_variable(key, value, masked) page.save_variables end @@ -49,7 +49,8 @@ module QA def api_post_body { key: key, - value: value + value: value, + masked: masked } end end diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb index 57e82ac19ad..ca74654bf90 100644 --- a/qa/qa/resource/file.rb +++ b/qa/qa/resource/file.rb @@ -3,9 +3,12 @@ module QA module Resource class File < Base - attr_accessor :name, + attr_accessor :author_email, + :author_name, + :branch, :content, - :commit_message + :commit_message, + :name attribute :project do Project.fabricate! do |resource| @@ -31,6 +34,30 @@ module QA page.commit_changes 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 + "/projects/#{CGI.escape(project.path_with_namespace)}/repository/files/#{CGI.escape(@name)}" + end + + def api_post_path + api_get_path + end + + def api_post_body + { + branch: @branch || "master", + author_email: @author_email || Runtime::User.default_email, + author_name: @author_name || Runtime::User.username, + content: content, + commit_message: commit_message + } + end end end end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 174a52bd376..3bf4b3bbbfb 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -33,6 +33,7 @@ module QA def self.visit(address, page = nil, &block) new.visit(address, page, &block) + page.validate_elements_present! end def self.configure! diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb index b1d641b507f..67610b62ed7 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb @@ -4,16 +4,14 @@ module QA context 'Manage', :orchestrated, :mattermost do describe 'Mattermost login' do it 'user logs into Mattermost using GitLab OAuth' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) do - Page::Main::Login.act { sign_in_using_credentials } + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.perform(&:sign_in_using_credentials) - Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do - Page::Mattermost::Login.act { sign_in_using_oauth } + Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) + Page::Mattermost::Login.perform(&:sign_in_using_oauth) - Page::Mattermost::Main.perform do |page| - expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/) - end - end + Page::Mattermost::Main.perform do |page| + expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/) end end end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb index 46346d1b984..d345fbfe995 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb @@ -12,7 +12,7 @@ module QA file_content = 'QA Test - File content' commit_message_for_create = 'QA Test - Create new file' - Resource::File.fabricate! do |file| + Resource::File.fabricate_via_browser_ui! do |file| file.name = file_name file.content = file_content file.commit_message = commit_message_for_create diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb index b7400cdca97..680c5e21fa4 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb @@ -3,13 +3,16 @@ module QA context 'Create' do # failure reported: https://gitlab.com/gitlab-org/quality/nightly/issues/42 - # also failing in staging until the fix is picked into the next release: - # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24533 describe 'Commit data', :quarantine do before(:context) do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.perform(&:sign_in_using_credentials) + # Get the user's details to confirm they're included in the email patch + @user = Resource::User.fabricate_via_api! do |user| + user.username = Runtime::User.username + end + project_push = Resource::Repository::ProjectPush.fabricate! do |push| push.file_name = 'README.md' push.file_content = '# This is a test project' @@ -21,12 +24,13 @@ module QA # add second file to repo to enable diff from initial commit @commit_message = 'Add second file' - Page::Project::Show.perform(&:create_new_file!) - Page::File::Form.perform do |f| - f.add_name('second') - f.add_content('second file content') - f.add_commit_message(@commit_message) - f.commit_changes + Resource::File.fabricate_via_api! do |file| + file.project = @project + file.name = 'second' + file.content = 'second file content' + file.commit_message = @commit_message + file.author_name = @user.name + file.author_email = @user.public_email end end @@ -42,15 +46,11 @@ module QA end it 'user views raw email patch' do - user = Resource::User.fabricate_via_api! do |user| - user.username = Runtime::User.username - end - view_commit Page::Project::Commit::Show.perform(&:select_email_patches) - expect(page).to have_content("From: #{user.name} <#{user.public_email}>") + expect(page).to have_content("From: #{@user.name} <#{@user.public_email}>") expect(page).to have_content('Subject: [PATCH] Add second file') expect(page).to have_content('diff --git a/second b/second') end diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb index 561a8895329..b060f15168c 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb @@ -16,6 +16,7 @@ module QA resource.project = project resource.key = 'VARIABLE_KEY' resource.value = 'some_CI_variable' + resource.masked = false end project.visit! 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 609155da855..2fe4e4d9d1f 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 @@ -60,6 +60,7 @@ module QA resource.project = @project resource.key = deploy_key_name resource.value = key.private_key + resource.masked = false end gitlab_ci = <<~YAML diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb index 0971e551db1..86ba5e819ba 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -9,8 +9,7 @@ module QA Page::Main::Login.perform(&:sign_in_using_credentials) end - # Transient failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/68 - describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do + describe 'Auto DevOps support', :orchestrated, :kubernetes do context 'when rbac is enabled' do before(:all) do @cluster = Service::KubernetesCluster.new.create! @@ -34,6 +33,7 @@ module QA resource.project = @project resource.key = 'CODE_QUALITY_DISABLED' resource.value = '1' + resource.masked = false end # Set an application secret CI variable (prefixed with K8S_SECRET_) @@ -41,6 +41,7 @@ module QA resource.project = @project resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE' resource.value = 'you_can_see_this_variable' + resource.masked = false end # Connect K8s cluster diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb index ff505fdbddd..3fe567d7757 100644 --- a/qa/qa/support/page/logging.rb +++ b/qa/qa/support/page/logging.rb @@ -56,8 +56,11 @@ module QA elements end - def click_element(name) - log("clicking :#{name}") + def click_element(name, page = nil) + msg = ["clicking :#{name}"] + msg << ", expecting to be at #{page.class}" if page + + log(msg.compact.join(' ')) super end diff --git a/qa/spec/page/element_spec.rb b/qa/spec/page/element_spec.rb index d5d6dff69da..f746fe06e40 100644 --- a/qa/spec/page/element_spec.rb +++ b/qa/spec/page/element_spec.rb @@ -50,4 +50,60 @@ describe QA::Page::Element do expect(subject.matches?('some_name selector')).to be false end end + + describe 'attributes' do + context 'element with no args' do + subject { described_class.new(:something) } + + it 'defaults pattern to #selector' do + expect(subject.attributes[:pattern]).to eq 'qa-something' + expect(subject.attributes[:pattern]).to eq subject.selector + end + + it 'is not required by default' do + expect(subject.required?).to be false + end + end + + context 'element with a pattern' do + subject { described_class.new(:something, /link_to 'something'/) } + + it 'has an attribute[pattern] of the pattern' do + expect(subject.attributes[:pattern]).to eq /link_to 'something'/ + end + + it 'is not required by default' do + expect(subject.required?).to be false + end + end + + context 'element with requirement; no pattern' do + subject { described_class.new(:something, required: true) } + + it 'has an attribute[pattern] of the selector' do + expect(subject.attributes[:pattern]).to eq 'qa-something' + expect(subject.attributes[:pattern]).to eq subject.selector + end + + it 'is required' do + expect(subject.required?).to be true + end + end + + context 'element with requirement and pattern' do + subject { described_class.new(:something, /link_to 'something_else_entirely'/, required: true) } + + it 'has an attribute[pattern] of the passed pattern' do + expect(subject.attributes[:pattern]).to eq /link_to 'something_else_entirely'/ + end + + it 'is required' do + expect(subject.required?).to be true + end + + it 'has a selector of the name' do + expect(subject.selector).to eq 'qa-something' + end + end + end end |