Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/favicon_admin.js19
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js50
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue5
-rw-r--r--app/serializers/status_entity.rb2
-rw-r--r--app/uploaders/favicon_uploader.rb37
-rw-r--r--app/views/admin/appearances/_form.html.haml7
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--lib/gitlab/favicon.rb33
-rw-r--r--spec/features/admin/admin_appearance_spec.rb27
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js36
-rw-r--r--spec/javascripts/lib/utils/mock_data.js5
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js13
-rw-r--r--spec/lib/gitlab/favicon_spec.rb30
-rw-r--r--spec/uploaders/favicon_uploader_spec.rb11
14 files changed, 169 insertions, 108 deletions
diff --git a/app/assets/javascripts/favicon_admin.js b/app/assets/javascripts/favicon_admin.js
new file mode 100644
index 00000000000..6b2dcf4502e
--- /dev/null
+++ b/app/assets/javascripts/favicon_admin.js
@@ -0,0 +1,19 @@
+import {createOverlayIcon} from '~/lib/utils/common_utils';
+
+export default class FaviconAdmin {
+ constructor() {
+ const faviconContainer = $('.js-favicons');
+ const faviconUrl = faviconContainer.data('favicon');
+ const overlayUrls = faviconContainer.data('status-overlays');
+
+ overlayUrls.forEach((statusOverlay) => {
+ createOverlayIcon(faviconUrl, statusOverlay).then((faviconWithOverlayUrl) => {
+ const image = $('<img />');
+ image.addClass('appearance-light-logo-preview');
+ image.attr('src', faviconWithOverlayUrl);
+
+ faviconContainer.append(image);
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 310b731c6d8..d55d0585031 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -384,6 +384,49 @@ export const backOff = (fn, timeout = 60000) => {
});
};
+export const createOverlayIcon = (iconPath, overlayPath) => {
+ const faviconImage = document.createElement('img');
+
+ return new Promise((resolve) => {
+ faviconImage.onload = () => {
+ const size = 32;
+
+ const canvas = document.createElement('canvas');
+ canvas.width = size;
+ canvas.height = size;
+
+ const context = canvas.getContext('2d');
+ context.clearRect(0, 0, size, size);
+ context.drawImage(
+ faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ );
+
+ const overlayImage = document.createElement('img');
+ overlayImage.onload = () => {
+ context.drawImage(
+ overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ );
+
+ const faviconWithOverlayUrl = canvas.toDataURL();
+
+ resolve(faviconWithOverlayUrl);
+ };
+ overlayImage.src = overlayPath;
+ };
+ faviconImage.src = iconPath;
+ });
+};
+
+export const setFaviconOverlay = (overlayPath) => {
+ const faviconEl = document.getElementById('favicon');
+
+ if (!faviconEl) { return null; }
+
+ const iconPath = faviconEl.getAttribute('data-original-href');
+
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+};
+
export const setFavicon = (faviconPath) => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
@@ -395,7 +438,7 @@ export const resetFavicon = () => {
const faviconEl = document.getElementById('favicon');
if (faviconEl) {
- const originalFavicon = faviconEl.getAttribute('data-default-href');
+ const originalFavicon = faviconEl.getAttribute('data-original-href');
faviconEl.setAttribute('href', originalFavicon);
}
};
@@ -404,10 +447,9 @@ export const setCiStatusFavicon = pageUrl =>
axios.get(pageUrl)
.then(({ data }) => {
if (data && data.favicon) {
- setFavicon(data.favicon);
- } else {
- resetFavicon();
+ return setFaviconOverlay(data.favicon);
}
+ return resetFavicon();
})
.catch(resetFavicon);
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index f69fe03fcb3..d6cba878b28 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -36,7 +36,7 @@ import {
notify,
SourceBranchRemovalStatus,
} from './dependencies';
-import { setFavicon } from '../lib/utils/common_utils';
+import { setFaviconOverlay } from '../lib/utils/common_utils';
export default {
el: '#js-vue-mr-widget',
@@ -159,8 +159,9 @@ export default {
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
- setFavicon(this.mr.ciStatusFaviconPath);
+ return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
+ return Promise.resolve();
},
fetchDeployments() {
return this.service.fetchDeployments()
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 2f3d4b80565..47df7f9dcf9 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -7,7 +7,7 @@ class StatusEntity < Grape::Entity
expose :details_path
expose :favicon do |status|
- Gitlab::Favicon.status(status.favicon)
+ Gitlab::Favicon.status_overlay(status.favicon)
end
expose :action, if: -> (status, _) { status.has_action? } do
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
index 7697f5fe885..d7be77477b2 100644
--- a/app/uploaders/favicon_uploader.rb
+++ b/app/uploaders/favicon_uploader.rb
@@ -1,35 +1,12 @@
class FaviconUploader < AttachmentUploader
include CarrierWave::MiniMagick
- STATUS_ICON_NAMES = [
- :favicon_status_canceled,
- :favicon_status_created,
- :favicon_status_failed,
- :favicon_status_manual,
- :favicon_status_not_found,
- :favicon_status_pending,
- :favicon_status_running,
- :favicon_status_skipped,
- :favicon_status_success,
- :favicon_status_warning
- ].freeze
-
version :favicon_main do
process resize_to_fill: [32, 32]
- process convert: 'ico'
+ process convert: 'png'
def full_filename(filename)
- filename_for_different_format(super(filename), 'ico')
- end
- end
-
- STATUS_ICON_NAMES.each do |status_name|
- version status_name, from_version: :favicon_main do
- process status_favicon: status_name
-
- def full_filename(filename)
- filename_for_different_format(super(filename), 'ico')
- end
+ filename_for_different_format(super(filename), 'png')
end
end
@@ -39,16 +16,6 @@ class FaviconUploader < AttachmentUploader
private
- def status_favicon(status_name)
- manipulate! do |img|
- overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/#{status_name}.png")
- overlay = MiniMagick::Image.open(overlay_path)
- img.composite(overlay) do |c|
- c.compose 'over'
- end
- end
- end
-
def filename_for_different_format(filename, format)
filename.chomp(File.extname(filename)) + ".#{format}"
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 1119c75637c..f77e22bcc45 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -62,13 +62,12 @@
= f.label :favicon, 'Favicon', class: 'control-label'
.col-sm-10
- if @appearance.favicon?
- = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
+ = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview js-main-favicon'
- if @appearance.favicon?
- = f.label :favicon, 'Generated status icons', class: 'control-label'
+ = f.label :favicon, 'Status icons preview', class: 'control-label'
.col-sm-10
- if @appearance.favicon?
- - FaviconUploader::STATUS_ICON_NAMES.each do |status_name|
- = image_tag @appearance.favicon.public_send(status_name).url, class: 'appearance-light-logo-preview'
+ .js-favicons{ data: { favicon: @appearance.favicon.favicon_main.url, status_overlays: Gitlab::Favicon.available_status_overlays } }
- if @appearance.persisted?
%br
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo"
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 024f80e9935..9253a0652da 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -25,7 +25,7 @@
%title= page_title(site_name)
%meta{ name: "description", content: page_description }
- = favicon_link_tag favicon, id: 'favicon', :'data-default-href': favicon
+ = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 51a25b408ee..e28d4c67661 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -9,18 +9,27 @@ module Gitlab
'favicon.ico'
end
- def status(status_name)
- if appearance_favicon.exists?
- custom_favicon_url(appearance_favicon.public_send("#{status_name}").url) # rubocop:disable GitlabSecurity/PublicSend
- else
- path = File.join(
- 'ci_favicons',
- Rails.env.development? ? 'dev' : '',
- Gitlab::Utils.to_boolean(ENV['CANARY']) ? 'canary' : '',
- "#{status_name}.ico"
- )
-
- ActionController::Base.helpers.image_path(path)
+ def status_overlay(status_name)
+ path = File.join(
+ 'ci_favicons',
+ 'overlays',
+ "#{status_name}.png"
+ )
+
+ ActionController::Base.helpers.image_path(path)
+ end
+
+ def available_status_overlays
+ available_status_names.map do |status_name|
+ status_overlay(status_name)
+ end
+ end
+
+ def available_status_names
+ @available_status_names ||= begin
+ Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', 'overlays', "*.png"))
+ .map { |file| File.basename(file, '.png') }
+ .sort
end
end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index ffffd14752e..0ac4f111c52 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -76,38 +76,19 @@ feature 'Admin Appearance' do
expect(page).not_to have_css(header_logo_selector)
end
- scenario 'Favicon' do
+ scenario 'Favicon', :js do
sign_in(create(:admin))
visit admin_appearances_path
attach_file(:appearance_favicon, logo_fixture)
click_button 'Save'
- expect(page).to have_css('//img[data-src$="/default_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_canceled_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_created_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_failed_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_manual_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_not_found_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_pending_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_running_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_skipped_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_success_dk.ico"]')
- expect(page).to have_css('//img[data-src$="/status_warning_dk.ico"]')
+ # 11 = 1 original + 10 overlay variations
+ expect(page).to have_css('.appearance-light-logo-preview', count: 11)
click_link 'Remove favicon'
- expect(page).not_to have_css('//img[data-src$="/default_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_canceled_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_created_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_failed_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_manual_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_not_found_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_pending_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_running_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]')
- expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]')
+ expect(page).not_to have_css('.appearance-light-logo-preview')
# allowed file types
attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg'))
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 64d13275a59..2d7cc3443cf 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -2,6 +2,7 @@
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
import MockAdapter from 'axios-mock-adapter';
+import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data';
describe('common_utils', () => {
describe('parseUrl', () => {
@@ -430,6 +431,35 @@ describe('common_utils', () => {
});
});
+ describe('createOverlayIcon', () => {
+ it('should return the favicon with the overlay', (done) => {
+ commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => {
+ expect(url).toEqual(faviconWithOverlayDataUrl);
+ done();
+ });
+ });
+ });
+
+ describe('setFaviconOverlay', () => {
+ beforeEach(() => {
+ const favicon = document.createElement('link');
+ favicon.setAttribute('id', 'favicon');
+ favicon.setAttribute('data-original-href', faviconDataUrl);
+ document.body.appendChild(favicon);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(document.getElementById('favicon'));
+ });
+
+ it('should set page favicon to provided favicon overlay', (done) => {
+ commonUtils.setFaviconOverlay(overlayDataUrl).then(() => {
+ expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
+ done();
+ });
+ });
+ });
+
describe('setCiStatusFavicon', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`;
let mock;
@@ -463,16 +493,14 @@ describe('common_utils', () => {
});
it('should set page favicon to CI status favicon based on provided status', (done) => {
- const FAVICON_PATH = '//icon_status_success';
-
mock.onGet(BUILD_URL).reply(200, {
- favicon: FAVICON_PATH,
+ favicon: overlayDataUrl,
});
commonUtils.setCiStatusFavicon(BUILD_URL)
.then(() => {
const favicon = document.getElementById('favicon');
- expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH);
+ expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
done();
})
.catch(done.fail);
diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js
new file mode 100644
index 00000000000..fd0d62b751f
--- /dev/null
+++ b/spec/javascripts/lib/utils/mock_data.js
@@ -0,0 +1,5 @@
+export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC';
+
+export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC';
+
+export const faviconWithOverlayDataUrl = 'data:image/png;base64,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';
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 30918428da2..6342ea00436 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -5,6 +5,7 @@ import notify from '~/lib/utils/notify';
import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mockData from './mock_data';
+import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data';
const returnPromise = data => new Promise((resolve) => {
resolve({
@@ -273,6 +274,7 @@ describe('mrWidgetOptions', () => {
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
+ favicon.setAttribute('data-original-href', faviconDataUrl);
document.body.appendChild(favicon);
faviconElement = document.getElementById('favicon');
@@ -282,10 +284,13 @@ describe('mrWidgetOptions', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should call setFavicon method', () => {
- vm.setFaviconHelper();
-
- expect(faviconElement.getAttribute('href')).toEqual(vm.mr.ciStatusFaviconPath);
+ it('should call setFavicon method', (done) => {
+ vm.mr.ciStatusFaviconPath = overlayDataUrl;
+ vm.setFaviconHelper().then(() => {
+ expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
+ done();
+ })
+ .catch(done.fail);
});
it('should not call setFavicon when there is no ciStatusFaviconPath', () => {
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 51b8fda81d1..22b9c631ed8 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -19,20 +19,34 @@ RSpec.describe Gitlab::Favicon, :request_store do
it 'uses the custom favicon if a favicon appearance is present' do
create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))
- expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.ico}
+ expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
end
end
- describe '.status' do
- subject { described_class.status('favicon_status_created') }
+ describe '.status_overlay' do
+ subject { described_class.status_overlay('favicon_status_created') }
- it 'defaults to the stock icon' do
- expect(subject).to eq '/assets/ci_favicons/favicon_status_created.ico'
+ it 'returns the overlay for the status' do
+ expect(subject).to eq '/assets/ci_favicons/overlays/favicon_status_created.png'
end
+ end
- it 'uses the custom favicon if a favicon appearance is present' do
- create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))
- expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/favicon_status_created_dk.ico})
+ describe '.available_status_names' do
+ subject { described_class.available_status_names }
+
+ it 'returns the available status names' do
+ expect(subject).to eq %w(
+ favicon_status_canceled
+ favicon_status_created
+ favicon_status_failed
+ favicon_status_manual
+ favicon_status_not_found
+ favicon_status_pending
+ favicon_status_running
+ favicon_status_skipped
+ favicon_status_success
+ favicon_status_warning
+ )
end
end
end
diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb
index b521670addb..db8a3207f4d 100644
--- a/spec/uploaders/favicon_uploader_spec.rb
+++ b/spec/uploaders/favicon_uploader_spec.rb
@@ -19,20 +19,11 @@ RSpec.describe FaviconUploader do
end
it 'has the correct format' do
- expect(uploader.favicon_main).to be_format('ico')
+ expect(uploader.favicon_main).to be_format('png')
end
it 'has the correct dimensions' do
expect(uploader.favicon_main).to have_dimensions(32, 32)
end
-
- it 'generates all the status icons' do
- # make sure that the following each statement actually loops
- expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10
-
- FaviconUploader::STATUS_ICON_NAMES.each do |status_name|
- expect(File.exist?(uploader.favicon_status_not_found.file.file)).to be true
- end
- end
end
end