summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-25 15:08:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-25 15:08:50 +0000
commite06d0e779673d745972863302858105aad9032e5 (patch)
tree0ff35b27a949a164f586613004b4abfe33e7d20e /spec
parentf7dae0cdcb70ecb71c1d65f099e9d96b27a4548c (diff)
downloadgitlab-ce-e06d0e779673d745972863302858105aad9032e5.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/boards/sidebar_spec.rb10
-rw-r--r--spec/features/container_registry_spec.rb75
-rw-r--r--spec/features/groups/container_registry_spec.rb93
-rw-r--r--spec/features/issuables/issuable_list_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/labels_hierarchy_spec.rb6
-rw-r--r--spec/features/projects/container_registry_spec.rb161
-rw-r--r--spec/frontend/lib/utils/unit_format/formatter_factory_spec.js200
-rw-r--r--spec/frontend/lib/utils/unit_format/index_spec.js117
-rw-r--r--spec/helpers/labels_helper_spec.rb12
-rw-r--r--spec/helpers/markup_helper_spec.rb4
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb26
-rw-r--r--spec/services/resource_events/change_milestone_service_spec.rb62
-rw-r--r--spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb46
14 files changed, 656 insertions, 166 deletions
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index c7edb574f19..e54c11f657d 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -277,7 +277,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page.within('.value') do
- expect(page).to have_selector('.badge', count: 2)
+ expect(page).to have_selector('.gl-label-text', count: 2)
expect(page).to have_content(development.title)
expect(page).to have_content(stretch.title)
end
@@ -299,7 +299,7 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.badge', count: 3)
+ expect(page).to have_selector('.gl-label-text', count: 3)
expect(page).to have_content(bug.title)
end
end
@@ -328,7 +328,7 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.badge', count: 4)
+ expect(page).to have_selector('.gl-label-text', count: 4)
expect(page).to have_content(bug.title)
expect(page).to have_content(regression.title)
end
@@ -357,13 +357,13 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.badge', count: 1)
+ expect(page).to have_selector('.gl-label-text', count: 1)
expect(page).not_to have_content(stretch.title)
end
end
# 'Development' label does not show since the card is in a 'Development' list label
- expect(card).to have_selector('.badge', count: 0)
+ expect(card).to have_selector('.gl-label-text', count: 0)
expect(card).not_to have_content(stretch.title)
end
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
deleted file mode 100644
index 881cad1864b..00000000000
--- a/spec/features/container_registry_spec.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe 'Container Registry', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- let(:container_repository) do
- create(:container_repository, name: 'my/image')
- end
-
- before do
- sign_in(user)
- project.add_developer(user)
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any, tags: [])
- stub_feature_flags(vue_container_registry_explorer: false)
- end
-
- it 'has a page title set' do
- visit_container_registry
- expect(page).to have_title(_('Container Registry'))
- end
-
- context 'when there are no image repositories' do
- it 'user visits container registry main page' do
- visit_container_registry
-
- expect(page).to have_content 'no container images'
- end
- end
-
- context 'when there are image repositories' do
- before do
- stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true)
- project.container_repositories << container_repository
- end
-
- it 'user wants to see multi-level container repository' do
- visit_container_registry
-
- expect(page).to have_content('my/image')
- end
-
- it 'user removes entire container repository', :sidekiq_might_not_need_inline do
- visit_container_registry
-
- expect_any_instance_of(ContainerRepository).to receive(:delete_tags!).and_return(true)
-
- click_on(class: 'js-remove-repo')
- expect(find('.modal .modal-title')).to have_content 'Remove repository'
- find('.modal .modal-footer .btn-danger').click
- end
-
- it 'user removes a specific tag from container repository' do
- visit_container_registry
-
- find('.js-toggle-repo').click
- wait_for_requests
-
- service = double('service')
- expect(service).to receive(:execute).with(container_repository) { { status: :success } }
- expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service }
-
- click_on(class: 'js-delete-registry-row', visible: false)
- expect(find('.modal .modal-title')).to have_content 'Remove tag'
- find('.modal .modal-footer .btn-danger').click
- end
- end
-
- def visit_container_registry
- visit project_container_registry_index_path(project)
- end
-end
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
new file mode 100644
index 00000000000..7e3c1728f3c
--- /dev/null
+++ b/spec/features/groups/container_registry_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Container Registry', :js do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
+ let(:container_repository) do
+ create(:container_repository, name: 'my/image')
+ end
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: [])
+ end
+
+ it 'has a page title set' do
+ visit_container_registry
+
+ expect(page).to have_title _('Container Registry')
+ end
+
+ context 'when there are no image repositories' do
+ it 'list page has no container title' do
+ visit_container_registry
+
+ expect(page).to have_content _('There are no container images available in this group')
+ end
+ end
+
+ context 'when there are image repositories' do
+ before do
+ stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true)
+ project.container_repositories << container_repository
+ end
+
+ it 'list page has a list of images' do
+ visit_container_registry
+
+ expect(page).to have_content 'my/image'
+ end
+
+ it 'image repository delete is disabled' do
+ visit_container_registry
+
+ delete_btn = find('[title="Remove repository"]')
+ expect(delete_btn).to be_disabled
+ end
+
+ it 'navigates to repo details' do
+ visit_container_registry_details('my/image')
+
+ expect(page).to have_content 'latest'
+ end
+
+ describe 'image repo details' do
+ before do
+ visit_container_registry_details 'my/image'
+ end
+
+ it 'shows the details breadcrumb' do
+ expect(find('.breadcrumbs')).to have_link 'my/image'
+ end
+
+ it 'shows the image title' do
+ expect(page).to have_content 'my/image tags'
+ end
+
+ it 'user removes a specific tag from container repository' do
+ service = double('service')
+ expect(service).to receive(:execute).with(container_repository) { { status: :success } }
+ expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service }
+
+ click_on(class: 'js-delete-registry')
+ expect(find('.modal .modal-title')).to have_content _('Remove tag')
+ find('.modal .modal-footer .btn-danger').click
+ end
+ end
+ end
+
+ def visit_container_registry
+ visit group_container_registries_path(group)
+ end
+
+ def visit_container_registry_details(name)
+ visit_container_registry
+ click_link(name)
+ end
+end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index bcc05d313ad..7014a51ccdc 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -41,10 +41,10 @@ describe 'issuable list' do
visit_issuable_list(issuable_type)
- expect(all('.label-link')[0].text).to have_content('B')
- expect(all('.label-link')[1].text).to have_content('X')
- expect(all('.label-link')[2].text).to have_content('a')
- expect(all('.label-link')[3].text).to have_content('z')
+ expect(all('.gl-label-text')[0].text).to have_content('B')
+ expect(all('.gl-label-text')[1].text).to have_content('X')
+ expect(all('.gl-label-text')[2].text).to have_content('a')
+ expect(all('.gl-label-text')[3].text).to have_content('z')
end
end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index ee5773f1484..a518831ea2b 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -332,7 +332,7 @@ describe 'Filter issues', :js do
context 'issue label clicked' do
it 'filters and displays in search bar' do
- find('.issues-list .issue .issuable-main-info .issuable-info a .badge', text: multiple_words_label.title).click
+ find('.issues-list .issue .issuable-main-info .issuable-info a .gl-label-text', text: multiple_words_label.title).click
expect_issues_list_count(1)
expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index c1a2e22a0c2..c66d858a019 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -161,9 +161,9 @@ describe 'Labels Hierarchy', :js do
find('.btn-success').click
expect(page.find('.issue-details h2.title')).to have_content('new created issue')
- expect(page).to have_selector('span.badge', text: grandparent_group_label.title)
- expect(page).to have_selector('span.badge', text: parent_group_label.title)
- expect(page).to have_selector('span.badge', text: project_label_1.title)
+ expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title)
+ expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title)
+ expect(page).to have_selector('span.gl-label-text', text: project_label_1.title)
end
end
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
new file mode 100644
index 00000000000..02b2d03a880
--- /dev/null
+++ b/spec/features/projects/container_registry_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Container Registry', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ let(:container_repository) do
+ create(:container_repository, name: 'my/image')
+ end
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: [])
+ end
+
+ describe 'Registry explorer is off' do
+ before do
+ stub_feature_flags(vue_container_registry_explorer: false)
+ end
+
+ it 'has a page title set' do
+ visit_container_registry
+
+ expect(page).to have_title _('Container Registry')
+ end
+
+ context 'when there are no image repositories' do
+ it 'user visits container registry main page' do
+ visit_container_registry
+
+ expect(page).to have_content _('no container images')
+ end
+ end
+
+ context 'when there are image repositories' do
+ before do
+ stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true)
+ project.container_repositories << container_repository
+ end
+
+ it 'user wants to see multi-level container repository' do
+ visit_container_registry
+
+ expect(page).to have_content 'my/image'
+ end
+
+ it 'user removes entire container repository', :sidekiq_might_not_need_inline do
+ visit_container_registry
+
+ expect_any_instance_of(ContainerRepository).to receive(:delete_tags!).and_return(true)
+
+ click_on(class: 'js-remove-repo')
+ expect(find('.modal .modal-title')).to have_content _('Remove repository')
+ find('.modal .modal-footer .btn-danger').click
+ end
+
+ it 'user removes a specific tag from container repository' do
+ visit_container_registry
+
+ find('.js-toggle-repo').click
+ wait_for_requests
+
+ service = double('service')
+ expect(service).to receive(:execute).with(container_repository) { { status: :success } }
+ expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service }
+
+ click_on(class: 'js-delete-registry-row', visible: false)
+ expect(find('.modal .modal-title')).to have_content _('Remove tag')
+ find('.modal .modal-footer .btn-danger').click
+ end
+ end
+ end
+
+ describe 'Registry explorer is on' do
+ it 'has a page title set' do
+ visit_container_registry
+
+ expect(page).to have_title _('Container Registry')
+ end
+
+ context 'when there are no image repositories' do
+ it 'list page has no container title' do
+ visit_container_registry
+
+ expect(page).to have_content _('There are no container images stored for this project')
+ end
+
+ it 'list page has quickstart' do
+ visit_container_registry
+
+ expect(page).to have_content _('Quick Start')
+ end
+ end
+
+ context 'when there are image repositories' do
+ before do
+ stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true)
+ project.container_repositories << container_repository
+ end
+
+ it 'list page has a list of images' do
+ visit_container_registry
+
+ expect(page).to have_content 'my/image'
+ end
+
+ it 'user removes entire container repository', :sidekiq_might_not_need_inline do
+ visit_container_registry
+
+ expect_any_instance_of(ContainerRepository).to receive(:delete_tags!).and_return(true)
+
+ find('[title="Remove repository"]').click
+ expect(find('.modal .modal-title')).to have_content _('Remove repository')
+ find('.modal .modal-footer .btn-danger').click
+ end
+
+ it 'navigates to repo details' do
+ visit_container_registry_details('my/image')
+
+ expect(page).to have_content 'latest'
+ end
+
+ describe 'image repo details' do
+ before do
+ visit_container_registry_details 'my/image'
+ end
+
+ it 'shows the details breadcrumb' do
+ expect(find('.breadcrumbs')).to have_link 'my/image'
+ end
+
+ it 'shows the image title' do
+ expect(page).to have_content 'my/image tags'
+ end
+
+ it 'user removes a specific tag from container repository' do
+ service = double('service')
+ expect(service).to receive(:execute).with(container_repository) { { status: :success } }
+ expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service }
+
+ click_on(class: 'js-delete-registry')
+ expect(find('.modal .modal-title')).to have_content _('Remove tag')
+ find('.modal .modal-footer .btn-danger').click
+ end
+ end
+ end
+ end
+
+ def visit_container_registry
+ visit project_container_registry_index_path(project)
+ end
+
+ def visit_container_registry_details(name)
+ visit_container_registry
+ click_link(name)
+ end
+end
diff --git a/spec/frontend/lib/utils/unit_format/formatter_factory_spec.js b/spec/frontend/lib/utils/unit_format/formatter_factory_spec.js
new file mode 100644
index 00000000000..071ecde6a6d
--- /dev/null
+++ b/spec/frontend/lib/utils/unit_format/formatter_factory_spec.js
@@ -0,0 +1,200 @@
+import {
+ numberFormatter,
+ suffixFormatter,
+ scaledSIFormatter,
+} from '~/lib/utils/unit_format/formatter_factory';
+
+describe('unit_format/formatter_factory', () => {
+ describe('numberFormatter', () => {
+ let formatNumber;
+ beforeEach(() => {
+ formatNumber = numberFormatter();
+ });
+
+ it('formats a integer', () => {
+ expect(formatNumber(1)).toEqual('1');
+ expect(formatNumber(100)).toEqual('100');
+ expect(formatNumber(1000)).toEqual('1,000');
+ expect(formatNumber(10000)).toEqual('10,000');
+ expect(formatNumber(1000000)).toEqual('1,000,000');
+ });
+
+ it('formats a floating point number', () => {
+ expect(formatNumber(0.1)).toEqual('0.1');
+ expect(formatNumber(0.1, 0)).toEqual('0');
+ expect(formatNumber(0.1, 2)).toEqual('0.10');
+ expect(formatNumber(0.1, 3)).toEqual('0.100');
+
+ expect(formatNumber(12.345)).toEqual('12.345');
+ expect(formatNumber(12.345, 2)).toEqual('12.35');
+ expect(formatNumber(12.345, 4)).toEqual('12.3450');
+ });
+
+ it('formats a large integer with a length limit', () => {
+ expect(formatNumber(10 ** 7, undefined)).toEqual('10,000,000');
+ expect(formatNumber(10 ** 7, undefined, 9)).toEqual('1.00e+7');
+ expect(formatNumber(10 ** 7, undefined, 10)).toEqual('10,000,000');
+ });
+ });
+
+ describe('suffixFormatter', () => {
+ let formatSuffix;
+ beforeEach(() => {
+ formatSuffix = suffixFormatter('pop.', undefined);
+ });
+
+ it('formats a integer', () => {
+ expect(formatSuffix(1)).toEqual('1pop.');
+ expect(formatSuffix(100)).toEqual('100pop.');
+ expect(formatSuffix(1000)).toEqual('1,000pop.');
+ expect(formatSuffix(10000)).toEqual('10,000pop.');
+ expect(formatSuffix(1000000)).toEqual('1,000,000pop.');
+ });
+
+ it('formats a floating point number', () => {
+ expect(formatSuffix(0.1)).toEqual('0.1pop.');
+ expect(formatSuffix(0.1, 0)).toEqual('0pop.');
+ expect(formatSuffix(0.1, 2)).toEqual('0.10pop.');
+ expect(formatSuffix(0.1, 3)).toEqual('0.100pop.');
+
+ expect(formatSuffix(12.345)).toEqual('12.345pop.');
+ expect(formatSuffix(12.345, 2)).toEqual('12.35pop.');
+ expect(formatSuffix(12.345, 4)).toEqual('12.3450pop.');
+ });
+
+ it('formats a negative integer', () => {
+ expect(formatSuffix(-1)).toEqual('-1pop.');
+ expect(formatSuffix(-100)).toEqual('-100pop.');
+ expect(formatSuffix(-1000)).toEqual('-1,000pop.');
+ expect(formatSuffix(-10000)).toEqual('-10,000pop.');
+ expect(formatSuffix(-1000000)).toEqual('-1,000,000pop.');
+ });
+
+ it('formats a floating point nugative number', () => {
+ expect(formatSuffix(-0.1)).toEqual('-0.1pop.');
+ expect(formatSuffix(-0.1, 0)).toEqual('-0pop.');
+ expect(formatSuffix(-0.1, 2)).toEqual('-0.10pop.');
+ expect(formatSuffix(-0.1, 3)).toEqual('-0.100pop.');
+
+ expect(formatSuffix(-12.345)).toEqual('-12.345pop.');
+ expect(formatSuffix(-12.345, 2)).toEqual('-12.35pop.');
+ expect(formatSuffix(-12.345, 4)).toEqual('-12.3450pop.');
+ });
+
+ it('formats a large integer', () => {
+ expect(formatSuffix(10 ** 7)).toEqual('10,000,000pop.');
+ expect(formatSuffix(10 ** 10)).toEqual('10,000,000,000pop.');
+ });
+
+ it('formats a large integer with a length limit', () => {
+ expect(formatSuffix(10 ** 7, undefined, 10)).toEqual('1.00e+7pop.');
+ expect(formatSuffix(10 ** 10, undefined, 10)).toEqual('1.00e+10pop.');
+ });
+ });
+
+ describe('scaledSIFormatter', () => {
+ describe('scaled format', () => {
+ let formatScaled;
+
+ beforeEach(() => {
+ formatScaled = scaledSIFormatter('B');
+ });
+
+ it('formats bytes', () => {
+ expect(formatScaled(12.345)).toEqual('12.345B');
+ expect(formatScaled(12.345, 0)).toEqual('12B');
+ expect(formatScaled(12.345, 1)).toEqual('12.3B');
+ expect(formatScaled(12.345, 2)).toEqual('12.35B');
+ });
+
+ it('formats bytes in a scale', () => {
+ expect(formatScaled(1)).toEqual('1B');
+ expect(formatScaled(10)).toEqual('10B');
+ expect(formatScaled(10 ** 2)).toEqual('100B');
+ expect(formatScaled(10 ** 3)).toEqual('1kB');
+ expect(formatScaled(10 ** 4)).toEqual('10kB');
+ expect(formatScaled(10 ** 5)).toEqual('100kB');
+ expect(formatScaled(10 ** 6)).toEqual('1MB');
+ expect(formatScaled(10 ** 7)).toEqual('10MB');
+ expect(formatScaled(10 ** 8)).toEqual('100MB');
+ expect(formatScaled(10 ** 9)).toEqual('1GB');
+ expect(formatScaled(10 ** 10)).toEqual('10GB');
+ expect(formatScaled(10 ** 11)).toEqual('100GB');
+ });
+ });
+
+ describe('scaled format with offset', () => {
+ let formatScaled;
+
+ beforeEach(() => {
+ // formats gigabytes
+ formatScaled = scaledSIFormatter('B', 3);
+ });
+
+ it('formats floating point numbers', () => {
+ expect(formatScaled(12.345)).toEqual('12.345GB');
+ expect(formatScaled(12.345, 0)).toEqual('12GB');
+ expect(formatScaled(12.345, 1)).toEqual('12.3GB');
+ expect(formatScaled(12.345, 2)).toEqual('12.35GB');
+ });
+
+ it('formats large numbers scaled', () => {
+ expect(formatScaled(1)).toEqual('1GB');
+ expect(formatScaled(1, 1)).toEqual('1.0GB');
+ expect(formatScaled(10)).toEqual('10GB');
+ expect(formatScaled(10 ** 2)).toEqual('100GB');
+ expect(formatScaled(10 ** 3)).toEqual('1TB');
+ expect(formatScaled(10 ** 4)).toEqual('10TB');
+ expect(formatScaled(10 ** 5)).toEqual('100TB');
+ expect(formatScaled(10 ** 6)).toEqual('1PB');
+ expect(formatScaled(10 ** 7)).toEqual('10PB');
+ expect(formatScaled(10 ** 8)).toEqual('100PB');
+ expect(formatScaled(10 ** 9)).toEqual('1EB');
+ });
+
+ it('formatting of too large numbers is not suported', () => {
+ // formatting YB is out of range
+ expect(() => scaledSIFormatter('B', 9)).toThrow();
+ });
+ });
+
+ describe('scaled format with negative offset', () => {
+ let formatScaled;
+
+ beforeEach(() => {
+ formatScaled = scaledSIFormatter('g', -1);
+ });
+
+ it('formats floating point numbers', () => {
+ expect(formatScaled(12.345)).toEqual('12.345mg');
+ expect(formatScaled(12.345, 0)).toEqual('12mg');
+ expect(formatScaled(12.345, 1)).toEqual('12.3mg');
+ expect(formatScaled(12.345, 2)).toEqual('12.35mg');
+ });
+
+ it('formats large numbers scaled', () => {
+ expect(formatScaled(1)).toEqual('1mg');
+ expect(formatScaled(1, 1)).toEqual('1.0mg');
+ expect(formatScaled(10)).toEqual('10mg');
+ expect(formatScaled(10 ** 2)).toEqual('100mg');
+ expect(formatScaled(10 ** 3)).toEqual('1g');
+ expect(formatScaled(10 ** 4)).toEqual('10g');
+ expect(formatScaled(10 ** 5)).toEqual('100g');
+ expect(formatScaled(10 ** 6)).toEqual('1kg');
+ expect(formatScaled(10 ** 7)).toEqual('10kg');
+ expect(formatScaled(10 ** 8)).toEqual('100kg');
+ });
+
+ it('formats negative numbers scaled', () => {
+ expect(formatScaled(-12.345)).toEqual('-12.345mg');
+ expect(formatScaled(-12.345, 0)).toEqual('-12mg');
+ expect(formatScaled(-12.345, 1)).toEqual('-12.3mg');
+ expect(formatScaled(-12.345, 2)).toEqual('-12.35mg');
+
+ expect(formatScaled(-10)).toEqual('-10mg');
+ expect(formatScaled(-100)).toEqual('-100mg');
+ expect(formatScaled(-(10 ** 4))).toEqual('-10g');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/unit_format/index_spec.js b/spec/frontend/lib/utils/unit_format/index_spec.js
new file mode 100644
index 00000000000..e0991f2909b
--- /dev/null
+++ b/spec/frontend/lib/utils/unit_format/index_spec.js
@@ -0,0 +1,117 @@
+import { getFormatter, SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
+
+describe('unit_format', () => {
+ describe('when a supported format is provided, the returned function formats', () => {
+ it('numbers, by default', () => {
+ expect(getFormatter()(1)).toEqual('1');
+ });
+
+ it('numbers', () => {
+ const formatNumber = getFormatter(SUPPORTED_FORMATS.number);
+
+ expect(formatNumber(1)).toEqual('1');
+ expect(formatNumber(100)).toEqual('100');
+ expect(formatNumber(1000)).toEqual('1,000');
+ expect(formatNumber(10000)).toEqual('10,000');
+ expect(formatNumber(1000000)).toEqual('1,000,000');
+ });
+
+ it('percent', () => {
+ const formatPercent = getFormatter(SUPPORTED_FORMATS.percent);
+
+ expect(formatPercent(1)).toEqual('100%');
+ expect(formatPercent(1, 2)).toEqual('100.00%');
+
+ expect(formatPercent(0.1)).toEqual('10%');
+ expect(formatPercent(0.5)).toEqual('50%');
+
+ expect(formatPercent(0.888888)).toEqual('89%');
+ expect(formatPercent(0.888888, 2)).toEqual('88.89%');
+ expect(formatPercent(0.888888, 5)).toEqual('88.88880%');
+
+ expect(formatPercent(2)).toEqual('200%');
+ expect(formatPercent(10)).toEqual('1,000%');
+ });
+
+ it('percentunit', () => {
+ const formatPercentHundred = getFormatter(SUPPORTED_FORMATS.percentHundred);
+
+ expect(formatPercentHundred(1)).toEqual('1%');
+ expect(formatPercentHundred(1, 2)).toEqual('1.00%');
+
+ expect(formatPercentHundred(88.8888)).toEqual('89%');
+ expect(formatPercentHundred(88.8888, 2)).toEqual('88.89%');
+ expect(formatPercentHundred(88.8888, 5)).toEqual('88.88880%');
+
+ expect(formatPercentHundred(100)).toEqual('100%');
+ expect(formatPercentHundred(100, 2)).toEqual('100.00%');
+
+ expect(formatPercentHundred(200)).toEqual('200%');
+ expect(formatPercentHundred(1000)).toEqual('1,000%');
+ });
+
+ it('seconds', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.seconds)(1)).toEqual('1s');
+ });
+
+ it('miliseconds', () => {
+ const formatMiliseconds = getFormatter(SUPPORTED_FORMATS.miliseconds);
+
+ expect(formatMiliseconds(1)).toEqual('1ms');
+ expect(formatMiliseconds(100)).toEqual('100ms');
+ expect(formatMiliseconds(1000)).toEqual('1,000ms');
+ expect(formatMiliseconds(10000)).toEqual('10,000ms');
+ expect(formatMiliseconds(1000000)).toEqual('1,000,000ms');
+ });
+
+ it('bytes', () => {
+ const formatBytes = getFormatter(SUPPORTED_FORMATS.bytes);
+
+ expect(formatBytes(1)).toEqual('1B');
+ expect(formatBytes(1, 1)).toEqual('1.0B');
+
+ expect(formatBytes(10)).toEqual('10B');
+ expect(formatBytes(10 ** 2)).toEqual('100B');
+ expect(formatBytes(10 ** 3)).toEqual('1kB');
+ expect(formatBytes(10 ** 4)).toEqual('10kB');
+ expect(formatBytes(10 ** 5)).toEqual('100kB');
+ expect(formatBytes(10 ** 6)).toEqual('1MB');
+ expect(formatBytes(10 ** 7)).toEqual('10MB');
+ expect(formatBytes(10 ** 8)).toEqual('100MB');
+ expect(formatBytes(10 ** 9)).toEqual('1GB');
+ expect(formatBytes(10 ** 10)).toEqual('10GB');
+ expect(formatBytes(10 ** 11)).toEqual('100GB');
+ });
+
+ it('kilobytes', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1)).toEqual('1kB');
+ expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1, 1)).toEqual('1.0kB');
+ });
+
+ it('megabytes', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1)).toEqual('1MB');
+ expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1, 1)).toEqual('1.0MB');
+ });
+
+ it('gigabytes', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1)).toEqual('1GB');
+ expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1, 1)).toEqual('1.0GB');
+ });
+
+ it('terabytes', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1)).toEqual('1TB');
+ expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1, 1)).toEqual('1.0TB');
+ });
+
+ it('petabytes', () => {
+ expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1)).toEqual('1PB');
+ expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1, 1)).toEqual('1.0PB');
+ });
+ });
+
+ describe('when get formatter format is incorrect', () => {
+ it('formatter fails', () => {
+ expect(() => getFormatter('not-supported')(1)).toThrow();
+ });
+ });
+});
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index f5771405687..322390c3840 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -56,7 +56,7 @@ describe LabelsHelper do
context 'without subject' do
it "uses the label's project" do
- expect(link_to_label(label_presenter)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a.*href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
@@ -65,7 +65,7 @@ describe LabelsHelper do
let(:subject) { build(:project, namespace: namespace, name: 'bar3') }
it 'links to project issues page' do
- expect(link_to_label(label_presenter)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a.*href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
@@ -73,7 +73,7 @@ describe LabelsHelper do
let(:subject) { build(:group, name: 'bar') }
it 'links to group issues page' do
- expect(link_to_label(label_presenter)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a.*href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
@@ -81,7 +81,7 @@ describe LabelsHelper do
['issue', :issue].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter, type: type)).to match %r{<a.*href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
end
@@ -89,7 +89,7 @@ describe LabelsHelper do
['merge_request', :merge_request].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/-/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter, type: type)).to match %r{<a.*href="/#{label.project.full_path}/-/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
end
@@ -113,7 +113,7 @@ describe LabelsHelper do
context 'without block' do
it 'uses render_colored_label as the link content' do
expect(self).to receive(:render_colored_label)
- .with(label_presenter, tooltip: true).and_return('Foo')
+ .with(label_presenter).and_return('Foo')
expect(link_to_label(label_presenter)).to match('Foo')
end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 3fb36e540b6..c22e20f0e73 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -537,8 +537,10 @@ describe MarkupHelper do
it 'does not style a label that can not be accessed by current_user' do
project = create(:project, :private)
+ label = create_and_format_label(project)
- expect(create_and_format_label(project)).to eq("<p>#{label_title}</p>")
+ expect(label).to include("~label_1")
+ expect(label).not_to match(/span class=.*style=.*/)
end
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 82df5064896..5a672de13d7 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -28,7 +28,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
- expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label has-tooltip'
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label has-tooltip gl-link gl-label-link'
end
it 'includes a data-project attribute' do
@@ -66,12 +66,12 @@ describe Banzai::Filter::LabelReferenceFilter do
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
- expect(doc.css('a span').first.attr('class')).to eq 'badge color-label has-tooltip'
+ expect(doc.css('a span').first.attr('class')).to include 'gl-label-text'
end
it 'includes a style attribute' do
doc = reference_filter("Label #{reference}")
- expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/)
+ expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}\z/)
end
end
@@ -85,7 +85,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{label.name}</span></a></span>\.\)))
end
it 'ignores invalid label IDs' do
@@ -109,7 +109,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}).")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{label.name}</span></a></span>\)\.))
end
it 'ignores invalid label names' do
@@ -133,7 +133,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}).")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{label.name}</span></a></span>\)\.))
end
it 'ignores invalid label names' do
@@ -158,7 +158,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'does not include trailing punctuation', :aggregate_failures do
['.', ', ok?', '...', '?', '!', ': is that ok?'].each do |trailing_punctuation|
doc = filter("Label #{reference}#{trailing_punctuation}")
- expect(doc.to_html).to match(%r(<a.+><span.+>\?g\.fm&amp;</span></a>#{Regexp.escape(trailing_punctuation)}))
+ expect(doc.to_html).to match(%r(<span.+><a.+><span.+>\?g\.fm&amp;</span></a></span>#{Regexp.escape(trailing_punctuation)}))
end
end
@@ -184,7 +184,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{label.name}</span></a></span>\.\)))
end
it 'ignores invalid label names' do
@@ -208,7 +208,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{label.name}</span></a></span>\.\)))
end
it 'ignores invalid label names' do
@@ -232,7 +232,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>g\.fm &amp; references\?</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>g\.fm &amp; references\?</span></a></span>\.\)))
end
it 'ignores invalid label names' do
@@ -320,7 +320,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+>Label</a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+>Label</a></span>\.\)))
end
it 'includes a data-project attribute' do
@@ -358,7 +358,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{group_label.name}</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{group_label.name}</span></a></span>\.\)))
end
it 'ignores invalid label names' do
@@ -381,7 +381,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{group_label.name}</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<span.+><a.+><span.+>#{group_label.name}</span></a></span>\.\)))
end
it 'ignores invalid label names' do
diff --git a/spec/services/resource_events/change_milestone_service_spec.rb b/spec/services/resource_events/change_milestone_service_spec.rb
index c6b4f8e1b7e..bc634fadadd 100644
--- a/spec/services/resource_events/change_milestone_service_spec.rb
+++ b/spec/services/resource_events/change_milestone_service_spec.rb
@@ -3,65 +3,11 @@
require 'spec_helper'
describe ResourceEvents::ChangeMilestoneService do
- shared_examples 'milestone events creator' do
- let_it_be(:user) { create(:user) }
-
- let_it_be(:milestone) { create(:milestone) }
-
- context 'when milestone is present' do
- before do
- resource.milestone = milestone
- end
-
- let(:service) { described_class.new(resource: resource, user: user, created_at: created_at_time) }
-
- it 'creates the expected event record' do
- expect { service.execute }.to change { ResourceMilestoneEvent.count }.from(0).to(1)
-
- events = ResourceMilestoneEvent.all
-
- expect(events.size).to eq(1)
- expect_event_record(events.first, action: 'add', milestone: milestone, state: 'opened')
- end
- end
-
- context 'when milestones is not present' do
- before do
- resource.milestone = nil
- end
-
- let(:service) { described_class.new(resource: resource, user: user, created_at: created_at_time) }
-
- it 'creates the expected event records' do
- expect { service.execute }.to change { ResourceMilestoneEvent.count }.from(0).to(1)
-
- expect_event_record(ResourceMilestoneEvent.first, action: 'remove', milestone: nil, state: 'opened')
- end
- end
-
- def expect_event_record(event, expected_attrs)
- expect(event.action).to eq(expected_attrs[:action])
- expect(event.state).to eq(expected_attrs[:state])
- expect(event.user).to eq(user)
- expect(event.issue).to eq(resource) if resource.is_a?(Issue)
- expect(event.issue).to be_nil unless resource.is_a?(Issue)
- expect(event.merge_request).to eq(resource) if resource.is_a?(MergeRequest)
- expect(event.merge_request).to be_nil unless resource.is_a?(MergeRequest)
- expect(event.milestone).to eq(expected_attrs[:milestone])
- expect(event.created_at).to eq(created_at_time)
- end
- end
-
- let_it_be(:merge_request) { create(:merge_request) }
- let_it_be(:issue) { create(:issue) }
-
- let!(:created_at_time) { Time.utc(2019, 12, 30) }
-
- it_behaves_like 'milestone events creator' do
- let(:resource) { issue }
+ it_behaves_like 'a milestone events creator' do
+ let(:resource) { create(:issue) }
end
- it_behaves_like 'milestone events creator' do
- let(:resource) { merge_request }
+ it_behaves_like 'a milestone events creator' do
+ let(:resource) { create(:merge_request) }
end
end
diff --git a/spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb b/spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb
new file mode 100644
index 00000000000..77f64e5e8f8
--- /dev/null
+++ b/spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+shared_examples 'a milestone events creator' do
+ let_it_be(:user) { create(:user) }
+
+ let(:created_at_time) { Time.utc(2019, 12, 30) }
+ let(:service) { described_class.new(resource, user, created_at: created_at_time) }
+
+ context 'when milestone is present' do
+ let_it_be(:milestone) { create(:milestone) }
+
+ before do
+ resource.milestone = milestone
+ end
+
+ it 'creates the expected event record' do
+ expect { service.execute }.to change { ResourceMilestoneEvent.count }.by(1)
+
+ expect_event_record(ResourceMilestoneEvent.last, action: 'add', milestone: milestone, state: 'opened')
+ end
+ end
+
+ context 'when milestones is not present' do
+ before do
+ resource.milestone = nil
+ end
+
+ it 'creates the expected event records' do
+ expect { service.execute }.to change { ResourceMilestoneEvent.count }.by(1)
+
+ expect_event_record(ResourceMilestoneEvent.last, action: 'remove', milestone: nil, state: 'opened')
+ end
+ end
+
+ def expect_event_record(event, expected_attrs)
+ expect(event.action).to eq(expected_attrs[:action])
+ expect(event.state).to eq(expected_attrs[:state])
+ expect(event.user).to eq(user)
+ expect(event.issue).to eq(resource) if resource.is_a?(Issue)
+ expect(event.issue).to be_nil unless resource.is_a?(Issue)
+ expect(event.merge_request).to eq(resource) if resource.is_a?(MergeRequest)
+ expect(event.merge_request).to be_nil unless resource.is_a?(MergeRequest)
+ expect(event.milestone).to eq(expected_attrs[:milestone])
+ expect(event.created_at).to eq(created_at_time)
+ end
+end