diff options
39 files changed, 360 insertions, 98 deletions
diff --git a/app/assets/images/ci_favicons/icon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico Binary files differindex 5a19458f2a2..5a19458f2a2 100755 --- a/app/assets/images/ci_favicons/icon_status_canceled.ico +++ b/app/assets/images/ci_favicons/favicon_status_canceled.ico diff --git a/app/assets/images/ci_favicons/icon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico Binary files differindex 4dca9640cb3..4dca9640cb3 100755 --- a/app/assets/images/ci_favicons/icon_status_created.ico +++ b/app/assets/images/ci_favicons/favicon_status_created.ico diff --git a/app/assets/images/ci_favicons/icon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico Binary files differindex c961ff9a69b..c961ff9a69b 100755 --- a/app/assets/images/ci_favicons/icon_status_failed.ico +++ b/app/assets/images/ci_favicons/favicon_status_failed.ico diff --git a/app/assets/images/ci_favicons/icon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico Binary files differindex 5fbbc99ea7c..5fbbc99ea7c 100755 --- a/app/assets/images/ci_favicons/icon_status_manual.ico +++ b/app/assets/images/ci_favicons/favicon_status_manual.ico diff --git a/app/assets/images/ci_favicons/icon_status_not_found.ico b/app/assets/images/ci_favicons/favicon_status_not_found.ico Binary files differindex 21afa9c72e6..21afa9c72e6 100755 --- a/app/assets/images/ci_favicons/icon_status_not_found.ico +++ b/app/assets/images/ci_favicons/favicon_status_not_found.ico diff --git a/app/assets/images/ci_favicons/icon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico Binary files differindex 8be32dab85a..8be32dab85a 100755 --- a/app/assets/images/ci_favicons/icon_status_pending.ico +++ b/app/assets/images/ci_favicons/favicon_status_pending.ico diff --git a/app/assets/images/ci_favicons/icon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico Binary files differindex f328ff1a5ed..f328ff1a5ed 100755 --- a/app/assets/images/ci_favicons/icon_status_running.ico +++ b/app/assets/images/ci_favicons/favicon_status_running.ico diff --git a/app/assets/images/ci_favicons/icon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico Binary files differindex b4394e1b4af..b4394e1b4af 100755 --- a/app/assets/images/ci_favicons/icon_status_skipped.ico +++ b/app/assets/images/ci_favicons/favicon_status_skipped.ico diff --git a/app/assets/images/ci_favicons/icon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico Binary files differindex 4f436c95242..4f436c95242 100755 --- a/app/assets/images/ci_favicons/icon_status_success.ico +++ b/app/assets/images/ci_favicons/favicon_status_success.ico diff --git a/app/assets/images/ci_favicons/icon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico Binary files differindex 805cc20cdec..805cc20cdec 100755 --- a/app/assets/images/ci_favicons/icon_status_warning.ico +++ b/app/assets/images/ci_favicons/favicon_status_warning.ico diff --git a/app/assets/javascripts/ci_status_icons.js b/app/assets/javascripts/ci_status_icons.js new file mode 100644 index 00000000000..f16616873b2 --- /dev/null +++ b/app/assets/javascripts/ci_status_icons.js @@ -0,0 +1,34 @@ +import CANCELED_SVG from 'icons/_icon_status_canceled_borderless.svg'; +import CREATED_SVG from 'icons/_icon_status_created_borderless.svg'; +import FAILED_SVG from 'icons/_icon_status_failed_borderless.svg'; +import MANUAL_SVG from 'icons/_icon_status_manual_borderless.svg'; +import PENDING_SVG from 'icons/_icon_status_pending_borderless.svg'; +import RUNNING_SVG from 'icons/_icon_status_running_borderless.svg'; +import SKIPPED_SVG from 'icons/_icon_status_skipped_borderless.svg'; +import SUCCESS_SVG from 'icons/_icon_status_success_borderless.svg'; +import WARNING_SVG from 'icons/_icon_status_warning_borderless.svg'; + +const StatusIconEntityMap = { + icon_status_canceled: CANCELED_SVG, + icon_status_created: CREATED_SVG, + icon_status_failed: FAILED_SVG, + icon_status_manual: MANUAL_SVG, + icon_status_pending: PENDING_SVG, + icon_status_running: RUNNING_SVG, + icon_status_skipped: SKIPPED_SVG, + icon_status_success: SUCCESS_SVG, + icon_status_warning: WARNING_SVG, +}; + +export { + CANCELED_SVG, + CREATED_SVG, + FAILED_SVG, + MANUAL_SVG, + PENDING_SVG, + RUNNING_SVG, + SKIPPED_SVG, + SUCCESS_SVG, + WARNING_SVG, + StatusIconEntityMap as default, +}; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 33c900e264b..01c4b9821d3 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -368,9 +368,9 @@ }); }; - w.gl.utils.setFavicon = (iconName) => { - if (faviconEl && iconName) { - faviconEl.setAttribute('href', `/assets/${iconName}.ico`); + w.gl.utils.setFavicon = (faviconPath) => { + if (faviconEl && faviconPath) { + faviconEl.setAttribute('href', faviconPath); } }; @@ -385,8 +385,8 @@ url: pageUrl, dataType: 'json', success: function(data) { - if (data && data.icon) { - gl.utils.setFavicon(`ci_favicons/${data.icon}`); + if (data && data.favicon) { + gl.utils.setFavicon(data.favicon); } else { gl.utils.resetFavicon(); } diff --git a/app/assets/javascripts/vue_pipelines_index/components/stage.js b/app/assets/javascripts/vue_pipelines_index/components/stage.js index a2c29002707..b8cc3630611 100644 --- a/app/assets/javascripts/vue_pipelines_index/components/stage.js +++ b/app/assets/javascripts/vue_pipelines_index/components/stage.js @@ -1,32 +1,11 @@ /* global Flash */ -import canceledSvg from 'icons/_icon_status_canceled_borderless.svg'; -import createdSvg from 'icons/_icon_status_created_borderless.svg'; -import failedSvg from 'icons/_icon_status_failed_borderless.svg'; -import manualSvg from 'icons/_icon_status_manual_borderless.svg'; -import pendingSvg from 'icons/_icon_status_pending_borderless.svg'; -import runningSvg from 'icons/_icon_status_running_borderless.svg'; -import skippedSvg from 'icons/_icon_status_skipped_borderless.svg'; -import successSvg from 'icons/_icon_status_success_borderless.svg'; -import warningSvg from 'icons/_icon_status_warning_borderless.svg'; +import StatusIconEntityMap from '../../ci_status_icons'; export default { data() { - const svgsDictionary = { - icon_status_canceled: canceledSvg, - icon_status_created: createdSvg, - icon_status_failed: failedSvg, - icon_status_manual: manualSvg, - icon_status_pending: pendingSvg, - icon_status_running: runningSvg, - icon_status_skipped: skippedSvg, - icon_status_success: successSvg, - icon_status_warning: warningSvg, - }; - return { builds: '', spinner: '<span class="fa fa-spinner fa-spin"></span>', - svg: svgsDictionary[this.stage.status.icon], }; }, @@ -89,6 +68,9 @@ export default { triggerButtonClass() { return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`; }, + svgHTML() { + return StatusIconEntityMap[this.stage.status.icon]; + }, }, template: ` <div> @@ -100,7 +82,7 @@ export default { data-toggle="dropdown" type="button" :aria-label="stage.title"> - <span v-html="svg" aria-hidden="true"></span> + <span v-html="svgHTML" aria-hidden="true"></span> <i class="fa fa-caret-down" aria-hidden="true"></i> </button> <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"> diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8c6dd392865..fe084eb9397 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -289,8 +289,12 @@ table.u2f-registrations { margin: 0 auto; .bordered-box { - border: 1px solid $border-color; + border: 1px solid $blue-300; border-radius: $border-radius-default; + background-color: $blue-25; + position: relative; + display: flex; + justify-content: center; } .landing { @@ -298,28 +302,59 @@ table.u2f-registrations { margin-bottom: $gl-padding; .close { - margin-right: 20px; - } + position: absolute; + right: 20px; + opacity: 1; + + .dismiss-icon { + float: right; + cursor: pointer; + color: $blue-300; + } - .dismiss-icon { - float: right; - cursor: pointer; - color: $cycle-analytics-dismiss-icon-color; + &:hover { + background-color: transparent; + border: 0; + + .dismiss-icon { + color: $blue-400; + } + } } .svg-container { - text-align: center; + margin-right: 30px; + display: inline-block; svg { - width: 136px; - height: 136px; + height: 110px; + vertical-align: top; } } + + .user-callout-copy { + display: inline-block; + vertical-align: top; + } } @media(max-width: $screen-xs-max) { - .inner-content { - padding-left: 30px; + text-align: center; + + .bordered-box { + display: block; + } + + .landing { + .svg-container, + .user-callout-copy { + margin: 0; + display: block; + + svg { + height: 75px; + } + } } } } diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index fa782c6fbb7..f2dfb87dbda 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -22,7 +22,7 @@ class ChatNotificationService < Service end def can_test? - valid? + super && valid? end def self.supported_events diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index dfd9d1584a1..944472f3e51 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -1,8 +1,12 @@ class StatusEntity < Grape::Entity include RequestAwareEntity - expose :icon, :favicon, :text, :label, :group + expose :icon, :text, :label, :group expose :has_details?, as: :has_details expose :details_path + + expose :favicon do |status| + ActionController::Base.helpers.image_path(File.join('ci_favicons', "#{status.favicon}.ico")) + end end diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index 8409b592b72..ff188102b62 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -7,16 +7,13 @@ module Search end def execute - group = Group.find_by(id: params[:group_id]) if params[:group_id].present? - projects = ProjectsFinder.new(current_user: current_user).execute - - if group - projects = projects.inside_path(group.full_path) - end - Gitlab::SearchResults.new(current_user, projects, params[:search]) end + def projects + @projects ||= ProjectsFinder.new(current_user: current_user).execute + end + def scope @scope ||= begin allowed_scopes = %w[issues merge_requests milestones] diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb new file mode 100644 index 00000000000..29478e3251f --- /dev/null +++ b/app/services/search/group_service.rb @@ -0,0 +1,18 @@ +module Search + class GroupService < Search::GlobalService + attr_accessor :group + + def initialize(user, group, params) + super(user, params) + + @group = group + end + + def projects + return Project.none unless group + return @projects if defined? @projects + + @projects = super.inside_path(group.full_path) + end + end +end diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 8d46a8dab3e..22736c71725 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -54,6 +54,8 @@ class SearchService Search::ProjectService.new(project, current_user, params) elsif show_snippets? Search::SnippetService.new(current_user, params) + elsif group + Search::GroupService.new(current_user, group, params) else Search::GlobalService.new(current_user, params) end diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index e34cddeb3e2..37429c7cfc0 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -11,13 +11,13 @@ Project - if project_nav_tab? :files - = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases graphs network)) do + = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do = link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do %span Repository - if project_nav_tab? :container_registry - = nav_link(controller: %w(container_registry)) do + = nav_link(controller: %w[projects/registry/repositories]) do = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do %span Registry diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 03069804c86..da79ca2ee75 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -46,7 +46,7 @@ -# This tab is always loaded via AJAX - if @pipelines.any? #pipelines.pipelines.tab-pane - = render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)) + = render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)), disable_initialization: true .mr-loading-status = spinner diff --git a/app/views/projects/merge_requests/show/_pipelines.html.haml b/app/views/projects/merge_requests/show/_pipelines.html.haml index de4aa255bbd..2f1dbe87619 100644 --- a/app/views/projects/merge_requests/show/_pipelines.html.haml +++ b/app/views/projects/merge_requests/show/_pipelines.html.haml @@ -1,3 +1,4 @@ - endpoint_path = local_assigns[:endpoint] || pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json) +- disable_initialization = local_assigns.fetch(:disable_initialization, false) -= render 'projects/commit/pipelines_list', endpoint: endpoint_path += render 'projects/commit/pipelines_list', endpoint: endpoint_path, disable_initialization: disable_initialization diff --git a/app/views/shared/_user_callout.html.haml b/app/views/shared/_user_callout.html.haml index 8f1293adcb1..8308baa7829 100644 --- a/app/views/shared/_user_callout.html.haml +++ b/app/views/shared/_user_callout.html.haml @@ -3,12 +3,11 @@ %button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss customize experience box' } = icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true') - .row - .col-sm-3.col-xs-12.svg-container - = custom_icon('icon_customization') - .col-sm-8.col-xs-12.inner-content - %h4 - Customize your experience - %p - Change syntax themes, default project pages, and more in preferences. - = link_to 'Check it out', profile_preferences_path, class: 'btn btn-default js-close-callout' + .svg-container + = custom_icon('icon_customization') + .user-callout-copy + %h4 + Customize your experience + %p + Change syntax themes, default project pages, and more in preferences. + = link_to 'Check it out', profile_preferences_path, class: 'btn btn-primary js-close-callout' diff --git a/changelogs/unreleased/29595-customize-experience-callout.yml b/changelogs/unreleased/29595-customize-experience-callout.yml new file mode 100644 index 00000000000..ec8393142c6 --- /dev/null +++ b/changelogs/unreleased/29595-customize-experience-callout.yml @@ -0,0 +1,4 @@ +--- +title: 29595 Update callout design +merge_request: +author: diff --git a/changelogs/unreleased/31009-disable-test-settings-on-services-when-repository-is-empty.yml b/changelogs/unreleased/31009-disable-test-settings-on-services-when-repository-is-empty.yml new file mode 100644 index 00000000000..6e43a032f20 --- /dev/null +++ b/changelogs/unreleased/31009-disable-test-settings-on-services-when-repository-is-empty.yml @@ -0,0 +1,4 @@ +--- +title: Disable test settings on chat notification services when repository is empty +merge_request: 10759 +author: diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index bcdf1b1faa8..c69676a1dac 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -15,7 +15,7 @@ module Gitlab end unless URI(address).scheme.in?(%w(tcp unix)) - raise "Unsupported Gitaly address: #{address.inspect}" + raise "Unsupported Gitaly address: #{address.inspect} does not use URL scheme 'tcp' or 'unix'" end @addresses[name] = address diff --git a/spec/controllers/projects/builds_controller_spec.rb b/spec/controllers/projects/builds_controller_spec.rb index 13208d21918..faf3770f5e9 100644 --- a/spec/controllers/projects/builds_controller_spec.rb +++ b/spec/controllers/projects/builds_controller_spec.rb @@ -60,7 +60,7 @@ describe Projects::BuildsController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to eq status.favicon + expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico" end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 1739d40ab88..cc393bd24f2 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1208,7 +1208,7 @@ describe Projects::MergeRequestsController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to eq status.favicon + expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico" end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index d8f9bfd0d37..d9192177a06 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -86,7 +86,7 @@ describe Projects::PipelinesController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to eq status.favicon + expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico" end end end diff --git a/spec/javascripts/ci_status_icon_spec.js b/spec/javascripts/ci_status_icon_spec.js new file mode 100644 index 00000000000..c83416c15ef --- /dev/null +++ b/spec/javascripts/ci_status_icon_spec.js @@ -0,0 +1,44 @@ +import * as icons from '~/ci_status_icons'; + +describe('CI status icons', () => { + const statuses = [ + 'canceled', + 'created', + 'failed', + 'manual', + 'pending', + 'running', + 'skipped', + 'success', + 'warning', + ]; + + statuses.forEach((status) => { + it(`should export a ${status} svg`, () => { + const key = `${status.toUpperCase()}_SVG`; + + expect(Object.hasOwnProperty.call(icons, key)).toBe(true); + expect(icons[key]).toMatch(/^<svg/); + }); + }); + + describe('default export map', () => { + const entityIconNames = [ + 'icon_status_canceled', + 'icon_status_created', + 'icon_status_failed', + 'icon_status_manual', + 'icon_status_pending', + 'icon_status_running', + 'icon_status_skipped', + 'icon_status_success', + 'icon_status_warning', + ]; + + entityIconNames.forEach((iconName) => { + it(`should have a '${iconName}' key`, () => { + expect(Object.hasOwnProperty.call(icons.default, iconName)).toBe(true); + }); + }); + }); +}); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 03f3c206f44..56aabc16382 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -313,7 +313,7 @@ require('~/lib/utils/common_utils'); describe('gl.utils.setFavicon', () => { it('should set page favicon to provided favicon', () => { - const faviconName = 'custom_favicon'; + const faviconPath = '//custom_favicon'; const fakeLink = { setAttribute() {}, }; @@ -321,9 +321,9 @@ require('~/lib/utils/common_utils'); spyOn(window.document, 'getElementById').and.callFake(() => fakeLink); spyOn(fakeLink, 'setAttribute').and.callFake((attr, val) => { expect(attr).toEqual('href'); - expect(val.indexOf('/assets/custom_favicon.ico') > -1).toBe(true); + expect(val.indexOf(faviconPath) > -1).toBe(true); }); - gl.utils.setFavicon(faviconName); + gl.utils.setFavicon(faviconPath); }); }); @@ -345,13 +345,12 @@ require('~/lib/utils/common_utils'); describe('gl.utils.setCiStatusFavicon', () => { it('should set page favicon to CI status favicon based on provided status', () => { const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1/status.json`; - const FAVICON_PATH = 'ci_favicons/'; - const FAVICON = 'icon_status_success'; + const FAVICON_PATH = '//icon_status_success'; const spySetFavicon = spyOn(gl.utils, 'setFavicon').and.stub(); const spyResetFavicon = spyOn(gl.utils, 'resetFavicon').and.stub(); spyOn($, 'ajax').and.callFake(function (options) { - options.success({ icon: FAVICON }); - expect(spySetFavicon).toHaveBeenCalledWith(FAVICON_PATH + FAVICON); + options.success({ favicon: FAVICON_PATH }); + expect(spySetFavicon).toHaveBeenCalledWith(FAVICON_PATH); options.success(); expect(spyResetFavicon).toHaveBeenCalled(); options.error(); diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js index c0375ebc61c..28d0c7dcd99 100644 --- a/spec/javascripts/user_callout_spec.js +++ b/spec/javascripts/user_callout_spec.js @@ -14,7 +14,6 @@ describe('UserCallout', function () { this.userCallout = new UserCallout(); this.closeButton = $('.js-close-callout.close'); this.userCalloutBtn = $('.js-close-callout:not(.close)'); - this.userCalloutContainer = $('.user-callout'); }); it('hides when user clicks on the dismiss-icon', (done) => { diff --git a/spec/javascripts/vue_pipelines_index/stage_spec.js b/spec/javascripts/vue_pipelines_index/stage_spec.js new file mode 100644 index 00000000000..542661df2b0 --- /dev/null +++ b/spec/javascripts/vue_pipelines_index/stage_spec.js @@ -0,0 +1,66 @@ +import Vue from 'vue'; +import { SUCCESS_SVG } from '~/ci_status_icons'; +import Stage from '~/vue_pipelines_index/components/stage'; + +function minify(string) { + return string.replace(/\s/g, ''); +} + +describe('Pipelines Stage', () => { + describe('data', () => { + let stageReturnValue; + + beforeEach(() => { + stageReturnValue = Stage.data(); + }); + + it('should return object with .builds and .spinner', () => { + expect(stageReturnValue).toEqual({ + builds: '', + spinner: '<span class="fa fa-spinner fa-spin"></span>', + }); + }); + }); + + describe('computed', () => { + describe('svgHTML', function () { + let stage; + let svgHTML; + + beforeEach(() => { + stage = { stage: { status: { icon: 'icon_status_success' } } }; + + svgHTML = Stage.computed.svgHTML.call(stage); + }); + + it("should return the correct icon for the stage's status", () => { + expect(svgHTML).toBe(SUCCESS_SVG); + }); + }); + }); + + describe('when mounted', () => { + let StageComponent; + let renderedComponent; + let stage; + + beforeEach(() => { + stage = { status: { icon: 'icon_status_success' } }; + + StageComponent = Vue.extend(Stage); + + renderedComponent = new StageComponent({ + propsData: { + stage, + }, + }).$mount(); + }); + + it('should render the correct status svg', () => { + const minifiedComponent = minify(renderedComponent.$el.outerHTML); + const expectedSVG = minify(SUCCESS_SVG); + + expect(minifiedComponent).toContain(expectedSVG); + }); + }); +}); diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb index c98e7ee14fd..592c90cda36 100644 --- a/spec/models/project_services/chat_notification_service_spec.rb +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -1,11 +1,29 @@ require 'spec_helper' describe ChatNotificationService, models: true do - describe "Associations" do + describe 'Associations' do before do allow(subject).to receive(:activated?).and_return(true) end it { is_expected.to validate_presence_of :webhook } end + + describe '#can_test?' do + context 'with empty repository' do + it 'returns false' do + subject.project = create(:empty_project, :empty_repo) + + expect(subject.can_test?).to be false + end + end + + context 'with repository' do + it 'returns true' do + subject.project = create(:project) + + expect(subject.can_test?).to be true + end + end + end end diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb index 3cc791bca50..7f1abecfafe 100644 --- a/spec/serializers/build_serializer_spec.rb +++ b/spec/serializers/build_serializer_spec.rb @@ -38,7 +38,7 @@ describe BuildSerializer do expect(subject[:text]).to eq(status.text) expect(subject[:label]).to eq(status.label) expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to eq(status.favicon) + expect(subject[:favicon]).to eq("/assets/ci_favicons/#{status.favicon}.ico") end end end diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index f6249ab4664..ecde45a6d44 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -144,7 +144,7 @@ describe PipelineSerializer do expect(subject[:text]).to eq(status.text) expect(subject[:label]).to eq(status.label) expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to eq(status.favicon) + expect(subject[:favicon]).to eq("/assets/ci_favicons/#{status.favicon}.ico") end end end diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb index 2531607acad..cbf4f56213d 100644 --- a/spec/services/search/global_service_spec.rb +++ b/spec/services/search/global_service_spec.rb @@ -40,27 +40,6 @@ describe Search::GlobalService, services: true do expect(results.objects('projects')).to match_array [found_project] end - - context 'nested group' do - let!(:nested_group) { create(:group, :nested) } - let!(:project) { create(:empty_project, namespace: nested_group) } - - before do - project.add_master(user) - end - - it 'returns result from nested group' do - results = Search::GlobalService.new(user, search: project.path).execute - - expect(results.objects('projects')).to match_array [project] - end - - it 'returns result from descendants when search inside group' do - results = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent).execute - - expect(results.objects('projects')).to match_array [project] - end - end end end end diff --git a/spec/services/search/group_service_spec.rb b/spec/services/search/group_service_spec.rb new file mode 100644 index 00000000000..38f264f6e7b --- /dev/null +++ b/spec/services/search/group_service_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Search::GroupService, services: true do + shared_examples_for 'group search' do + context 'finding projects by name' do + let(:user) { create(:user) } + let(:term) { "Project Name" } + let(:nested_group) { create(:group, :nested) } + + # These projects shouldn't be found + let!(:outside_project) { create(:empty_project, :public, name: "Outside #{term}") } + let!(:private_project) { create(:empty_project, :private, namespace: nested_group, name: "Private #{term}" )} + let!(:other_project) { create(:empty_project, :public, namespace: nested_group, name: term.reverse) } + + # These projects should be found + let!(:project1) { create(:empty_project, :internal, namespace: nested_group, name: "Inner #{term} 1") } + let!(:project2) { create(:empty_project, :internal, namespace: nested_group, name: "Inner #{term} 2") } + let!(:project3) { create(:empty_project, :internal, namespace: nested_group.parent, name: "Outer #{term}") } + + let(:results) { Search::GroupService.new(user, search_group, search: term).execute } + subject { results.objects('projects') } + + context 'in parent group' do + let(:search_group) { nested_group.parent } + + it { is_expected.to match_array([project1, project2, project3]) } + end + + context 'in subgroup' do + let(:search_group) { nested_group } + + it { is_expected.to match_array([project1, project2]) } + end + end + end + + describe 'basic search' do + include_examples 'group search' + end +end diff --git a/spec/views/layouts/nav/_project.html.haml_spec.rb b/spec/views/layouts/nav/_project.html.haml_spec.rb new file mode 100644 index 00000000000..fd1637ca91b --- /dev/null +++ b/spec/views/layouts/nav/_project.html.haml_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'layouts/nav/_project' do + describe 'container registry tab' do + before do + stub_container_registry_config(enabled: true) + + assign(:project, create(:project)) + allow(view).to receive(:current_ref).and_return('master') + + allow(view).to receive(:can?).and_return(true) + allow(controller).to receive(:controller_name) + .and_return('repositories') + allow(controller).to receive(:controller_path) + .and_return('projects/registry/repositories') + end + + it 'has both Registry and Repository tabs' do + render + + expect(rendered).to have_text 'Repository' + expect(rendered).to have_text 'Registry' + end + + it 'highlights only one tab' do + render + + expect(rendered).to have_css('.active', count: 1) + end + + it 'highlights container registry tab only' do + render + + expect(rendered).to have_css('.active', text: 'Registry') + end + end +end |