From 8aad78838374c761a69d7f0e9727706a611ebcaf Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 04:10:28 +0300 Subject: [PATCH 01/13] Added data-project attribute to body tag. --- app/views/layouts/application.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 2b86b289bb..504abd8f3e 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body{class: "#{user_application_theme}", 'data-page' => body_data_page} + %body{class: "#{user_application_theme}", 'data-page' => body_data_page, 'data-project' => "#{@project.path if @project}"} = Gon::Base.render_data -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body. From b13f6fa99dace85844db2591c922fe7158e7baac Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 04:10:47 +0300 Subject: [PATCH 02/13] Added a common util to get project slug. --- app/assets/javascripts/lib/common_utils.js.coffee | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 app/assets/javascripts/lib/common_utils.js.coffee diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/common_utils.js.coffee new file mode 100644 index 0000000000..3ec569f73e --- /dev/null +++ b/app/assets/javascripts/lib/common_utils.js.coffee @@ -0,0 +1,13 @@ +((w) -> + + w.gl or= {} + w.gl.utils or= {} + + w.gl.utils.getProjectSlug = -> + + $body = $ 'body' + isInProjectPage = $body.data('page').split(':')[0] is 'projects' + + return if isInProjectPage then $body.data 'project' else null + +) window From 5b64e486cceda778161ee99da6f60a06c3ba4d08 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 04:11:23 +0300 Subject: [PATCH 03/13] Added projectOptions and dashboardOptions into gl object. --- app/views/layouts/_search.html.haml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index b49207fc31..b76e31f7dc 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -36,6 +36,21 @@ - else = hidden_field_tag :search_code, true + :javascript + gl.projectOptions = gl.projectOptions || {}; + gl.projectOptions["#{@project.path}"] = { + issuesPath: "#{namespace_project_issues_path(@project.namespace, @project)}", + mrPath: "#{namespace_project_merge_requests_path(@project.namespace, @project)}", + projectName: "#{@project.name}" + }; + + :javascript + gl.dashboardOptions = { + issuesPath: "#{issues_dashboard_url}", + mrPath: "#{merge_requests_dashboard_url}" + }; + + - if @snippet || @snippets = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref From 495d27be382266513f159a5babc334a93b540a95 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 04:13:02 +0300 Subject: [PATCH 04/13] Show category search content in the search dropdown. --- .../javascripts/search_autocomplete.js.coffee | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 5eb915a51e..0ba2c4958a 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -67,8 +67,14 @@ class @SearchAutocomplete getData: (term, callback) -> _this = @ - # Do not trigger request if input is empty - return if @searchInput.val() is '' + unless term + return unless @hasLocationBadge() + + if contents = @getCategoryContents() + @searchInput.data('glDropdown').filter.options.callback contents + @enableAutocomplete() + + return # Prevent multiple ajax calls return if @loadingSuggestions @@ -122,6 +128,27 @@ class @SearchAutocomplete ).always -> _this.loadingSuggestions = false + + getCategoryContents: -> + + userId = gon.current_user_id + projectSlug = gl.utils.getProjectSlug() + projectOptions = gl.projectOptions[projectSlug] + + return null if not projectSlug or not projectOptions + + { issuesPath, mrPath, projectName } = projectOptions + + return [ + { header: "Go to in #{projectName}" } + { text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" } + { text: "Issues I've created", url: "#{issuesPath}/?author_id=#{userId}" } + 'separator' + { text: 'Merge requests assigned to me', url: "#{mrPath}/?assignee_id=#{userId}" } + { text: "Merge requests I've created", url: "#{mrPath}/?author_id=#{userId}" } + ] + + serializeState: -> { # Search Criteria @@ -209,6 +236,13 @@ class @SearchAutocomplete @isFocused = true @wrap.addClass('search-active') + if @hasLocationBadge() and @getValue() is '' + @getData() + + + getValue: -> return @searchInput.val() + + onClearInputClick: (e) => e.preventDefault() @searchInput.val('').focus() @@ -229,6 +263,10 @@ class @SearchAutocomplete @locationBadgeEl.text(badgeText).show() @wrap.addClass('has-location-badge') + + hasLocationBadge: -> return @wrap.is '.has-location-badge' + + restoreOriginalState: -> inputs = Object.keys @originalState @@ -257,13 +295,14 @@ class @SearchAutocomplete @getElement("##{input}").val('') + removeLocationBadge: -> + @locationBadgeEl.hide() - - # Reset state @resetSearchState() - @wrap.removeClass('has-location-badge') + @disableAutocomplete() + disableAutocomplete: -> @searchInput.addClass('disabled') From 36f67b305f37cdf4eb9f75f12cfde3b0dfc01183 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 14:49:25 +0300 Subject: [PATCH 05/13] Show dashboard related options in the search dropdown. --- app/assets/javascripts/search_autocomplete.js.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 0ba2c4958a..943dba9bcb 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -132,12 +132,14 @@ class @SearchAutocomplete getCategoryContents: -> userId = gon.current_user_id + projectName = 'Dashboard' projectSlug = gl.utils.getProjectSlug() projectOptions = gl.projectOptions[projectSlug] - return null if not projectSlug or not projectOptions - - { issuesPath, mrPath, projectName } = projectOptions + if projectSlug and projectOptions + { issuesPath, mrPath, projectName } = projectOptions + else + { issuesPath, mrPath } = gl.dashboardOptions return [ { header: "Go to in #{projectName}" } From 50b3b8ce80b3573f53c22ac5ff34391b5bc469d8 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 7 Jun 2016 17:54:29 +0300 Subject: [PATCH 06/13] Added tests for categorised search autocomplete. --- .../javascripts/search_autocomplete.js.coffee | 2 +- spec/features/search_spec.rb | 79 +++++++++++ .../fixtures/search_autocomplete.html.haml | 10 ++ spec/javascripts/notes_spec.js.coffee | 2 +- spec/javascripts/project_title_spec.js.coffee | 2 +- .../search_autocomplete_spec.js.coffee | 129 ++++++++++++++++++ 6 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 spec/javascripts/fixtures/search_autocomplete.html.haml create mode 100644 spec/javascripts/search_autocomplete_spec.js.coffee diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 943dba9bcb..8493d2684d 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -134,7 +134,7 @@ class @SearchAutocomplete userId = gon.current_user_id projectName = 'Dashboard' projectSlug = gl.utils.getProjectSlug() - projectOptions = gl.projectOptions[projectSlug] + projectOptions = gl.projectOptions?[projectSlug] if projectSlug and projectOptions { issuesPath, mrPath, projectName } = projectOptions diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 029a11ea43..4f4d4b1e3e 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -47,4 +47,83 @@ describe "Search", feature: true do expect(page).to have_link(snippet.title) end end + + + describe 'Right header search field', feature: true do + + describe 'Search in project page' do + before do + visit namespace_project_path(project.namespace, project) + end + + it 'top right search form is present' do + expect(page).to have_selector('#search') + end + + it 'top right search form contains location badge' do + expect(page).to have_selector('.has-location-badge') + end + + context 'clicking the search field', js: true do + it 'should show category search dropdown' do + page.find('#search').click + + expect(page).to have_selector('.dropdown-header', text: /go to in #{project.name}/i) + end + end + + context 'click the links in the category search dropdown', js: true do + + before do + page.find('#search').click + end + + it 'should take user to her issues page when issues assigned is clicked' do + find('.dropdown-menu').click_link 'Issues assigned to me' + sleep 2 + + expect(page).to have_selector('.issues-holder') + expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + end + + it 'should take user to her issues page when issues authored is clicked' do + find('.dropdown-menu').click_link "Issues I've created" + sleep 2 + + expect(page).to have_selector('.issues-holder') + expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name) + end + + it 'should take user to her MR page when MR assigned is clicked' do + find('.dropdown-menu').click_link 'Merge requests assigned to me' + sleep 2 + + expect(page).to have_selector('.merge-requests-holder') + expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + end + + it 'should take user to her MR page when MR authored is clicked' do + find('.dropdown-menu').click_link "Merge requests I've created" + sleep 2 + + expect(page).to have_selector('.merge-requests-holder') + expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name) + end + end + + context 'entering text into the search field', js: true do + before do + page.within '.search-input-wrap' do + fill_in "search", with: project.name[0..3] + end + end + + it 'should not display the category search dropdown' do + expect(page).not_to have_selector('.dropdown-header', text: /go to in #{project.name}/i) + end + end + end + end + + end diff --git a/spec/javascripts/fixtures/search_autocomplete.html.haml b/spec/javascripts/fixtures/search_autocomplete.html.haml new file mode 100644 index 0000000000..7785120da5 --- /dev/null +++ b/spec/javascripts/fixtures/search_autocomplete.html.haml @@ -0,0 +1,10 @@ +.search.search-form.has-location-badge + %form.navbar-form + .search-input-container + %div.location-badge + This project + .search-input-wrap + .dropdown + %input#search.search-input.dropdown-menu-toggle + .dropdown-menu.dropdown-select + .dropdown-content diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee index dd160e821b..3a3c8d63e8 100644 --- a/spec/javascripts/notes_spec.js.coffee +++ b/spec/javascripts/notes_spec.js.coffee @@ -1,7 +1,7 @@ #= require notes #= require gl_form -window.gon = {} +window.gon or= {} window.disableButtonIfEmptyField = -> null describe 'Notes', -> diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee index 1cf34d4d2d..9be29097f4 100644 --- a/spec/javascripts/project_title_spec.js.coffee +++ b/spec/javascripts/project_title_spec.js.coffee @@ -6,7 +6,7 @@ #= require project_select #= require project -window.gon = {} +window.gon or= {} window.gon.api_version = 'v3' describe 'Project Title', -> diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee new file mode 100644 index 0000000000..5212f5d223 --- /dev/null +++ b/spec/javascripts/search_autocomplete_spec.js.coffee @@ -0,0 +1,129 @@ +#= require gl_dropdown +#= require search_autocomplete +#= require jquery +#= require lib/common_utils +#= require lib/type_utility +#= require fuzzaldrin-plus + + +widget = null +userId = 1 +window.gon or= {} +window.gon.current_user_id = userId + +dashboardIssuesPath = '/dashboard/issues' +dashboardMRsPath = '/dashboard/merge_requests' +projectIssuesPath = "/gitlab-org/gitlab-ce/issues" +projectMRsPath = "/gitlab-org/gitlab-ce/merge_requests" +projectName = 'GitLab Community Edition' + +# Add required attributes to body before starting the test. +addBodyAttributes = (page = 'groups') -> + + $('body').removeAttr 'data-page' + $('body').removeAttr 'data-project' + + $('body').data 'page', "#{page}:show" + $('body').data 'project', 'gitlab-ce' + + +# Mock `gl` object in window for dashboard specific page. App code will need it. +mockDashboardOptions = -> + + window.gl or= {} + window.gl.dashboardOptions = + issuesPath: dashboardIssuesPath + mrPath : dashboardMRsPath + + +# Mock `gl` object in window for project specific page. App code will need it. +mockProjectOptions = -> + + window.gl or= {} + window.gl.projectOptions = + 'gitlab-ce' : + issuesPath : projectIssuesPath + mrPath : projectMRsPath + projectName : projectName + + +assertLinks = (list, a1, a2, a3, a4) -> + + expect(list.find(a1).length).toBe 1 + expect(list.find(a1).text()).toBe ' Issues assigned to me ' + + expect(list.find(a2).length).toBe 1 + expect(list.find(a2).text()).toBe " Issues I've created " + + expect(list.find(a3).length).toBe 1 + expect(list.find(a3).text()).toBe ' Merge requests assigned to me ' + + expect(list.find(a4).length).toBe 1 + expect(list.find(a4).text()).toBe " Merge requests I've created " + + + +describe 'Search autocomplete dropdown', -> + + fixture.preload 'search_autocomplete.html' + + beforeEach -> + + fixture.load 'search_autocomplete.html' + widget = new SearchAutocomplete + + + it 'should show Dashboard specific dropdown menu', -> + + addBodyAttributes() + mockDashboardOptions() + + # Focus input to show dropdown list. + widget.searchInput.focus() + + w = widget.wrap.find '.dropdown-menu' + l = w.find 'ul' + + # # Expect dropdown and dropdown header + expect(w.find('.dropdown-header').text()).toBe 'Go to in Dashboard' + + # Create links then assert link urls and inner texts + issuesAssignedToMeLink = "#{dashboardIssuesPath}/?assignee_id=#{userId}" + issuesIHaveCreatedLink = "#{dashboardIssuesPath}/?author_id=#{userId}" + mrsAssignedToMeLink = "#{dashboardMRsPath}/?assignee_id=#{userId}" + mrsIHaveCreatedLink = "#{dashboardMRsPath}/?author_id=#{userId}" + + a1 = "a[href='#{issuesAssignedToMeLink}']" + a2 = "a[href='#{issuesIHaveCreatedLink}']" + a3 = "a[href='#{mrsAssignedToMeLink}']" + a4 = "a[href='#{mrsIHaveCreatedLink}']" + + assertLinks l, a1, a2, a3, a4 + + + it 'should show Project specific dropdown menu', -> + + addBodyAttributes 'projects' + mockProjectOptions() + + # Focus input to show dropdown list. + widget.searchInput.focus() + + w = widget.wrap.find '.dropdown-menu' + l = w.find 'ul' + + # Expect dropdown and dropdown header + expect(w.find('.dropdown-header').text()).toBe "Go to in #{projectName}" + + # Create links then verify link urls and inner texts + issuesAssignedToMeLink = "#{projectIssuesPath}/?assignee_id=#{userId}" + issuesIHaveCreatedLink = "#{projectIssuesPath}/?author_id=#{userId}" + mrsAssignedToMeLink = "#{projectMRsPath}/?assignee_id=#{userId}" + mrsIHaveCreatedLink = "#{projectMRsPath}/?author_id=#{userId}" + + a1 = "a[href='#{issuesAssignedToMeLink}']" + a2 = "a[href='#{issuesIHaveCreatedLink}']" + a3 = "a[href='#{mrsAssignedToMeLink}']" + a4 = "a[href='#{mrsIHaveCreatedLink}']" + + assertLinks l, a1, a2, a3, a4 From 57ec290f0c47d04745e49598c490735b3e650edb Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Wed, 8 Jun 2016 16:37:13 +0300 Subject: [PATCH 07/13] Updated CHANGELOG. --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index bee1a82497..b37c23de40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -40,6 +40,7 @@ v 8.9.0 (unreleased) - Put project Files and Commits tabs under Code tab - Replace Colorize with Rainbow for coloring console output in Rake tasks. - An indicator is now displayed at the top of the comment field for confidential issues. + - Show categorised search queries in the search autocomplete v 8.8.4 (unreleased) - Ensure branch cleanup regardless of whether the GitHub import process succeeds From c1818eec1d10688c05467a900f60259490b627c1 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 9 Jun 2016 04:21:35 +0300 Subject: [PATCH 08/13] Inject group options. --- app/views/layouts/_search.html.haml | 12 +++++++++++- app/views/layouts/application.html.haml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index b76e31f7dc..5c6429d07b 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -41,9 +41,19 @@ gl.projectOptions["#{@project.path}"] = { issuesPath: "#{namespace_project_issues_path(@project.namespace, @project)}", mrPath: "#{namespace_project_merge_requests_path(@project.namespace, @project)}", - projectName: "#{@project.name}" + name: "#{@project.name}" }; + - if @group + :javascript + gl.groupOptions = gl.groupOptions || {}; + gl.groupOptions["#{@group.path}"] = { + name: "#{@group.name}", + issuesPath: "#{issues_group_path(@group.path)}", + mrPath: "#{merge_requests_group_path(@group.path)}" + }; + + :javascript gl.dashboardOptions = { issuesPath: "#{issues_dashboard_url}", diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 504abd8f3e..33cedaaf2e 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body{class: "#{user_application_theme}", 'data-page' => body_data_page, 'data-project' => "#{@project.path if @project}"} + %body{class: "#{user_application_theme}", data: {page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}"}} = Gon::Base.render_data -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body. From 7df512d5a209cc82b06020d6196a47d79b73f861 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 9 Jun 2016 04:21:50 +0300 Subject: [PATCH 09/13] Add new utils. --- .../javascripts/lib/common_utils.js.coffee | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/common_utils.js.coffee index 3ec569f73e..95c4dd319a 100644 --- a/app/assets/javascripts/lib/common_utils.js.coffee +++ b/app/assets/javascripts/lib/common_utils.js.coffee @@ -3,11 +3,24 @@ w.gl or= {} w.gl.utils or= {} + w.gl.utils.isInGroupsPage = -> + + return $('body').data('page').split(':')[0] is 'groups' + + + w.gl.utils.isInProjectPage = -> + + return $('body').data('page').split(':')[0] is 'projects' + + w.gl.utils.getProjectSlug = -> - $body = $ 'body' - isInProjectPage = $body.data('page').split(':')[0] is 'projects' + return if @isInProjectPage() then $('body').data 'project' else null + + + w.gl.utils.getGroupSlug = -> + + return if @isInGroupsPage() then $('body').data 'group' else null - return if isInProjectPage then $body.data 'project' else null ) window From 522ef5754d222de1e8687c4b9bbc081478c69041 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 9 Jun 2016 04:22:13 +0300 Subject: [PATCH 10/13] Refactor search autocomplete to support groups category contents. --- .../javascripts/search_autocomplete.js.coffee | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 8493d2684d..421328554b 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -68,8 +68,6 @@ class @SearchAutocomplete _this = @ unless term - return unless @hasLocationBadge() - if contents = @getCategoryContents() @searchInput.data('glDropdown').filter.options.callback contents @enableAutocomplete() @@ -131,18 +129,22 @@ class @SearchAutocomplete getCategoryContents: -> - userId = gon.current_user_id - projectName = 'Dashboard' - projectSlug = gl.utils.getProjectSlug() - projectOptions = gl.projectOptions?[projectSlug] + userId = gon.current_user_id + { utils, projectOptions, groupOptions, dashboardOptions } = gl - if projectSlug and projectOptions - { issuesPath, mrPath, projectName } = projectOptions - else - { issuesPath, mrPath } = gl.dashboardOptions + if utils.isInGroupsPage() and groupOptions + options = groupOptions[utils.getGroupSlug()] - return [ - { header: "Go to in #{projectName}" } + else if utils.isInProjectPage() and projectOptions + options = projectOptions[utils.getProjectSlug()] + + else if dashboardOptions + options = dashboardOptions + + { issuesPath, mrPath, name } = options + + items = [ + { header: "#{name}" } { text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" } { text: "Issues I've created", url: "#{issuesPath}/?author_id=#{userId}" } 'separator' @@ -150,6 +152,10 @@ class @SearchAutocomplete { text: "Merge requests I've created", url: "#{mrPath}/?author_id=#{userId}" } ] + items.splice 0, 1 unless name + + return items + serializeState: -> { @@ -238,8 +244,7 @@ class @SearchAutocomplete @isFocused = true @wrap.addClass('search-active') - if @hasLocationBadge() and @getValue() is '' - @getData() + @getData() if @getValue() is '' getValue: -> return @searchInput.val() From e864bdf25b0082be8d0847fed6a2d16fe348ae59 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Fri, 10 Jun 2016 01:05:09 +0300 Subject: [PATCH 11/13] Fix specs and add new tests. --- spec/features/search_spec.rb | 4 +- .../search_autocomplete_spec.js.coffee | 104 +++++++++++------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 4f4d4b1e3e..b9e63a7152 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -68,7 +68,7 @@ describe "Search", feature: true do it 'should show category search dropdown' do page.find('#search').click - expect(page).to have_selector('.dropdown-header', text: /go to in #{project.name}/i) + expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i) end end @@ -119,7 +119,7 @@ describe "Search", feature: true do end it 'should not display the category search dropdown' do - expect(page).not_to have_selector('.dropdown-header', text: /go to in #{project.name}/i) + expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i) end end end diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee index 5212f5d223..e77177783a 100644 --- a/spec/javascripts/search_autocomplete_spec.js.coffee +++ b/spec/javascripts/search_autocomplete_spec.js.coffee @@ -13,18 +13,33 @@ window.gon.current_user_id = userId dashboardIssuesPath = '/dashboard/issues' dashboardMRsPath = '/dashboard/merge_requests' -projectIssuesPath = "/gitlab-org/gitlab-ce/issues" -projectMRsPath = "/gitlab-org/gitlab-ce/merge_requests" +projectIssuesPath = '/gitlab-org/gitlab-ce/issues' +projectMRsPath = '/gitlab-org/gitlab-ce/merge_requests' +groupIssuesPath = '/groups/gitlab-org/issues' +groupMRsPath = '/groups/gitlab-org/merge_requests' projectName = 'GitLab Community Edition' +groupName = 'Gitlab Org' + # Add required attributes to body before starting the test. -addBodyAttributes = (page = 'groups') -> +# section would be dashboard|group|project +addBodyAttributes = (section = 'dashboard') -> - $('body').removeAttr 'data-page' - $('body').removeAttr 'data-project' + $body = $ 'body' - $('body').data 'page', "#{page}:show" - $('body').data 'project', 'gitlab-ce' + $body.removeAttr 'data-page' + $body.removeAttr 'data-project' + $body.removeAttr 'data-group' + + switch section + when 'dashboard' + $body.data 'page', 'root:index' + when 'group' + $body.data 'page', 'groups:show' + $body.data 'group', 'gitlab-org' + when 'project' + $body.data 'page', 'projects:show' + $body.data 'project', 'gitlab-ce' # Mock `gl` object in window for dashboard specific page. App code will need it. @@ -47,7 +62,27 @@ mockProjectOptions = -> projectName : projectName -assertLinks = (list, a1, a2, a3, a4) -> +mockGroupOptions = -> + + window.gl or= {} + window.gl.groupOptions = + 'gitlab-org' : + issuesPath : groupIssuesPath + mrPath : groupMRsPath + projectName : groupName + + +assertLinks = (list, issuesPath, mrsPath) -> + + issuesAssignedToMeLink = "#{issuesPath}/?assignee_id=#{userId}" + issuesIHaveCreatedLink = "#{issuesPath}/?author_id=#{userId}" + mrsAssignedToMeLink = "#{mrsPath}/?assignee_id=#{userId}" + mrsIHaveCreatedLink = "#{mrsPath}/?author_id=#{userId}" + + a1 = "a[href='#{issuesAssignedToMeLink}']" + a2 = "a[href='#{issuesIHaveCreatedLink}']" + a3 = "a[href='#{mrsAssignedToMeLink}']" + a4 = "a[href='#{mrsIHaveCreatedLink}']" expect(list.find(a1).length).toBe 1 expect(list.find(a1).text()).toBe ' Issues assigned to me ' @@ -62,7 +97,6 @@ assertLinks = (list, a1, a2, a3, a4) -> expect(list.find(a4).text()).toBe " Merge requests I've created " - describe 'Search autocomplete dropdown', -> fixture.preload 'search_autocomplete.html' @@ -77,53 +111,39 @@ describe 'Search autocomplete dropdown', -> addBodyAttributes() mockDashboardOptions() - - # Focus input to show dropdown list. widget.searchInput.focus() - w = widget.wrap.find '.dropdown-menu' - l = w.find 'ul' + list = widget.wrap.find('.dropdown-menu').find 'ul' + assertLinks list, dashboardIssuesPath, dashboardMRsPath - # # Expect dropdown and dropdown header - expect(w.find('.dropdown-header').text()).toBe 'Go to in Dashboard' - # Create links then assert link urls and inner texts - issuesAssignedToMeLink = "#{dashboardIssuesPath}/?assignee_id=#{userId}" - issuesIHaveCreatedLink = "#{dashboardIssuesPath}/?author_id=#{userId}" - mrsAssignedToMeLink = "#{dashboardMRsPath}/?assignee_id=#{userId}" - mrsIHaveCreatedLink = "#{dashboardMRsPath}/?author_id=#{userId}" + it 'should show Group specific dropdown menu', -> - a1 = "a[href='#{issuesAssignedToMeLink}']" - a2 = "a[href='#{issuesIHaveCreatedLink}']" - a3 = "a[href='#{mrsAssignedToMeLink}']" - a4 = "a[href='#{mrsIHaveCreatedLink}']" + addBodyAttributes 'group' + mockGroupOptions() + widget.searchInput.focus() - assertLinks l, a1, a2, a3, a4 + list = widget.wrap.find('.dropdown-menu').find 'ul' + assertLinks list, groupIssuesPath, groupMRsPath it 'should show Project specific dropdown menu', -> - addBodyAttributes 'projects' + addBodyAttributes 'project' mockProjectOptions() - - # Focus input to show dropdown list. widget.searchInput.focus() - w = widget.wrap.find '.dropdown-menu' - l = w.find 'ul' + list = widget.wrap.find('.dropdown-menu').find 'ul' + assertLinks list, projectIssuesPath, projectMRsPath - # Expect dropdown and dropdown header - expect(w.find('.dropdown-header').text()).toBe "Go to in #{projectName}" - # Create links then verify link urls and inner texts - issuesAssignedToMeLink = "#{projectIssuesPath}/?assignee_id=#{userId}" - issuesIHaveCreatedLink = "#{projectIssuesPath}/?author_id=#{userId}" - mrsAssignedToMeLink = "#{projectMRsPath}/?assignee_id=#{userId}" - mrsIHaveCreatedLink = "#{projectMRsPath}/?author_id=#{userId}" + it 'should not show category related menu if there is text in the input', -> - a1 = "a[href='#{issuesAssignedToMeLink}']" - a2 = "a[href='#{issuesIHaveCreatedLink}']" - a3 = "a[href='#{mrsAssignedToMeLink}']" - a4 = "a[href='#{mrsIHaveCreatedLink}']" + addBodyAttributes 'project' + mockProjectOptions() + widget.searchInput.val 'help' + widget.searchInput.focus() - assertLinks l, a1, a2, a3, a4 + list = widget.wrap.find('.dropdown-menu').find 'ul' + link = "a[href='#{projectIssuesPath}/?assignee_id=#{userId}']" + expect(list.find(link).length).toBe 0 From b63dc993534a567b7aba737db1565e3b56033ba2 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 13 Jun 2016 19:18:15 +0300 Subject: [PATCH 12/13] Defensive check for the group options. --- app/views/layouts/_search.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 5c6429d07b..4587cf5065 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -44,7 +44,7 @@ name: "#{@project.name}" }; - - if @group + - if @group and @group.path :javascript gl.groupOptions = gl.groupOptions || {}; gl.groupOptions["#{@group.path}"] = { From 6f8626de0609da6c789457153b2b19dc79db2c95 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 14 Jun 2016 00:07:18 +0300 Subject: [PATCH 13/13] Escape JavaScript in haml template. --- app/views/layouts/_search.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 4587cf5065..245b9c3b4d 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -38,19 +38,19 @@ :javascript gl.projectOptions = gl.projectOptions || {}; - gl.projectOptions["#{@project.path}"] = { + gl.projectOptions["#{j(@project.path)}"] = { issuesPath: "#{namespace_project_issues_path(@project.namespace, @project)}", mrPath: "#{namespace_project_merge_requests_path(@project.namespace, @project)}", - name: "#{@project.name}" + name: "#{j(@project.name)}" }; - if @group and @group.path :javascript gl.groupOptions = gl.groupOptions || {}; - gl.groupOptions["#{@group.path}"] = { - name: "#{@group.name}", - issuesPath: "#{issues_group_path(@group.path)}", - mrPath: "#{merge_requests_group_path(@group.path)}" + gl.groupOptions["#{j(@group.path)}"] = { + name: "#{j(@group.name)}", + issuesPath: "#{issues_group_path(j(@group.path))}", + mrPath: "#{merge_requests_group_path(j(@group.path))}" };