diff options
| author | Matija Čupić <matteeyah@gmail.com> | 2018-02-04 23:38:59 +0100 |
|---|---|---|
| committer | Matija Čupić <matteeyah@gmail.com> | 2018-02-04 23:38:59 +0100 |
| commit | 3366f377c1d4cbb02ecc5a2e47b059ed375c5e09 (patch) | |
| tree | 6a19813b4820ad6d2813bf86b30d8e5be2bd10c6 /qa | |
| parent | 0abce36cd20cdd3579138bee835d28519a5593f2 (diff) | |
| parent | cf887a8b3108edb715ee5618377f4ffab1824d85 (diff) | |
| download | gitlab-ce-3366f377c1d4cbb02ecc5a2e47b059ed375c5e09.tar.gz | |
Merge branch 'master' into 38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour
Diffstat (limited to 'qa')
25 files changed, 435 insertions, 54 deletions
diff --git a/qa/README.md b/qa/README.md index 3c1b61900d9..b937dc4c7a0 100644 --- a/qa/README.md +++ b/qa/README.md @@ -34,6 +34,9 @@ You can use GitLab QA to exercise tests on any live instance! For example, the following call would login to a local [GDK] instance and run all specs in `qa/specs/features`: +First, `cd` into the `$gdk/gitlab/qa` directory. +The `bin/qa` script expects you to be in the `qa` folder of the app. + ``` bin/qa Test::Instance http://localhost:3000 ``` @@ -27,7 +27,9 @@ module QA module Resource autoload :Sandbox, 'qa/factory/resource/sandbox' autoload :Group, 'qa/factory/resource/group' + autoload :Issue, 'qa/factory/resource/issue' autoload :Project, 'qa/factory/resource/project' + autoload :MergeRequest, 'qa/factory/resource/merge_request' autoload :DeployKey, 'qa/factory/resource/deploy_key' autoload :SecretVariable, 'qa/factory/resource/secret_variable' autoload :Runner, 'qa/factory/resource/runner' @@ -124,12 +126,22 @@ module QA autoload :SecretVariables, 'qa/page/project/settings/secret_variables' autoload :Runners, 'qa/page/project/settings/runners' end + + module Issue + autoload :New, 'qa/page/project/issue/new' + autoload :Show, 'qa/page/project/issue/show' + autoload :Index, 'qa/page/project/issue/index' + end end module Profile autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens' end + module MergeRequest + autoload :New, 'qa/page/merge_request/new' + end + module Admin autoload :Settings, 'qa/page/admin/settings' end @@ -138,6 +150,13 @@ module QA autoload :Main, 'qa/page/mattermost/main' autoload :Login, 'qa/page/mattermost/login' end + + ## + # Classes describing components that are used by several pages. + # + module Component + autoload :Dropzone, 'qa/page/component/dropzone' + end end ## diff --git a/qa/qa/factory/dependency.rb b/qa/qa/factory/dependency.rb index d0e85a68237..fc5dc82ce29 100644 --- a/qa/qa/factory/dependency.rb +++ b/qa/qa/factory/dependency.rb @@ -16,20 +16,21 @@ module QA def build! return if overridden? - Builder.new(@signature).fabricate!.tap do |product| + Builder.new(@signature, @factory).fabricate!.tap do |product| @factory.public_send("#{@name}=", product) end end class Builder - def initialize(signature) + def initialize(signature, caller_factory) @factory = signature.factory @block = signature.block + @caller_factory = caller_factory end def fabricate! @factory.fabricate! do |factory| - @block&.call(factory) + @block&.call(factory, @caller_factory) end end end diff --git a/qa/qa/factory/resource/issue.rb b/qa/qa/factory/resource/issue.rb new file mode 100644 index 00000000000..95f48e20b3e --- /dev/null +++ b/qa/qa/factory/resource/issue.rb @@ -0,0 +1,32 @@ +module QA + module Factory + module Resource + class Issue < Factory::Base + attr_writer :title, :description, :project + + dependency Factory::Resource::Project, as: :project do |project| + project.name = 'project-for-issues' + project.description = 'project for adding issues' + end + + product :title do + Page::Project::Issue::Show.act { issue_title } + end + + def fabricate! + project.visit! + + Page::Project::Show.act do + go_to_new_issue + end + + Page::Project::Issue::New.perform do |page| + page.add_title(@title) + page.add_description(@description) + page.create_new_issue + end + end + end + end + end +end diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb new file mode 100644 index 00000000000..ce04e904aaf --- /dev/null +++ b/qa/qa/factory/resource/merge_request.rb @@ -0,0 +1,49 @@ +require 'securerandom' + +module QA + module Factory + module Resource + class MergeRequest < Factory::Base + attr_accessor :title, + :description, + :source_branch, + :target_branch + + dependency Factory::Resource::Project, as: :project do |project| + project.name = 'project-with-merge-request' + end + + dependency Factory::Repository::Push, as: :target do |push, factory| + push.project = factory.project + push.branch_name = "master:#{factory.target_branch}" + end + + dependency Factory::Repository::Push, as: :source do |push, factory| + push.project = factory.project + push.branch_name = "#{factory.target_branch}:#{factory.source_branch}" + push.file_name = "added_file.txt" + push.file_content = "File Added" + end + + def initialize + @title = 'QA test - merge request' + @description = 'This is a test merge request' + @source_branch = "qa-test-feature-#{SecureRandom.hex(8)}" + @target_branch = "master" + end + + def fabricate! + project.visit! + + Page::Project::Show.act { new_merge_request } + + Page::MergeRequest::New.perform do |page| + page.fill_title(@title) + page.fill_description(@description) + page.create_merge_request + end + end + end + end + end +end diff --git a/qa/qa/page/admin/settings.rb b/qa/qa/page/admin/settings.rb index 1904732aee6..1f646103e7f 100644 --- a/qa/qa/page/admin/settings.rb +++ b/qa/qa/page/admin/settings.rb @@ -2,12 +2,13 @@ module QA module Page module Admin class Settings < Page::Base - ## - # TODO, define all selectors required by this page object - # - # See gitlab-org/gitlab-qa#154 - # - view 'app/views/admin/application_settings/show.html.haml' + view 'app/views/admin/application_settings/_form.html.haml' do + element :form_actions, '.form-actions' + element :submit, "submit 'Save'" + element :repository_storage, '%legend Repository Storage' + element :hashed_storage, + 'Create new projects using hashed storage paths' + end def enable_hashed_storage scroll_to 'legend', text: 'Repository Storage' diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 81ba80cdbaf..5c3af4b9115 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -13,16 +13,18 @@ module QA visit current_url end - def wait(css = '.application', time: 60) - Time.now.tap do |start| - while Time.now - start < time - break if page.has_css?(css, wait: 5) + def wait(max: 60, time: 1, reload: true) + start = Time.now - refresh - end + while Time.now - start < max + return true if yield + + sleep(time) + + refresh if reload end - yield if block_given? + false end def scroll_to(selector, text: nil) @@ -40,14 +42,35 @@ module QA page.within(selector) { yield } if block_given? end - def click_element(name) - find_element(name).click + # Returns true if successfully GETs the given URL + # Useful because `page.status_code` is unsupported by our driver, and + # we don't have access to the `response` to use `have_http_status`. + def asset_exists?(url) + page.execute_script <<~JS + xhr = new XMLHttpRequest(); + xhr.open('GET', '#{url}', true); + xhr.send(); + JS + + return false unless wait(time: 0.5, max: 60, reload: false) do + page.evaluate_script('xhr.readyState == XMLHttpRequest.DONE') + end + + page.evaluate_script('xhr.status') == 200 end def find_element(name) find(element_selector_css(name)) end + def click_element(name) + find_element(name).click + end + + def fill_element(name, content) + find_element(name).set(content) + end + def within_element(name) page.within(element_selector_css(name)) do yield diff --git a/qa/qa/page/component/dropzone.rb b/qa/qa/page/component/dropzone.rb new file mode 100644 index 00000000000..15bdc742fda --- /dev/null +++ b/qa/qa/page/component/dropzone.rb @@ -0,0 +1,31 @@ +module QA + module Page + module Component + class Dropzone + attr_reader :page, :container + + # page - A QA::Page::Base object + # container - CSS selector of the comment textarea's container + def initialize(page, container) + @page = page + @container = container + end + + # Not tested and not expected to work with multiple dropzones + # instantiated on one page because there is no distinguishing + # attribute per dropzone file field. + def attach_file(attachment) + filename = File.basename(attachment) + + field_style = { visibility: 'visible', height: '', width: '' } + page.attach_file(attachment, class: 'dz-hidden-input', make_visible: field_style) + + # Wait for link to be appended to dropzone text + page.wait(reload: false) do + page.find("#{container} textarea").value.match(filename) + end + end + end + end + end +end diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb index 37ed3b35bce..d215518d316 100644 --- a/qa/qa/page/group/show.rb +++ b/qa/qa/page/group/show.rb @@ -2,12 +2,20 @@ module QA module Page module Group class Show < Page::Base - ## - # TODO, define all selectors required by this page object - # - # See gitlab-org/gitlab-qa#154 - # - view 'app/views/groups/show.html.haml' + view 'app/views/groups/show.html.haml' do + element :new_project_or_subgroup_dropdown, '.new-project-subgroup' + element :new_project_or_subgroup_dropdown_toggle, '.dropdown-toggle' + element :new_project_option, /%li.*data:.*value: "new-project"/ + element :new_project_button, /%input.*data:.*action: "new-project"/ + element :new_subgroup_option, /%li.*data:.*value: "new-subgroup"/ + + # data-value and data-action get modified by JS for subgroup + element :new_subgroup_button, /%input.*\.js-new-group-child/ + end + + view 'app/assets/javascripts/groups/constants.js' do + element :no_result_text, 'Sorry, no groups or projects matched your search' + end def go_to_subgroup(name) click_link name @@ -20,26 +28,41 @@ module QA def has_subgroup?(name) filter_by_name(name) - page.has_link?(name) + wait(reload: false) do + return false if page.has_content?('Sorry, no groups or projects matched your search') + + page.has_link?(name) + end end def go_to_new_subgroup - within '.new-project-subgroup' do - find('.dropdown-toggle').click - find("li[data-value='new-subgroup']").click - end + click_new('subgroup') find("input[data-action='new-subgroup']").click end def go_to_new_project - within '.new-project-subgroup' do - find('.dropdown-toggle').click - find("li[data-value='new-project']").click - end + click_new('project') find("input[data-action='new-project']").click end + + private + + def click_new(kind) + within '.new-project-subgroup' do + css = "li[data-value='new-#{kind}']" + + # May need to click again because it is possible to click the button quicker than the JS is bound + wait(reload: false) do + find('.dropdown-toggle').click + + page.has_css?(css) + end + + find(css).click + end + end end end end diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index 9cff2c5c317..95880475ffa 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -10,12 +10,14 @@ module QA view 'app/views/devise/sessions/_new_base.html.haml' do element :login_field, 'text_field :login' - element :passowrd_field, 'password_field :password' + element :password_field, 'password_field :password' element :sign_in_button, 'submit "Sign in"' end def initialize - wait('.application', time: 500) + wait(max: 500) do + page.has_css?('.application') + end end def sign_in_using_credentials diff --git a/qa/qa/page/menu/admin.rb b/qa/qa/page/menu/admin.rb index 40da4a53e8a..573b98f7386 100644 --- a/qa/qa/page/menu/admin.rb +++ b/qa/qa/page/menu/admin.rb @@ -2,15 +2,8 @@ module QA module Page module Menu class Admin < Page::Base - ## - # TODO, define all selectors required by this page object - # - # See gitlab-org/gitlab-qa#154 - # - view 'app/views/admin/dashboard/index.html.haml' - - def go_to_license - click_link 'License' + view 'app/views/layouts/nav/sidebar/_admin.html.haml' do + element :settings, "_('Settings')" end def go_to_settings diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb index b2738152907..7e028add2ef 100644 --- a/qa/qa/page/menu/side.rb +++ b/qa/qa/page/menu/side.rb @@ -7,6 +7,8 @@ module QA element :settings_link, 'link_to edit_project_path' element :repository_link, "title: 'Repository'" element :pipelines_settings_link, "title: 'CI / CD'" + element :issues_link, /link_to.*shortcuts-issues/ + element :issues_link_text, "Issues" element :top_level_items, '.sidebar-top-level-items' element :activity_link, "title: 'Activity'" end @@ -43,6 +45,12 @@ module QA end end + def click_issues + within_sidebar do + click_link('Issues') + end + end + private def hover_settings diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb new file mode 100644 index 00000000000..ec94ff4ac98 --- /dev/null +++ b/qa/qa/page/merge_request/new.rb @@ -0,0 +1,31 @@ +module QA + module Page + module MergeRequest + class New < Page::Base + view 'app/views/shared/issuable/_form.html.haml' do + element :issuable_create_button + end + + view 'app/views/shared/issuable/form/_title.html.haml' do + element :issuable_form_title + end + + view 'app/views/shared/form_elements/_description.html.haml' do + element :issuable_form_description + end + + def create_merge_request + click_element :issuable_create_button + end + + def fill_title(title) + fill_element :issuable_form_title, title + end + + def fill_description(description) + fill_element :issuable_form_description, description + end + end + end + end +end diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb new file mode 100644 index 00000000000..b5903f536a4 --- /dev/null +++ b/qa/qa/page/project/issue/index.rb @@ -0,0 +1,17 @@ +module QA + module Page + module Project + module Issue + class Index < Page::Base + view 'app/views/projects/issues/_issue.html.haml' do + element :issue_link, 'link_to issue.title' + end + + def go_to_issue(title) + click_link(title) + end + end + end + end + end +end diff --git a/qa/qa/page/project/issue/new.rb b/qa/qa/page/project/issue/new.rb new file mode 100644 index 00000000000..7fc581da1ed --- /dev/null +++ b/qa/qa/page/project/issue/new.rb @@ -0,0 +1,33 @@ +module QA + module Page + module Project + module Issue + class New < Page::Base + view 'app/views/shared/issuable/_form.html.haml' do + element :submit_issue_button, 'form.submit "Submit' + end + + view 'app/views/shared/issuable/form/_title.html.haml' do + element :issue_title_textbox, 'form.text_field :title' + end + + view 'app/views/shared/form_elements/_description.html.haml' do + element :issue_description_textarea, "render 'projects/zen', f: form, attr: :description" + end + + def add_title(title) + fill_in 'issue_title', with: title + end + + def add_description(description) + fill_in 'issue_description', with: description + end + + def create_new_issue + click_on 'Submit issue' + end + end + end + end + end +end diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb new file mode 100644 index 00000000000..5bc0598a524 --- /dev/null +++ b/qa/qa/page/project/issue/show.rb @@ -0,0 +1,40 @@ +module QA + module Page + module Project + module Issue + class Show < Page::Base + view 'app/views/projects/issues/show.html.haml' do + element :issue_details, '.issue-details' + element :title, '.title' + end + + view 'app/views/shared/notes/_form.html.haml' do + element :new_note_form, 'new-note' + element :new_note_form, 'attr: :note' + end + + view 'app/views/shared/notes/_comment_button.html.haml' do + element :comment_button, '%strong Comment' + end + + def issue_title + find('.issue-details .title').text + end + + # Adds a comment to an issue + # attachment option should be an absolute path + def comment(text, attachment: nil) + fill_in(with: text, name: 'note[note]') + + unless attachment.nil? + QA::Page::Component::Dropzone.new(self, '.new-note') + .attach_file(attachment) + end + + click_on 'Comment' + end + end + end + end + end +end diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb index 9b1438f76d5..186a4724326 100644 --- a/qa/qa/page/project/new.rb +++ b/qa/qa/page/project/new.rb @@ -4,7 +4,7 @@ module QA class New < Page::Base view 'app/views/projects/_new_project_fields.html.haml' do element :project_namespace_select - element :project_namespace_field, 'select :namespace_id' + element :project_namespace_field, /select :namespace_id.*class: 'select2/ element :project_path, 'text_field :path' element :project_description, 'text_area :description' element :project_create_button, "submit 'Create project'" @@ -13,7 +13,7 @@ module QA def choose_test_namespace click_element :project_namespace_select - first('li', text: Runtime::Namespace.path).click + find('ul.select2-result-sub > li', text: Runtime::Namespace.path).click end def choose_name(name) diff --git a/qa/qa/page/project/settings/common.rb b/qa/qa/page/project/settings/common.rb index c7955124ef3..319cb1045b6 100644 --- a/qa/qa/page/project/settings/common.rb +++ b/qa/qa/page/project/settings/common.rb @@ -17,7 +17,12 @@ module QA def expand_section(name) page.within('#content-body') do page.within('section', text: name) do - click_button 'Expand' unless first('button', text: 'Collapse') + # Because it is possible to click the button before the JS toggle code is bound + wait(reload: false) do + click_button 'Expand' unless first('button', text: 'Collapse') + + page.has_content?('Collapse') + end yield if block_given? end diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 5e66e40a0b5..553d35f9579 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -3,23 +3,35 @@ module QA module Project class Show < Page::Base view 'app/views/shared/_clone_panel.html.haml' do + element :clone_holder, '.git-clone-holder' element :clone_dropdown element :clone_options_dropdown, '.clone-options-dropdown' + element :project_repository_location, 'text_field_tag :project_clone' end - view 'app/views/shared/_clone_panel.html.haml' do - element :project_repository_location, 'text_field_tag :project_clone' + view 'app/views/projects/_last_push.html.haml' do + element :create_merge_request end view 'app/views/projects/_home_panel.html.haml' do element :project_name end + view 'app/views/layouts/header/_new_dropdown.haml' do + element :new_menu_toggle + element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)" + end + def choose_repository_clone_http - click_element :clone_dropdown + wait(reload: false) do + click_element :clone_dropdown + + page.within('.clone-options-dropdown') do + click_link('HTTP') + end - page.within('.clone-options-dropdown') do - click_link('HTTP') + # Ensure git clone textbox was updated to http URI + page.has_css?('.git-clone-holder input#project_clone[value*="http"]') end end @@ -31,10 +43,20 @@ module QA find('.qa-project-name').text end + def new_merge_request + click_element :create_merge_request + end + def wait_for_push sleep 5 refresh end + + def go_to_new_issue + click_element :new_menu_toggle + + click_link 'New issue' + end end end end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index ce888b51ea5..a12d95683af 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -84,7 +84,7 @@ module QA config.javascript_driver = :chrome config.default_max_wait_time = 10 # https://github.com/mattheworiordan/capybara-screenshot/issues/164 - config.save_path = 'tmp' + config.save_path = File.expand_path('../../tmp', __dir__) end end diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb new file mode 100644 index 00000000000..fbf9a4d17e5 --- /dev/null +++ b/qa/qa/specs/features/merge_request/create_spec.rb @@ -0,0 +1,17 @@ +module QA + feature 'creates a merge request', :core do + scenario 'user creates a new merge request' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.title = 'This is a merge request' + merge_request.description = 'Great feature' + end + + expect(page).to have_content('This is a merge request') + expect(page).to have_content('Great feature') + expect(page).to have_content('Opened less than a minute ago') + end + end +end diff --git a/qa/qa/specs/features/project/create_issue_spec.rb b/qa/qa/specs/features/project/create_issue_spec.rb new file mode 100644 index 00000000000..b73f108c2d9 --- /dev/null +++ b/qa/qa/specs/features/project/create_issue_spec.rb @@ -0,0 +1,18 @@ +module QA + feature 'creates issue', :core do + let(:issue_title) { 'issue title' } + + scenario 'user creates issue' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Resource::Issue.fabricate! do |issue| + issue.title = issue_title + end + + Page::Menu::Side.act { click_issues } + + expect(page).to have_content(issue_title) + end + end +end diff --git a/qa/spec/factory/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb index 32405415126..8aaa6665a18 100644 --- a/qa/spec/factory/dependency_spec.rb +++ b/qa/spec/factory/dependency_spec.rb @@ -54,6 +54,19 @@ describe QA::Factory::Dependency do expect(factory).to have_received(:mydep=).with(dependency) end + + context 'when receives a caller factory as block argument' do + let(:dependency) { QA::Factory::Base } + + it 'calls given block with dependency factory and caller factory' do + allow_any_instance_of(QA::Factory::Base).to receive(:fabricate!).and_return(factory) + allow(QA::Factory::Product).to receive(:populate!).and_return(spy('any')) + + subject.build! + + expect(block).to have_received(:call).with(an_instance_of(QA::Factory::Base), factory) + end + end end end end diff --git a/qa/spec/fixtures/banana_sample.gif b/qa/spec/fixtures/banana_sample.gif Binary files differnew file mode 100644 index 00000000000..1322ac92d14 --- /dev/null +++ b/qa/spec/fixtures/banana_sample.gif diff --git a/qa/spec/runtime/rsa_key.rb b/qa/spec/runtime/rsa_key.rb index ff277b9077b..6d7ab4dcd2e 100644 --- a/qa/spec/runtime/rsa_key.rb +++ b/qa/spec/runtime/rsa_key.rb @@ -3,7 +3,7 @@ describe QA::Runtime::RSAKey do subject { described_class.new.public_key } it 'generates a public RSA key' do - expect(subject).to match(/\Assh\-rsa AAAA[0-9A-Za-z+\/]+={0,3}\z/) + expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}\z}) end end end |
