diff options
21 files changed, 377 insertions, 26 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue index ee8eb206980..802827fce76 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue @@ -117,7 +117,7 @@ export default { <button :disabled="!hasChanges" type="button" - class="btn btn-primary btn-sm btn-block" + class="btn btn-primary btn-sm btn-block qa-begin-commit-button" @click="toggleIsSmall" > {{ __('Commit…') }} @@ -147,7 +147,7 @@ export default { <loading-button :loading="submitCommitLoading" :label="commitButtonText" - container-class="btn btn-success btn-sm float-left" + container-class="btn btn-success btn-sm float-left qa-commit-button" @click="commitChanges" /> <button diff --git a/app/assets/javascripts/ide/components/file_templates/bar.vue b/app/assets/javascripts/ide/components/file_templates/bar.vue index 23be5f45f16..3587626c580 100644 --- a/app/assets/javascripts/ide/components/file_templates/bar.vue +++ b/app/assets/javascripts/ide/components/file_templates/bar.vue @@ -47,7 +47,7 @@ export default { </script> <template> - <div class="d-flex align-items-center ide-file-templates"> + <div class="d-flex align-items-center ide-file-templates qa-file-templates-bar"> <strong class="append-right-default"> {{ __('File templates') }} </strong> @@ -63,7 +63,7 @@ export default { :is-async-data="true" :searchable="true" :title="__('File templates')" - class="mr-2" + class="mr-2 qa-file-template-dropdown" @click="selectTemplate" /> <transition name="fade"> diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue index ef1f6de3a86..94222c08e91 100644 --- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue +++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue @@ -92,7 +92,7 @@ export default { v-model="search" :placeholder="__('Filter...')" type="search" - class="dropdown-input-field" + class="dropdown-input-field qa-dropdown-filter-input" /> <i aria-hidden="true" diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 39d46a91731..9f9e638f1aa 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -45,7 +45,7 @@ export default { <new-entry-button :label="__('New file')" :show-label="false" - class="d-flex border-0 p-0 mr-3" + class="d-flex border-0 p-0 mr-3 qa-new-file" icon="doc-new" @click="openNewEntryModal({ type: 'blob' })" /> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index cfe25084b42..e88f01fb4f4 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -43,7 +43,7 @@ export default { <template> <div - class="ide-file-list" + class="ide-file-list qa-file-list" > <template v-if="showLoading"> <div diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index bcd53ac1ba2..f0a04011a3e 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -110,12 +110,12 @@ export default { ref="fieldName" v-model="entryName" type="text" - class="form-control" + class="form-control qa-full-file-path" placeholder="/dir/file_name" /> <ul v-if="isCreatingNew" - class="prepend-top-default list-inline" + class="prepend-top-default list-inline qa-template-list" > <li v-for="(template, index) in templateTypes" diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue index b023c5cfeb1..b5444d43ded 100644 --- a/app/assets/javascripts/vue_shared/components/gl_modal.vue +++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue @@ -41,10 +41,14 @@ export default { }, }, mounted() { - $(this.$el).on('shown.bs.modal', this.opened).on('hidden.bs.modal', this.closed); + $(this.$el) + .on('shown.bs.modal', this.opened) + .on('hidden.bs.modal', this.closed); }, beforeDestroy() { - $(this.$el).off('shown.bs.modal', this.opened).off('hidden.bs.modal', this.closed); + $(this.$el) + .off('shown.bs.modal', this.opened) + .off('hidden.bs.modal', this.closed); }, methods: { emitCancel(event) { @@ -103,7 +107,7 @@ export default { <slot name="footer"> <button type="button" - class="btn js-modal-cancel-action" + class="btn js-modal-cancel-action qa-modal-cancel-button" data-dismiss="modal" @click="emitCancel($event)" > @@ -112,7 +116,7 @@ export default { <button :class="`btn-${footerPrimaryButtonVariant}`" type="button" - class="btn js-modal-primary-action" + class="btn js-modal-primary-action qa-modal-primary-button" data-dismiss="modal" @click="emitSubmit($event)" > diff --git a/app/views/projects/blob/_template_selectors.html.haml b/app/views/projects/blob/_template_selectors.html.haml index 5b092427496..2c8dd45670f 100644 --- a/app/views/projects/blob/_template_selectors.html.haml +++ b/app/views/projects/blob/_template_selectors.html.haml @@ -3,15 +3,15 @@ Template .template-selector-dropdowns-wrap .template-type-selector.js-template-type-selector-wrap.hidden - = dropdown_tag("Choose type", options: { toggle_class: 'js-template-type-selector', title: "Choose a template type" } ) + = dropdown_tag("Choose type", options: { toggle_class: 'js-template-type-selector qa-template-type-dropdown', title: "Choose a template type" } ) .license-selector.js-license-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a license template", options: { toggle_class: 'js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) + = dropdown_tag("Apply a license template", options: { toggle_class: 'js-license-selector qa-license-dropdown', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) .gitignore-selector.js-gitignore-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) + = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'js-gitignore-selector qa-gitignore-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) + = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector qa-gitlab-ci-yml-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) .dockerfile-selector.js-dockerfile-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) + = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector qa-dockerfile-dropdown', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) .template-selectors-undo-menu.hidden %span.text-info Template applied %button.btn.btn-sm.btn-info Undo diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 8b9c52f0802..45515fb492f 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -8,7 +8,7 @@ - if show_menu .project-action-button.dropdown.inline - %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' } + %a.btn.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' } = icon('plus') = icon("caret-down") %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown @@ -28,7 +28,7 @@ %li.dropdown-header= _('This repository') - if can_push_code - %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') + %li.qa-new-file-option= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') - unless @project.empty_repo? %li= link_to _('New branch'), new_project_branch_path(@project) %li= link_to _('New tag'), new_project_tag_path(@project) diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 9d196075bf1..601e3f25852 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -82,7 +82,7 @@ - if can_collaborate = succeed " " do - = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default' do + = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do = _('Web IDE') = render 'projects/buttons/download', project: @project, ref: @ref @@ -16,6 +16,8 @@ module QA autoload :Browser, 'qa/runtime/browser' autoload :Env, 'qa/runtime/env' autoload :Address, 'qa/runtime/address' + autoload :Path, 'qa/runtime/path' + autoload :Fixtures, 'qa/runtime/fixtures' module API autoload :Client, 'qa/runtime/api/client' @@ -214,6 +216,10 @@ module QA autoload :New, 'qa/page/project/wiki/new' autoload :Show, 'qa/page/project/wiki/show' end + + module WebIDE + autoload :Edit, 'qa/page/project/web_ide/edit' + end end module Profile @@ -257,6 +263,8 @@ module QA autoload :Dropzone, 'qa/page/component/dropzone' autoload :GroupsFilter, 'qa/page/component/groups_filter' autoload :Select2, 'qa/page/component/select2' + autoload :DropdownFilter, 'qa/page/component/dropdown_filter' + module Issuable autoload :Common, 'qa/page/component/issuable/common' end diff --git a/qa/qa/factory/resource/file.rb b/qa/qa/factory/resource/file.rb index 2016d10ddae..f8dea06d361 100644 --- a/qa/qa/factory/resource/file.rb +++ b/qa/qa/factory/resource/file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module QA module Factory module Resource @@ -19,7 +21,7 @@ module QA def fabricate! project.visit! - Page::Project::Show.act { go_to_new_file! } + Page::Project::Show.act { create_new_file! } Page::File::Form.perform do |page| page.add_name(@name) diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 142707521df..826cfe6b198 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'capybara/dsl' module QA @@ -7,6 +9,8 @@ module QA include Scenario::Actable extend SingleForwardable + ElementNotFound = Class.new(RuntimeError) + def_delegators :evaluator, :view, :views def refresh diff --git a/qa/qa/page/component/dropdown_filter.rb b/qa/qa/page/component/dropdown_filter.rb new file mode 100644 index 00000000000..e896c382779 --- /dev/null +++ b/qa/qa/page/component/dropdown_filter.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module DropdownFilter + def filter_and_select(item) + wait(reload: false) do + page.has_css?('.dropdown-input-field') + end + + find('.dropdown-input-field').set(item) + click_link item + end + end + end + end +end diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb index f6e502f500b..3752f5066c9 100644 --- a/qa/qa/page/file/form.rb +++ b/qa/qa/page/file/form.rb @@ -3,6 +3,7 @@ module QA module File class Form < Page::Base include Shared::CommitMessage + include Page::Component::DropdownFilter view 'app/views/projects/blob/_editor.html.haml' do element :file_name, "text_field_tag 'file_name'" @@ -13,6 +14,14 @@ module QA element :commit_changes, "button_tag 'Commit changes'" end + view 'app/views/projects/blob/_template_selectors.html.haml' do + element :template_type_dropdown + element :gitignore_dropdown + element :gitlab_ci_yml_dropdown + element :dockerfile_dropdown + element :license_dropdown + end + def add_name(name) fill_in 'file_name', with: name end @@ -29,6 +38,25 @@ module QA click_on 'Commit changes' end + def select_template(template_type, template) + click_element :template_type_dropdown + click_link template_type + + case template_type + when '.gitignore' + click_element :gitignore_dropdown + when '.gitlab-ci.yml' + click_element :gitlab_ci_yml_dropdown + when 'Dockerfile' + click_element :dockerfile_dropdown + when 'LICENSE' + click_element :license_dropdown + else + raise %Q(Unsupported template_type "#{template_type}". Please confirm that it is a valid option.) + end + filter_and_select template + end + private def text_area diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 267e7bbc249..2e7be1deb27 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module QA module Page module Project @@ -31,16 +33,22 @@ module QA element :tree_holder, '.tree-holder' end - view 'app/presenters/project_presenter.rb' do - element :new_file_button, "_('New file')," + view 'app/views/projects/buttons/_dropdown.html.haml' do + element :create_new_dropdown + element :new_file_option + end + + view 'app/views/projects/tree/_tree_header.html.haml' do + element :web_ide_button end def project_name find('.qa-project-name').text end - def go_to_new_file! - click_on 'New file' + def create_new_file! + click_element :create_new_dropdown + click_element :new_file_option end def switch_to_branch(branch_name) @@ -78,6 +86,10 @@ module QA def fork_project click_on 'Fork' end + + def open_web_ide! + click_element :web_ide_button + end end end end diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb new file mode 100644 index 00000000000..23e580b81b6 --- /dev/null +++ b/qa/qa/page/project/web_ide/edit.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module QA + module Page + module Project + module WebIDE + class Edit < Page::Base + include Page::Component::DropdownFilter + + view 'app/assets/javascripts/ide/components/ide_tree.vue' do + element :new_file + end + + view 'app/assets/javascripts/ide/components/ide_tree_list.vue' do + element :file_list + end + + view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do + element :full_file_path + element :template_list + end + + view 'app/assets/javascripts/ide/components/file_templates/bar.vue' do + element :file_templates_bar + element :file_template_dropdown + end + + view 'app/assets/javascripts/ide/components/file_templates/dropdown.vue' do + element :dropdown_filter_input + end + + view 'app/assets/javascripts/ide/components/commit_sidebar/form.vue' do + element :begin_commit_button + element :commit_button + end + + def has_file?(file_name) + within_element(:file_list) do + page.has_content? file_name + end + end + + def create_new_file_from_template(file_name, template) + click_element :new_file + within_element(:template_list) do + begin + click_on file_name + rescue Capybara::ElementNotFound + raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.) + end + end + + wait(reload: false) do + within_element(:file_templates_bar) do + click_element :file_template_dropdown + fill_element :dropdown_filter_input, template + + begin + click_on template + rescue Capybara::ElementNotFound + raise ElementNotFound, %Q(Couldn't find template "#{template}" for #{file_name}. Please confirm that it exists in the list of templates.) + end + end + end + end + + def commit_changes + click_element :begin_commit_button + click_element :commit_button + + wait(reload: false) do + page.has_content?('Your changes have been committed') + end + end + end + end + end + end +end diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb new file mode 100644 index 00000000000..72004d5b00a --- /dev/null +++ b/qa/qa/runtime/fixtures.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module QA + module Runtime + module Fixtures + def fetch_template_from_api(api_path, key) + request = Runtime::API::Request.new(api_client, "/templates/#{api_path}/#{key}") + get request.url + json_body[:content] + end + + private + + def api_client + @api_client ||= Runtime::API::Client.new(:gitlab) + end + end + end +end diff --git a/qa/qa/runtime/path.rb b/qa/qa/runtime/path.rb new file mode 100644 index 00000000000..3169c5dd743 --- /dev/null +++ b/qa/qa/runtime/path.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module QA + module Runtime + module Path + extend self + + def qa_root + ::File.expand_path('../../', __dir__) + end + end + end +end 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 new file mode 100644 index 00000000000..57892b65e28 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'File templates' do + include Runtime::Fixtures + + def login + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + before(:all) do + login + + @project = Factory::Resource::Project.fabricate! do |project| + project.name = 'file-template-project' + project.description = 'Add file templates via the Files view' + end + + Page::Menu::Main.act { sign_out } + end + + templates = [ + { + file_name: '.gitignore', + name: 'Android', + api_path: 'gitignores', + api_key: 'Android' + }, + { + file_name: '.gitlab-ci.yml', + name: 'Julia', + api_path: 'gitlab_ci_ymls', + api_key: 'Julia' + }, + { + file_name: 'Dockerfile', + name: 'Python', + api_path: 'dockerfiles', + api_key: 'Python' + }, + { + file_name: 'LICENSE', + name: 'Mozilla Public License 2.0', + api_path: 'licenses', + api_key: 'mpl-2.0' + } + ] + + templates.each do |template| + it "user adds #{template[:file_name]} via file template #{template[:name]}" do + content = fetch_template_from_api(template[:api_path], template[:api_key]) + + login + @project.visit! + + Page::Project::Show.act { create_new_file! } + Page::File::Form.perform do |page| + page.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) + + expect(page).to have_content('The file has been successfully created.') + expect(page).to have_content(template[:file_name]) + expect(page).to have_content('Add new file') + expect(page).to have_content(content[0..100]) + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb new file mode 100644 index 00000000000..dc96b19d36d --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Web IDE file templates' do + include Runtime::Fixtures + + def login + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + before(:all) do + login + + @project = Factory::Resource::Project.fabricate! do |project| + project.name = 'file-template-project' + project.description = 'Add file templates via the Web IDE' + end + + # Add a file via the regular Files view because the Web IDE isn't + # available unless there is a file present + Page::Project::Show.act { create_new_file! } + Page::File::Form.perform do |page| + page.add_name('dummy') + page.add_content('Enable the Web IDE') + page.commit_changes + end + + Page::Menu::Main.act { sign_out } + end + + templates = [ + { + file_name: '.gitignore', + name: 'Android', + api_path: 'gitignores', + api_key: 'Android' + }, + { + file_name: '.gitlab-ci.yml', + name: 'Julia', + api_path: 'gitlab_ci_ymls', + api_key: 'Julia' + }, + { + file_name: 'Dockerfile', + name: 'Python', + api_path: 'dockerfiles', + api_key: 'Python' + }, + { + file_name: 'LICENSE', + name: 'Mozilla Public License 2.0', + api_path: 'licenses', + api_key: 'mpl-2.0' + } + ] + + templates.each do |template| + it "user adds #{template[:file_name]} via file template #{template[:name]}" do + content = fetch_template_from_api(template[:api_path], template[:api_key]) + + login + @project.visit! + + Page::Project::Show.act { open_web_ide! } + Page::Project::WebIDE::Edit.perform do |page| + page.create_new_file_from_template template[:file_name], template[:name] + + expect(page.has_file?(template[:file_name])).to be_truthy + end + + expect(page).to have_button('Undo') + expect(page).to have_content(content[0..100]) + + Page::Project::WebIDE::Edit.perform do |page| + page.commit_changes + end + + expect(page).to have_content(template[:file_name]) + expect(page).to have_content(content[0..100]) + end + end + end + end +end |