summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/concerns/confirm_email_warning_spec.rb2
-rw-r--r--spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb4
-rw-r--r--spec/controllers/concerns/group_tree_spec.rb2
-rw-r--r--spec/controllers/concerns/lfs_request_spec.rb2
-rw-r--r--spec/controllers/concerns/metrics_dashboard_spec.rb2
-rw-r--r--spec/controllers/concerns/renders_commits_spec.rb2
-rw-r--r--spec/controllers/concerns/routable_actions_spec.rb2
-rw-r--r--spec/controllers/concerns/sourcegraph_gon_spec.rb2
-rw-r--r--spec/controllers/concerns/static_object_external_storage_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb4
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb11
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb10
-rw-r--r--spec/features/projects/pages_spec.rb4
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb4
-rw-r--r--spec/finders/branches_finder_spec.rb2
-rw-r--r--spec/finders/todos_finder_spec.rb2
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js573
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_url_spec.js54
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_window_spec.js75
-rw-r--r--spec/frontend/monitoring/components/graph_group_spec.js (renamed from spec/javascripts/monitoring/components/graph_group_spec.js)0
-rw-r--r--spec/frontend/monitoring/init_utils.js56
-rw-r--r--spec/frontend/monitoring/shared/prometheus_header_spec.js (renamed from spec/javascripts/monitoring/shared/prometheus_header_spec.js)2
-rw-r--r--spec/helpers/container_expiration_policies_helper_spec.rb6
-rw-r--r--spec/helpers/projects_helper_spec.rb4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js3
-rw-r--r--spec/javascripts/monitoring/components/dashboard_resize_spec.js140
-rw-r--r--spec/javascripts/monitoring/components/dashboard_spec.js729
-rw-r--r--spec/lib/expand_variables_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb6
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb4
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/blob_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb2
-rw-r--r--spec/lib/gitlab/phabricator_import/conduit/user_spec.rb6
-rw-r--r--spec/lib/gitlab/phabricator_import/user_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb2
-rw-r--r--spec/lib/quality/helm_client_spec.rb2
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb2
-rw-r--r--spec/migrations/fix_max_pages_size_spec.rb19
-rw-r--r--spec/models/active_session_spec.rb2
-rw-r--r--spec/models/application_setting_spec.rb7
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb6
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb21
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb1
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/requests/api/wikis_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb2
-rw-r--r--spec/services/projects/update_pages_service_spec.rb48
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/helpers/test_env.rb1
-rw-r--r--spec/support/shared_examples/pages_size_limit_shared_examples.rb30
-rw-r--r--spec/views/projects/commit/branches.html.haml_spec.rb4
54 files changed, 1051 insertions, 833 deletions
diff --git a/spec/controllers/concerns/confirm_email_warning_spec.rb b/spec/controllers/concerns/confirm_email_warning_spec.rb
index 25429cdd149..56a6efab8ed 100644
--- a/spec/controllers/concerns/confirm_email_warning_spec.rb
+++ b/spec/controllers/concerns/confirm_email_warning_spec.rb
@@ -10,7 +10,7 @@ describe ConfirmEmailWarning do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ConfirmEmailWarning # rubocop:disable RSpec/DescribedClass
+ include ConfirmEmailWarning
def index
head :ok
diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
index 7a56f7203b0..e47f1650b1f 100644
--- a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
+++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
@@ -22,7 +22,7 @@ describe ControllerWithCrossProjectAccessCheck do
describe '#requires_cross_project_access' do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+ include ControllerWithCrossProjectAccessCheck
requires_cross_project_access :index, show: false,
unless: -> { unless_condition },
@@ -81,7 +81,7 @@ describe ControllerWithCrossProjectAccessCheck do
describe '#skip_cross_project_access_check' do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+ include ControllerWithCrossProjectAccessCheck
requires_cross_project_access
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb
index 835c3d9b3af..543f0170be0 100644
--- a/spec/controllers/concerns/group_tree_spec.rb
+++ b/spec/controllers/concerns/group_tree_spec.rb
@@ -8,7 +8,7 @@ describe GroupTree do
controller(ApplicationController) do
# `described_class` is not available in this context
- include GroupTree # rubocop:disable RSpec/DescribedClass
+ include GroupTree
def index
render_group_tree GroupsFinder.new(current_user).execute
diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb
index 823b9a50434..584448e68f9 100644
--- a/spec/controllers/concerns/lfs_request_spec.rb
+++ b/spec/controllers/concerns/lfs_request_spec.rb
@@ -7,7 +7,7 @@ describe LfsRequest do
controller(Projects::GitHttpClientController) do
# `described_class` is not available in this context
- include LfsRequest # rubocop:disable RSpec/DescribedClass
+ include LfsRequest
def show
storage_project
diff --git a/spec/controllers/concerns/metrics_dashboard_spec.rb b/spec/controllers/concerns/metrics_dashboard_spec.rb
index ff2b6fbb8ec..389d264bed3 100644
--- a/spec/controllers/concerns/metrics_dashboard_spec.rb
+++ b/spec/controllers/concerns/metrics_dashboard_spec.rb
@@ -16,7 +16,7 @@ describe MetricsDashboard do
end
controller(::ApplicationController) do
- include MetricsDashboard # rubocop:disable RSpec/DescribedClass
+ include MetricsDashboard
end
let(:json_response) do
diff --git a/spec/controllers/concerns/renders_commits_spec.rb b/spec/controllers/concerns/renders_commits_spec.rb
index 79350847383..c43ceb6b795 100644
--- a/spec/controllers/concerns/renders_commits_spec.rb
+++ b/spec/controllers/concerns/renders_commits_spec.rb
@@ -9,7 +9,7 @@ describe RendersCommits do
controller(ApplicationController) do
# `described_class` is not available in this context
- include RendersCommits # rubocop:disable RSpec/DescribedClass
+ include RendersCommits
def index
@merge_request = MergeRequest.find(params[:id])
diff --git a/spec/controllers/concerns/routable_actions_spec.rb b/spec/controllers/concerns/routable_actions_spec.rb
index 59d48c68b9c..a11f4d2a154 100644
--- a/spec/controllers/concerns/routable_actions_spec.rb
+++ b/spec/controllers/concerns/routable_actions_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe RoutableActions do
controller(::ApplicationController) do
- include RoutableActions # rubocop:disable RSpec/DescribedClass
+ include RoutableActions
before_action :routable
diff --git a/spec/controllers/concerns/sourcegraph_gon_spec.rb b/spec/controllers/concerns/sourcegraph_gon_spec.rb
index 4fb7e37d148..d9273987871 100644
--- a/spec/controllers/concerns/sourcegraph_gon_spec.rb
+++ b/spec/controllers/concerns/sourcegraph_gon_spec.rb
@@ -17,7 +17,7 @@ describe SourcegraphGon do
let(:project) { internal_project }
controller(ApplicationController) do
- include SourcegraphGon # rubocop:disable RSpec/DescribedClass
+ include SourcegraphGon
def index
head :ok
diff --git a/spec/controllers/concerns/static_object_external_storage_spec.rb b/spec/controllers/concerns/static_object_external_storage_spec.rb
index 3a0219ddaa1..ddd1a95427e 100644
--- a/spec/controllers/concerns/static_object_external_storage_spec.rb
+++ b/spec/controllers/concerns/static_object_external_storage_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe StaticObjectExternalStorage do
controller(Projects::ApplicationController) do
- include StaticObjectExternalStorage # rubocop:disable RSpec/DescribedClass
+ include StaticObjectExternalStorage
before_action :redirect_to_external_storage, if: :static_objects_external_storage_enabled?
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 757d8704a6a..ac39ac626c7 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -591,7 +591,7 @@ describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
- names: ['fix', 'add-pdf-file', 'branch-merged']
+ names: %w[fix add-pdf-file branch-merged]
}
expect(response).to have_gitlab_http_status(200)
@@ -639,7 +639,7 @@ describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
- names: ['fix', 'add-pdf-file', 'branch-merged']
+ names: %w[fix add-pdf-file branch-merged]
}
expect(response).to have_gitlab_http_status(200)
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index f80bbf0d78f..c07619465bf 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -115,5 +115,16 @@ describe Projects::PagesController do
patch :update, params: request_params
end
+
+ context 'when update_service returns an error message' do
+ let(:update_service) { double(execute: { status: :error, message: 'some error happened' }) }
+
+ it 'adds an error message' do
+ patch :update, params: request_params
+
+ expect(response).to redirect_to(project_pages_path(project))
+ expect(flash[:alert]).to eq('some error happened')
+ end
+ end
end
end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 10672bbec68..657513431e5 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -229,6 +229,16 @@ describe "User browses files" do
expect(page).to have_content("*.rb")
.and have_content("Dmitriy Zaporozhets")
.and have_content("Initial commit")
+ .and have_content("Ignore DS files")
+
+ previous_commit_anchor = "//a[@title='Ignore DS files']/parent::span/following-sibling::span/a"
+ find(:xpath, previous_commit_anchor).click
+
+ expect(page).to have_content("*.rb")
+ .and have_content("Dmitriy Zaporozhets")
+ .and have_content("Initial commit")
+
+ expect(page).not_to have_content("Ignore DS files")
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 3c4b5b2c4ca..afd1178f7f2 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -322,7 +322,7 @@ shared_examples 'pages settings editing' do
before do
allow(Projects::UpdateService).to receive(:new).and_return(service)
- allow(service).to receive(:execute).and_return(status: :error)
+ allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
end
it 'tries to change the setting' do
@@ -332,7 +332,7 @@ shared_examples 'pages settings editing' do
click_button 'Save'
- expect(page).to have_text('Something went wrong on our end')
+ expect(page).to have_text('Some error has occured')
end
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index ad65e04473c..94af023e804 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -50,7 +50,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
end
it 'creates a snippet when all required fields are filled in after validation failing' do
@@ -72,7 +72,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
expect(page).to have_selector('strong')
end
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
end
end
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index 70b5da0cc3c..5f75ff8c6ff 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -66,7 +66,7 @@ describe BranchesFinder do
end
it 'filters branches by provided names' do
- branches_finder = described_class.new(repository, { names: ['fix', 'csv', 'lfs', 'does-not-exist'] })
+ branches_finder = described_class.new(repository, { names: %w[fix csv lfs does-not-exist] })
result = branches_finder.execute
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index a837e7af251..a35c3a954e7 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -219,7 +219,7 @@ describe TodosFinder do
end
it "sorts by priority" do
- project_2 = create(:project)
+ project_2 = create(:project)
label_1 = create(:label, title: 'label_1', project: project, priority: 1)
label_2 = create(:label, title: 'label_2', project: project, priority: 2)
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
new file mode 100644
index 00000000000..8a10857d0ff
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -0,0 +1,573 @@
+import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
+import { GlDropdownItem, GlButton, GlToast } from '@gitlab/ui';
+import VueDraggable from 'vuedraggable';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import statusCodes from '~/lib/utils/http_status';
+import { metricStates } from '~/monitoring/constants';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
+import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
+import { createStore } from '~/monitoring/stores';
+import * as types from '~/monitoring/stores/mutation_types';
+import * as monitoringUtils from '~/monitoring/utils';
+import { setupComponentStore, propsData } from '../init_utils';
+import {
+ metricsGroupsAPIResponse,
+ mockedQueryResultPayload,
+ mockApiEndpoint,
+ environmentData,
+ dashboardGitResponse,
+} from '../mock_data';
+
+const localVue = createLocalVue();
+const expectedPanelCount = 2;
+
+describe('Dashboard', () => {
+ let DashboardComponent;
+ let store;
+ let wrapper;
+ let mock;
+
+ const createShallowWrapper = (props = {}, options = {}) => {
+ wrapper = shallowMount(localVue.extend(DashboardComponent), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ const createMountedWrapper = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(DashboardComponent), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ DashboardComponent = localVue.extend(Dashboard);
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ describe('no metrics are available yet', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({}, { attachToDocument: true });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the environment selector', () => {
+ expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
+ });
+
+ describe('no data found', () => {
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({}, { attachToDocument: true });
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the environment selector dropdown', () => {
+ expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
+ });
+
+ describe('request information to the server', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+ });
+
+ it('shows up a loading state', done => {
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.vm.emptyState).toEqual('loading');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('hides the group panels when showPanels is false', done => {
+ createMountedWrapper(
+ { hasMetrics: true, showPanels: false },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.vm.showEmptyState).toEqual(false);
+ expect(wrapper.vm.$el.querySelector('.prometheus-panel')).toEqual(null);
+ // TODO: The last expectation doesn't belong here, it belongs in a `group_group_spec.js` file
+ // Issue: https://gitlab.com/gitlab-org/gitlab/issues/118780
+ // expect(wrapper.vm.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('fetches the metrics data with proper time window', done => {
+ const getTimeDiffSpy = jest.spyOn(monitoringUtils, 'getTimeDiff');
+ jest.spyOn(store, 'dispatch');
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(store.dispatch).toHaveBeenCalled();
+ expect(getTimeDiffSpy).toHaveBeenCalled();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('when all requests have been commited by the store', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the environments dropdown with a number of environments', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+
+ expect(wrapper.vm.environments.length).toEqual(environmentData.length);
+ expect(environmentDropdownItems.length).toEqual(wrapper.vm.environments.length);
+
+ environmentDropdownItems.wrappers.forEach((itemWrapper, index) => {
+ const anchorEl = itemWrapper.find('a');
+ if (anchorEl.exists() && environmentData[index].metrics_path) {
+ const href = anchorEl.attributes('href');
+ expect(href).toBe(environmentData[index].metrics_path);
+ }
+ });
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('renders the environments dropdown with a single active element', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+ const activeItem = environmentDropdownItems.wrappers.filter(itemWrapper =>
+ itemWrapper.find('.active').exists(),
+ );
+
+ expect(activeItem.length).toBe(1);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ it('hides the environments dropdown list when there is no environments', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+
+ expect(environmentDropdownItems.length).toEqual(0);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('renders the datetimepicker dropdown', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.find(DateTimePicker).exists()).toBe(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ describe('when one of the metrics is missing', () => {
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+ setupComponentStore(wrapper);
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('shows a group empty area', () => {
+ const emptyGroup = wrapper.findAll({ ref: 'empty-group' });
+
+ expect(emptyGroup).toHaveLength(1);
+ expect(emptyGroup.is(GroupEmptyState)).toBe(true);
+ });
+
+ it('group empty area displays a NO_DATA state', () => {
+ expect(
+ wrapper
+ .findAll({ ref: 'empty-group' })
+ .at(0)
+ .props('selectedState'),
+ ).toEqual(metricStates.NO_DATA);
+ });
+ });
+
+ describe('drag and drop function', () => {
+ const findDraggables = () => wrapper.findAll(VueDraggable);
+ const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled'));
+ const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
+ const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
+
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+ });
+
+ beforeEach(done => {
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('wraps vuedraggable', () => {
+ expect(findDraggablePanels().exists()).toBe(true);
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount);
+ });
+
+ it('is disabled by default', () => {
+ expect(findRearrangeButton().exists()).toBe(false);
+ expect(findEnabledDraggables().length).toBe(0);
+ });
+
+ describe('when rearrange is enabled', () => {
+ beforeEach(done => {
+ wrapper.setProps({ rearrangePanelsAvailable: true });
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('displays rearrange button', () => {
+ expect(findRearrangeButton().exists()).toBe(true);
+ });
+
+ describe('when rearrange button is clicked', () => {
+ const findFirstDraggableRemoveButton = () =>
+ findDraggablePanels()
+ .at(0)
+ .find('.js-draggable-remove');
+
+ beforeEach(done => {
+ findRearrangeButton().vm.$emit('click');
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('it enables draggables', () => {
+ expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
+ expect(findEnabledDraggables()).toEqual(findDraggables());
+ });
+
+ it('metrics can be swapped', done => {
+ const firstDraggable = findDraggables().at(0);
+ const mockMetrics = [...metricsGroupsAPIResponse[1].panels];
+
+ const firstTitle = mockMetrics[0].title;
+ const secondTitle = mockMetrics[1].title;
+
+ // swap two elements and `input` them
+ [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]];
+ firstDraggable.vm.$emit('input', mockMetrics);
+
+ wrapper.vm.$nextTick(() => {
+ const { panels } = wrapper.vm.dashboard.panel_groups[1];
+
+ expect(panels[1].title).toEqual(firstTitle);
+ expect(panels[0].title).toEqual(secondTitle);
+ done();
+ });
+ });
+
+ it('shows a remove button, which removes a panel', done => {
+ expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
+
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount);
+ findFirstDraggableRemoveButton().trigger('click');
+
+ wrapper.vm.$nextTick(() => {
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
+ done();
+ });
+ });
+
+ it('it disables draggables when clicked again', done => {
+ findRearrangeButton().vm.$emit('click');
+ wrapper.vm.$nextTick(() => {
+ expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
+ expect(findEnabledDraggables().length).toBe(0);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe('cluster health', () => {
+ beforeEach(done => {
+ mock.onGet(propsData.metricsEndpoint).reply(statusCodes.OK, JSON.stringify({}));
+ createShallowWrapper({ hasMetrics: true });
+
+ // all_dashboards is not defined in health dashboards
+ wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders correctly', () => {
+ expect(wrapper.isVueInstance()).toBe(true);
+ expect(wrapper.exists()).toBe(true);
+ });
+ });
+
+ describe('dashboard edit link', () => {
+ const findEditLink = () => wrapper.find('.js-edit-link');
+
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
+ dashboardGitResponse,
+ );
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('is not present for the default dashboard', () => {
+ expect(findEditLink().exists()).toBe(false);
+ });
+
+ it('is present for a custom dashboard, and links to its edit_path', done => {
+ const dashboard = dashboardGitResponse[1]; // non-default dashboard
+ const currentDashboard = dashboard.path;
+
+ wrapper.setProps({ currentDashboard });
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(findEditLink().exists()).toBe(true);
+ expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('Dashboard dropdown', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
+ dashboardGitResponse,
+ );
+ });
+
+ it('shows the dashboard dropdown', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const dashboardDropdown = wrapper.find('.js-dashboards-dropdown');
+
+ expect(dashboardDropdown.exists()).toBe(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('external dashboard link', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ {
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ externalDashboardUrl: '/mockUrl',
+ },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+ });
+
+ it('shows the link', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const externalDashboardButton = wrapper.find('.js-external-dashboard-link');
+
+ expect(externalDashboardButton.exists()).toBe(true);
+ expect(externalDashboardButton.is(GlButton)).toBe(true);
+ expect(externalDashboardButton.text()).toContain('View full dashboard');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
+ // eslint-disable-next-line jest/no-disabled-tests
+ describe.skip('link to chart', () => {
+ const currentDashboard = 'TEST_DASHBOARD';
+ localVue.use(GlToast);
+ const link = () => wrapper.find('.js-chart-link');
+ const clipboardText = () => link().element.dataset.clipboardText;
+
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
+
+ setTimeout(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('adds a copy button to the dropdown', () => {
+ expect(link().text()).toContain('Generate link to chart');
+ });
+
+ it('contains a link to the dashboard', () => {
+ expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
+ expect(clipboardText()).toContain(`group=`);
+ expect(clipboardText()).toContain(`title=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ });
+
+ it('undefined parameter is stripped', done => {
+ wrapper.setProps({ currentDashboard: undefined });
+
+ wrapper.vm.$nextTick(() => {
+ expect(clipboardText()).not.toContain(`dashboard=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ done();
+ });
+ });
+
+ it('null parameter is stripped', done => {
+ wrapper.setProps({ currentDashboard: null });
+
+ wrapper.vm.$nextTick(() => {
+ expect(clipboardText()).not.toContain(`dashboard=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ done();
+ });
+ });
+
+ it('creates a toast when clicked', () => {
+ jest.spyOn(wrapper.vm.$toast, 'show').and.stub();
+
+ link().vm.$emit('click');
+
+ expect(wrapper.vm.$toast.show).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/dashboard_time_url_spec.js b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
new file mode 100644
index 00000000000..8dc450cf131
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
@@ -0,0 +1,54 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import createFlash from '~/flash';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import { createStore } from '~/monitoring/stores';
+import { propsData } from '../init_utils';
+
+const localVue = createLocalVue();
+
+jest.mock('~/flash');
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ getParameterValues: jest.fn().mockReturnValue('<script>alert("XSS")</script>'),
+}));
+
+describe('dashboard invalid url parameters', () => {
+ let store;
+ let wrapper;
+
+ const createMountedWrapper = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(Dashboard), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ it('shows an error message if invalid url parameters are passed', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(createFlash).toHaveBeenCalled();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+});
diff --git a/spec/frontend/monitoring/components/dashboard_time_window_spec.js b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
new file mode 100644
index 00000000000..d49af6f84cb
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
@@ -0,0 +1,75 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import { GlDropdownItem } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import statusCodes from '~/lib/utils/http_status';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import { createStore } from '~/monitoring/stores';
+import { propsData, setupComponentStore } from '../init_utils';
+import { metricsGroupsAPIResponse, mockApiEndpoint } from '../mock_data';
+
+const localVue = createLocalVue();
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ getParameterValues: jest.fn().mockImplementation(param => {
+ if (param === 'start') return ['2019-10-01T18:27:47.000Z'];
+ if (param === 'end') return ['2019-10-01T18:57:47.000Z'];
+ return [];
+ }),
+ mergeUrlParams: jest.fn().mockReturnValue('#'),
+}));
+
+describe('dashboard time window', () => {
+ let store;
+ let wrapper;
+ let mock;
+
+ const createComponentWrapperMounted = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(Dashboard), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ it('shows an error message if invalid url parameters are passed', done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createComponentWrapperMounted(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const timeWindowDropdownItems = wrapper
+ .find('.js-time-window-dropdown')
+ .findAll(GlDropdownItem);
+ const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
+ itemWrapper.find('.active').exists(),
+ );
+
+ expect(activeItem.length).toBe(1);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/monitoring/components/graph_group_spec.js b/spec/frontend/monitoring/components/graph_group_spec.js
index 43ca17c3cbc..43ca17c3cbc 100644
--- a/spec/javascripts/monitoring/components/graph_group_spec.js
+++ b/spec/frontend/monitoring/components/graph_group_spec.js
diff --git a/spec/frontend/monitoring/init_utils.js b/spec/frontend/monitoring/init_utils.js
new file mode 100644
index 00000000000..10db8b902b5
--- /dev/null
+++ b/spec/frontend/monitoring/init_utils.js
@@ -0,0 +1,56 @@
+import * as types from '~/monitoring/stores/mutation_types';
+import {
+ metricsGroupsAPIResponse,
+ mockedEmptyResult,
+ mockedQueryResultPayload,
+ mockedQueryResultPayloadCoresTotal,
+ mockApiEndpoint,
+ environmentData,
+} from './mock_data';
+
+export const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentsEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+ customMetricsAvailable: false,
+ customMetricsPath: '',
+ validateQueryPath: '',
+};
+
+export const setupComponentStore = wrapper => {
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+
+ // Load 3 panels to the dashboard, one with an empty result
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedEmptyResult,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayloadCoresTotal,
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+};
diff --git a/spec/javascripts/monitoring/shared/prometheus_header_spec.js b/spec/frontend/monitoring/shared/prometheus_header_spec.js
index 9f916a4dfbb..b216bfb72d8 100644
--- a/spec/javascripts/monitoring/shared/prometheus_header_spec.js
+++ b/spec/frontend/monitoring/shared/prometheus_header_spec.js
@@ -18,7 +18,7 @@ describe('Prometheus Header component', () => {
describe('Prometheus header component', () => {
it('should show a title', () => {
- const title = prometheusHeader.vm.$el.querySelector('.js-graph-title').textContent;
+ const title = prometheusHeader.find({ ref: 'title' }).text();
expect(title).toBe('graph header');
});
diff --git a/spec/helpers/container_expiration_policies_helper_spec.rb b/spec/helpers/container_expiration_policies_helper_spec.rb
index 3eb1234d82b..f7e851fb012 100644
--- a/spec/helpers/container_expiration_policies_helper_spec.rb
+++ b/spec/helpers/container_expiration_policies_helper_spec.rb
@@ -8,7 +8,7 @@ describe ContainerExpirationPoliciesHelper do
expected_result = [
{ key: 1, label: '1 tag per image name' },
{ key: 5, label: '5 tags per image name' },
- { key: 10, label: '10 tags per image name' },
+ { key: 10, label: '10 tags per image name', default: true },
{ key: 25, label: '25 tags per image name' },
{ key: 50, label: '50 tags per image name' },
{ key: 100, label: '100 tags per image name' }
@@ -21,7 +21,7 @@ describe ContainerExpirationPoliciesHelper do
describe '#cadence_options' do
it 'returns cadence options formatted for dropdown usage' do
expected_result = [
- { key: '1d', label: 'Every day' },
+ { key: '1d', label: 'Every day', default: true },
{ key: '7d', label: 'Every week' },
{ key: '14d', label: 'Every two weeks' },
{ key: '1month', label: 'Every month' },
@@ -37,7 +37,7 @@ describe ContainerExpirationPoliciesHelper do
expected_result = [
{ key: '7d', label: '7 days until tags are automatically removed' },
{ key: '14d', label: '14 days until tags are automatically removed' },
- { key: '30d', label: '30 days until tags are automatically removed' },
+ { key: '30d', label: '30 days until tags are automatically removed', default: true },
{ key: '90d', label: '90 days until tags are automatically removed' }
]
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 46228d0d1c2..c7e454771bb 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -332,13 +332,13 @@ describe ProjectsHelper do
end
it 'returns image tag for member avatar' do
- expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "", "data-src" => anything })
+ expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16], alt: "", "data-src" => anything })
helper.link_to_member_avatar(user)
end
it 'returns image tag with avatar class' do
- expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "", "data-src" => anything })
+ expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16 any-avatar-class], alt: "", "data-src" => anything })
helper.link_to_member_avatar(user, avatar_class: "any-avatar-class")
end
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index e471be608c8..1d91b508b71 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -88,10 +88,12 @@ describe('common_utils', () => {
describe('handleLocationHash', () => {
beforeEach(() => {
spyOn(window.document, 'getElementById').and.callThrough();
+ jasmine.clock().install();
});
afterEach(() => {
window.history.pushState({}, null, '');
+ jasmine.clock().uninstall();
});
function expectGetElementIdToHaveBeenCalledWith(elementId) {
@@ -171,6 +173,7 @@ describe('common_utils', () => {
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
+ jasmine.clock().tick(1);
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
diff --git a/spec/javascripts/monitoring/components/dashboard_resize_spec.js b/spec/javascripts/monitoring/components/dashboard_resize_spec.js
new file mode 100644
index 00000000000..4eab398e3ab
--- /dev/null
+++ b/spec/javascripts/monitoring/components/dashboard_resize_spec.js
@@ -0,0 +1,140 @@
+import Vue from 'vue';
+import { createLocalVue } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import * as types from '~/monitoring/stores/mutation_types';
+import { createStore } from '~/monitoring/stores';
+import axios from '~/lib/utils/axios_utils';
+import {
+ metricsGroupsAPIResponse,
+ mockedEmptyResult,
+ mockedQueryResultPayload,
+ mockedQueryResultPayloadCoresTotal,
+ mockApiEndpoint,
+ environmentData,
+} from '../mock_data';
+
+const localVue = createLocalVue();
+const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentsEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+ customMetricsAvailable: false,
+ customMetricsPath: '',
+ validateQueryPath: '',
+};
+
+function setupComponentStore(component) {
+ // Load 2 panel groups
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+
+ // Load 3 panels to the dashboard, one with an empty result
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedEmptyResult,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayloadCoresTotal,
+ );
+
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+}
+
+describe('Dashboard', () => {
+ let DashboardComponent;
+ let mock;
+ let store;
+ let component;
+ let wrapper;
+
+ beforeEach(() => {
+ setFixtures(`
+ <div class="prometheus-graphs"></div>
+ <div class="layout-page"></div>
+ `);
+
+ store = createStore();
+ mock = new MockAdapter(axios);
+ DashboardComponent = localVue.extend(Dashboard);
+ });
+
+ afterEach(() => {
+ if (component) {
+ component.$destroy();
+ }
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ describe('responds to window resizes', () => {
+ let promPanel;
+ let promGroup;
+ let panelToggle;
+ let chart;
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: true,
+ },
+ store,
+ });
+
+ setupComponentStore(component);
+
+ return Vue.nextTick().then(() => {
+ [, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
+ promGroup = promPanel.querySelector('.prometheus-graph-group');
+ panelToggle = promPanel.querySelector('.js-graph-group-toggle');
+ chart = promGroup.querySelector('.position-relative svg');
+ });
+ });
+
+ it('setting chart size to zero when panel group is hidden', () => {
+ expect(promGroup.style.display).toBe('');
+ expect(chart.clientWidth).toBeGreaterThan(0);
+
+ panelToggle.click();
+ return Vue.nextTick().then(() => {
+ expect(promGroup.style.display).toBe('none');
+ expect(chart.clientWidth).toBe(0);
+ promPanel.style.width = '500px';
+ });
+ });
+
+ it('expanding chart panel group after resize displays chart', () => {
+ panelToggle.click();
+
+ expect(chart.clientWidth).toBeGreaterThan(0);
+ });
+ });
+});
diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js
deleted file mode 100644
index b29bac21820..00000000000
--- a/spec/javascripts/monitoring/components/dashboard_spec.js
+++ /dev/null
@@ -1,729 +0,0 @@
-import Vue from 'vue';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { GlToast } from '@gitlab/ui';
-import VueDraggable from 'vuedraggable';
-import MockAdapter from 'axios-mock-adapter';
-import Dashboard from '~/monitoring/components/dashboard.vue';
-import { metricStates } from '~/monitoring/constants';
-import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
-import * as types from '~/monitoring/stores/mutation_types';
-import { createStore } from '~/monitoring/stores';
-import axios from '~/lib/utils/axios_utils';
-import {
- metricsGroupsAPIResponse,
- mockedEmptyResult,
- mockedQueryResultPayload,
- mockedQueryResultPayloadCoresTotal,
- mockApiEndpoint,
- environmentData,
- dashboardGitResponse,
-} from '../mock_data';
-
-const localVue = createLocalVue();
-const propsData = {
- hasMetrics: false,
- documentationPath: '/path/to/docs',
- settingsPath: '/path/to/settings',
- clustersPath: '/path/to/clusters',
- tagsPath: '/path/to/tags',
- projectPath: '/path/to/project',
- metricsEndpoint: mockApiEndpoint,
- deploymentsEndpoint: null,
- emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
- emptyLoadingSvgPath: '/path/to/loading.svg',
- emptyNoDataSvgPath: '/path/to/no-data.svg',
- emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
- emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
- environmentsEndpoint: '/root/hello-prometheus/environments/35',
- currentEnvironmentName: 'production',
- customMetricsAvailable: false,
- customMetricsPath: '',
- validateQueryPath: '',
-};
-
-const resetSpy = spy => {
- if (spy) {
- spy.calls.reset();
- }
-};
-
-let expectedPanelCount;
-
-function setupComponentStore(component) {
- // Load 2 panel groups
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
- metricsGroupsAPIResponse,
- );
-
- // Load 3 panels to the dashboard, one with an empty result
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedEmptyResult,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayload,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayloadCoresTotal,
- );
-
- expectedPanelCount = 2;
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-}
-
-describe('Dashboard', () => {
- let DashboardComponent;
- let mock;
- let store;
- let component;
- let wrapper;
-
- const createComponentWrapper = (props = {}, options = {}) => {
- wrapper = shallowMount(localVue.extend(DashboardComponent), {
- localVue,
- sync: false,
- propsData: { ...propsData, ...props },
- store,
- ...options,
- });
- };
-
- beforeEach(() => {
- setFixtures(`
- <div class="prometheus-graphs"></div>
- <div class="layout-page"></div>
- `);
-
- store = createStore();
- mock = new MockAdapter(axios);
- DashboardComponent = localVue.extend(Dashboard);
- });
-
- afterEach(() => {
- if (component) {
- component.$destroy();
- }
- if (wrapper) {
- wrapper.destroy();
- }
- mock.restore();
- });
-
- describe('no metrics are available yet', () => {
- beforeEach(() => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData },
- store,
- });
- });
-
- it('shows a getting started empty state when no metrics are present', () => {
- expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
- expect(component.emptyState).toEqual('gettingStarted');
- });
-
- it('shows the environment selector', () => {
- expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
- });
- });
-
- describe('no data found', () => {
- it('shows the environment selector dropdown', () => {
- createComponentWrapper();
-
- expect(wrapper.find('.js-environments-dropdown').exists()).toBeTruthy();
- });
- });
-
- describe('cluster health', () => {
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true });
-
- // all_dashboards is not defined in health dashboards
- wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders correctly', () => {
- expect(wrapper.isVueInstance()).toBe(true);
- expect(wrapper.exists()).toBe(true);
- });
- });
-
- describe('requests information to the server', () => {
- let spy;
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- afterEach(() => {
- resetSpy(spy);
- });
-
- it('shows up a loading state', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- });
-
- Vue.nextTick(() => {
- expect(component.emptyState).toEqual('loading');
- done();
- });
- });
-
- it('hides the group panels when showPanels is false', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- expect(component.showEmptyState).toEqual(false);
- expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
- expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
- });
-
- describe('when all the requests have been commited by the store', () => {
- beforeEach(() => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- },
- store,
- });
-
- setupComponentStore(component);
- });
-
- it('renders the environments dropdown with a number of environments', done => {
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(component.environments.length).toEqual(environmentData.length);
- expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
-
- Array.from(dropdownMenuEnvironments).forEach((value, index) => {
- if (environmentData[index].metrics_path) {
- expect(value).toHaveAttr('href', environmentData[index].metrics_path);
- }
- });
-
- done();
- })
- .catch(done.fail);
- });
-
- it('renders the environments dropdown with a single active element', done => {
- Vue.nextTick()
- .then(() => {
- const dropdownItems = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item.active',
- );
-
- expect(dropdownItems.length).toEqual(1);
- done();
- })
- .catch(done.fail);
- });
- });
-
- it('hides the environments dropdown list when there is no environments', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- },
- store,
- });
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
- metricsGroupsAPIResponse,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayload,
- );
-
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(dropdownMenuEnvironments.length).toEqual(0);
- done();
- })
- .catch(done.fail);
- });
-
- it('renders the datetimepicker dropdown', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull();
- done();
- })
- .catch(done.fail);
- });
-
- it('fetches the metrics data with proper time window', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- spyOn(component.$store, 'dispatch').and.stub();
- const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-
- component.$mount();
-
- Vue.nextTick()
- .then(() => {
- expect(component.$store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalled();
-
- done();
- })
- .catch(done.fail);
- });
-
- it('shows a specific time window selected from the url params', done => {
- const start = '2019-10-01T18:27:47.000Z';
- const end = '2019-10-01T18:57:47.000Z';
- spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
- start,
- end,
- });
- spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
- if (param === 'start') return [start];
- if (param === 'end') return [end];
- return [];
- });
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- sync: false,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- const selectedTimeWindow = component.$el.querySelector(
- '.js-time-window-dropdown .active',
- );
-
- expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
- done();
- })
- .catch(done.fail);
- });
-
- it('shows an error message if invalid url parameters are passed', done => {
- spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([
- '<script>alert("XSS")</script>',
- ]);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- });
-
- spy = spyOn(component, 'showInvalidDateError');
- component.$mount();
-
- component.$nextTick(() => {
- expect(component.showInvalidDateError).toHaveBeenCalled();
- done();
- });
- });
- });
-
- describe('when one of the metrics is missing', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true });
- setupComponentStore(wrapper.vm);
-
- wrapper.vm.$nextTick(done);
- });
-
- it('shows a group empty area', () => {
- const emptyGroup = wrapper.findAll({ ref: 'empty-group' });
-
- expect(emptyGroup).toHaveLength(1);
- expect(emptyGroup.is(GroupEmptyState)).toBe(true);
- });
-
- it('group empty area displays a NO_DATA state', () => {
- expect(
- wrapper
- .findAll({ ref: 'empty-group' })
- .at(0)
- .props('selectedState'),
- ).toEqual(metricStates.NO_DATA);
- });
- });
-
- describe('drag and drop function', () => {
- const findDraggables = () => wrapper.findAll(VueDraggable);
- const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled'));
- const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
- const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
-
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true }, { attachToDocument: true });
-
- setupComponentStore(wrapper.vm);
-
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('wraps vuedraggable', () => {
- expect(findDraggablePanels().exists()).toBe(true);
- expect(findDraggablePanels().length).toEqual(expectedPanelCount);
- });
-
- it('is disabled by default', () => {
- expect(findRearrangeButton().exists()).toBe(false);
- expect(findEnabledDraggables().length).toBe(0);
- });
-
- describe('when rearrange is enabled', () => {
- beforeEach(done => {
- wrapper.setProps({ rearrangePanelsAvailable: true });
- wrapper.vm.$nextTick(done);
- });
-
- it('displays rearrange button', () => {
- expect(findRearrangeButton().exists()).toBe(true);
- });
-
- describe('when rearrange button is clicked', () => {
- const findFirstDraggableRemoveButton = () =>
- findDraggablePanels()
- .at(0)
- .find('.js-draggable-remove');
-
- beforeEach(done => {
- findRearrangeButton().vm.$emit('click');
- wrapper.vm.$nextTick(done);
- });
-
- it('it enables draggables', () => {
- expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
- expect(findEnabledDraggables()).toEqual(findDraggables());
- });
-
- it('metrics can be swapped', done => {
- const firstDraggable = findDraggables().at(0);
- const mockMetrics = [...metricsGroupsAPIResponse[1].panels];
-
- const firstTitle = mockMetrics[0].title;
- const secondTitle = mockMetrics[1].title;
-
- // swap two elements and `input` them
- [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]];
- firstDraggable.vm.$emit('input', mockMetrics);
-
- wrapper.vm.$nextTick(() => {
- const { panels } = wrapper.vm.dashboard.panel_groups[1];
-
- expect(panels[1].title).toEqual(firstTitle);
- expect(panels[0].title).toEqual(secondTitle);
- done();
- });
- });
-
- it('shows a remove button, which removes a panel', done => {
- expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
-
- expect(findDraggablePanels().length).toEqual(expectedPanelCount);
- findFirstDraggableRemoveButton().trigger('click');
-
- wrapper.vm.$nextTick(() => {
- expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
- done();
- });
- });
-
- it('it disables draggables when clicked again', done => {
- findRearrangeButton().vm.$emit('click');
- wrapper.vm.$nextTick(() => {
- expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
- expect(findEnabledDraggables().length).toBe(0);
- done();
- });
- });
- });
- });
- });
-
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
- // eslint-disable-next-line jasmine/no-disabled-tests
- xdescribe('link to chart', () => {
- const currentDashboard = 'TEST_DASHBOARD';
- localVue.use(GlToast);
- const link = () => wrapper.find('.js-chart-link');
- const clipboardText = () => link().element.dataset.clipboardText;
-
- beforeEach(done => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- createComponentWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
-
- setTimeout(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('adds a copy button to the dropdown', () => {
- expect(link().text()).toContain('Generate link to chart');
- });
-
- it('contains a link to the dashboard', () => {
- expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
- expect(clipboardText()).toContain(`group=`);
- expect(clipboardText()).toContain(`title=`);
- expect(clipboardText()).toContain(`y_label=`);
- });
-
- it('undefined parameter is stripped', done => {
- wrapper.setProps({ currentDashboard: undefined });
-
- wrapper.vm.$nextTick(() => {
- expect(clipboardText()).not.toContain(`dashboard=`);
- expect(clipboardText()).toContain(`y_label=`);
- done();
- });
- });
-
- it('null parameter is stripped', done => {
- wrapper.setProps({ currentDashboard: null });
-
- wrapper.vm.$nextTick(() => {
- expect(clipboardText()).not.toContain(`dashboard=`);
- expect(clipboardText()).toContain(`y_label=`);
- done();
- });
- });
-
- it('creates a toast when clicked', () => {
- spyOn(wrapper.vm.$toast, 'show').and.stub();
-
- link().vm.$emit('click');
-
- expect(wrapper.vm.$toast.show).toHaveBeenCalled();
- });
- });
-
- describe('responds to window resizes', () => {
- let promPanel;
- let promGroup;
- let panelToggle;
- let chart;
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: true,
- },
- store,
- });
-
- setupComponentStore(component);
-
- return Vue.nextTick().then(() => {
- [, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
- promGroup = promPanel.querySelector('.prometheus-graph-group');
- panelToggle = promPanel.querySelector('.js-graph-group-toggle');
- chart = promGroup.querySelector('.position-relative svg');
- });
- });
-
- it('setting chart size to zero when panel group is hidden', () => {
- expect(promGroup.style.display).toBe('');
- expect(chart.clientWidth).toBeGreaterThan(0);
-
- panelToggle.click();
- return Vue.nextTick().then(() => {
- expect(promGroup.style.display).toBe('none');
- expect(chart.clientWidth).toBe(0);
- promPanel.style.width = '500px';
- });
- });
-
- it('expanding chart panel group after resize displays chart', () => {
- panelToggle.click();
-
- expect(chart.clientWidth).toBeGreaterThan(0);
- });
- });
-
- describe('dashboard edit link', () => {
- const findEditLink = () => wrapper.find('.js-edit-link');
-
- beforeEach(done => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- createComponentWrapper({ hasMetrics: true }, { attachToDocument: true });
-
- wrapper.vm.$store.commit(
- `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
- dashboardGitResponse,
- );
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('is not present for the default dashboard', () => {
- expect(findEditLink().exists()).toBe(false);
- });
-
- it('is present for a custom dashboard, and links to its edit_path', done => {
- const dashboard = dashboardGitResponse[1]; // non-default dashboard
- const currentDashboard = dashboard.path;
-
- wrapper.setProps({ currentDashboard });
- wrapper.vm.$nextTick(() => {
- expect(findEditLink().exists()).toBe(true);
- expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
- done();
- });
- });
- });
-
- describe('external dashboard link', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- showTimeWindowDropdown: false,
- externalDashboardUrl: '/mockUrl',
- },
- store,
- });
- });
-
- it('shows the link', done => {
- setTimeout(() => {
- expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
- 'View full dashboard',
- );
- done();
- });
- });
- });
-
- describe('Dashboard dropdown', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- component.$store.commit(
- `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
- dashboardGitResponse,
- );
- });
-
- it('shows the dashboard dropdown', done => {
- setTimeout(() => {
- const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
-
- expect(dashboardDropdown).not.toEqual(null);
- done();
- });
- });
- });
-});
diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb
index 394efa85701..1b8ec2b1979 100644
--- a/spec/lib/expand_variables_spec.rb
+++ b/spec/lib/expand_variables_spec.rb
@@ -100,7 +100,7 @@ describe ExpandVariables do
end
with_them do
- subject { ExpandVariables.expand(value, variables) } # rubocop:disable RSpec/DescribedClass
+ subject { ExpandVariables.expand(value, variables) }
it { is_expected.to eq(result) }
end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 38ec04ebe81..ba5b70b44de 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -481,7 +481,6 @@ module Gitlab
['../sample.adoc', 'doc/sample.adoc', 'relative path to a file up one directory'],
['../../sample.adoc', 'sample.adoc', 'relative path for a file up multiple directories']
].each do |include_path_, file_path_, desc|
-
context "the file is specified by #{desc}" do
let(:include_path) { include_path_ }
let(:file_path) { file_path_ }
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 4fa0a57dc82..f7b14360af3 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -31,13 +31,13 @@ describe Gitlab::Ci::Config::Entry::Cache do
it_behaves_like 'hash key value'
context 'with files' do
- let(:key) { { files: ['a-file', 'other-file'] } }
+ let(:key) { { files: %w[a-file other-file] } }
it_behaves_like 'hash key value'
end
context 'with files and prefix' do
- let(:key) { { files: ['a-file', 'other-file'], prefix: 'prefix-value' } }
+ let(:key) { { files: %w[a-file other-file], prefix: 'prefix-value' } }
it_behaves_like 'hash key value'
end
@@ -55,7 +55,7 @@ describe Gitlab::Ci::Config::Entry::Cache do
it { is_expected.to be_valid }
context 'with files' do
- let(:key) { { files: ['a-file', 'other-file'] } }
+ let(:key) { { files: %w[a-file other-file] } }
it { is_expected.to be_valid }
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index b09258ae227..56767c21ab7 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -6,12 +6,12 @@ shared_examples 'renames child namespaces' do |type|
it 'renames namespaces' do
rename_namespaces = double
expect(described_class::RenameNamespaces)
- .to receive(:new).with(['first-path', 'second-path'], subject)
+ .to receive(:new).with(%w[first-path second-path], subject)
.and_return(rename_namespaces)
expect(rename_namespaces).to receive(:rename_namespaces)
.with(type: :child)
- subject.rename_wildcard_paths(['first-path', 'second-path'])
+ subject.rename_wildcard_paths(%w[first-path second-path])
end
end
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index b8be72cf8d7..e4624accd58 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::Experimentation do
describe '#experiment_enabled?' do
context 'cookie is not present' do
it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of nil' do
- expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, nil) # rubocop:disable RSpec/DescribedClass
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, nil)
controller.experiment_enabled?(:test_experiment)
end
end
@@ -67,7 +67,7 @@ describe Gitlab::Experimentation do
it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
# 'abcd1234'.hex % 100 = 76
- expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, 76) # rubocop:disable RSpec/DescribedClass
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, 76)
controller.experiment_enabled?(:test_experiment)
end
end
diff --git a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
index 887a6baf659..fc6ac491671 100644
--- a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::GitalyClient::BlobService do
describe '#get_new_lfs_pointers' do
let(:revision) { 'master' }
let(:limit) { 5 }
- let(:not_in) { ['branch-a', 'branch-b'] }
+ let(:not_in) { %w[branch-a branch-b] }
let(:expected_params) do
{ revision: revision, limit: limit, not_in_refs: not_in, not_in_all: false }
end
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
index 929ff5dee5d..73ae4cd95ce 100644
--- a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -69,7 +69,7 @@ describe Gitlab::GitalyClient::RemoteService do
describe '#update_remote_mirror' do
let(:ref_name) { 'remote_mirror_1' }
- let(:only_branches_matching) { ['my-branch', 'master'] }
+ let(:only_branches_matching) { %w[my-branch master] }
let(:ssh_key) { 'KEY' }
let(:known_hosts) { 'KNOWN HOSTS' }
diff --git a/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
index e88eec2c393..f3928f390bc 100644
--- a/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
@@ -15,13 +15,13 @@ describe Gitlab::PhabricatorImport::Conduit::User do
it 'calls the api with the correct params' do
expected_params = {
- constraints: { phids: ['phid-1', 'phid-2'] }
+ constraints: { phids: %w[phid-1 phid-2] }
}
expect(fake_client).to receive(:get).with('user.search',
params: expected_params)
- user_client.users(['phid-1', 'phid-2'])
+ user_client.users(%w[phid-1 phid-2])
end
it 'returns an array of parsed responses' do
@@ -43,7 +43,7 @@ describe Gitlab::PhabricatorImport::Conduit::User do
expect(fake_client).to receive(:get).with('user.search',
params: second_params).once
- user_client.users(['phid-1', 'phid-2'])
+ user_client.users(%w[phid-1 phid-2])
end
end
end
diff --git a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
index 14a00deeb16..f260e38b7c8 100644
--- a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::PhabricatorImport::UserFinder, :clean_gitlab_redis_cache do
let(:project) { create(:project, namespace: create(:group)) }
- subject(:finder) { described_class.new(project, ['first-phid', 'second-phid']) }
+ subject(:finder) { described_class.new(project, %w[first-phid second-phid]) }
before do
project.namespace.add_developer(existing_user)
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index c98c36622f5..1145a7edc85 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::QuickActions::Dsl do
before :all do
DummyClass = Struct.new(:project) do
- include Gitlab::QuickActions::Dsl # rubocop:disable RSpec/DescribedClass
+ include Gitlab::QuickActions::Dsl
desc 'A command with no args'
command :no_args, :none do
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
index 795aa43b849..8d199fe3531 100644
--- a/spec/lib/quality/helm_client_spec.rb
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Quality::HelmClient do
end
context 'with multiple release names' do
- let(:release_name) { ['my-release', 'my-release-2'] }
+ let(:release_name) { %w[my-release my-release-2] }
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 59d4a977d5e..6a62ef456c1 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Quality::KubernetesClient do
end
context 'with multiple releases' do
- let(:release_name) { ['my-release', 'my-release-2'] }
+ let(:release_name) { %w[my-release my-release-2] }
it 'raises an error if the Kubernetes command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
diff --git a/spec/migrations/fix_max_pages_size_spec.rb b/spec/migrations/fix_max_pages_size_spec.rb
new file mode 100644
index 00000000000..36b5445603e
--- /dev/null
+++ b/spec/migrations/fix_max_pages_size_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20191213120427_fix_max_pages_size.rb')
+
+describe FixMaxPagesSize, :migration do
+ let(:application_settings) { table(:application_settings) }
+ let!(:default_setting) { application_settings.create! }
+ let!(:max_possible_setting) { application_settings.create!(max_pages_size: described_class::MAX_SIZE) }
+ let!(:higher_than_maximum_setting) { application_settings.create!(max_pages_size: described_class::MAX_SIZE + 1) }
+
+ it 'correctly updates settings only if needed' do
+ migrate!
+
+ expect(default_setting.reload.max_pages_size).to eq(100)
+ expect(max_possible_setting.reload.max_pages_size).to eq(described_class::MAX_SIZE)
+ expect(higher_than_maximum_setting.reload.max_pages_size).to eq(described_class::MAX_SIZE)
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index 072d0fa86e5..6930f743c2f 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -139,7 +139,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
redis = double(:redis)
expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
- sessions = ['session-a', 'session-b']
+ sessions = %w[session-a session-b]
mget_responses = sessions.map { |session| [Marshal.dump(session)]}
expect(redis).to receive(:mget).twice.and_return(*mget_responses)
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index a403aa296d4..52e60a69a52 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -67,6 +67,13 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:push_event_activities_limit) }
it { is_expected.to validate_numericality_of(:snippet_size_limit).only_integer.is_greater_than(0) }
+ it { is_expected.to validate_presence_of(:max_artifacts_size) }
+ it do
+ is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than(0)
+ .is_less_than(::Gitlab::Pages::MAX_SIZE / 1.megabyte)
+ end
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
+ it { is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than(0) }
it { is_expected.not_to allow_value(7).for(:minimum_password_length) }
it { is_expected.not_to allow_value(129).for(:minimum_password_length) }
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 0f829e138d5..e7f5f493b82 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -130,7 +130,7 @@ describe Clusters::Applications::Prometheus do
it 'is initialized with 3 arguments' do
expect(subject.name).to eq('prometheus')
expect(subject.chart).to eq('stable/prometheus')
- expect(subject.version).to eq('6.7.3')
+ expect(subject.version).to eq('9.5.2')
expect(subject).to be_rbac
expect(subject.files).to eq(prometheus.files)
end
@@ -147,7 +147,7 @@ describe Clusters::Applications::Prometheus do
let(:prometheus) { create(:clusters_applications_prometheus, :errored, version: '2.0.0') }
it 'is initialized with the locked version' do
- expect(subject.version).to eq('6.7.3')
+ expect(subject.version).to eq('9.5.2')
end
end
@@ -218,7 +218,7 @@ describe Clusters::Applications::Prometheus do
it 'is initialized with 3 arguments' do
expect(patch_command.name).to eq('prometheus')
expect(patch_command.chart).to eq('stable/prometheus')
- expect(patch_command.version).to eq('6.7.3')
+ expect(patch_command.version).to eq('9.5.2')
expect(patch_command.files).to eq(prometheus.files)
end
end
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index ef426661066..5b8be7914d4 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -138,8 +138,6 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: 'error message',
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
end
end
@@ -159,8 +157,6 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: 'Sentry API response is missing keys. key not found: "id"',
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
end
end
@@ -181,8 +177,21 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: error_msg,
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_INVALID_SIZE
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
+ end
+ end
+
+ context 'when sentry client raises StandardError' do
+ let(:sentry_client) { spy(:sentry_client) }
+
+ before do
+ synchronous_reactive_cache(subject)
+
+ allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:list_issues).with(opts).and_raise(StandardError)
+ end
+
+ it 'returns error' do
+ expect(result).to eq(error: 'Unexpected Error')
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c98d123ff52..0e151475128 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -384,7 +384,7 @@ describe MergeRequest do
end
it 'returns target branches sort by updated at desc' do
- expect(described_class.recent_target_branches).to match_array(['feature', 'merge-test', 'fix'])
+ expect(described_class.recent_target_branches).to match_array(%w[feature merge-test fix])
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 2ba0d97792b..b732412c52c 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -26,6 +26,7 @@ describe Namespace do
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_length_of(:path).is_at_most(255) }
it { is_expected.to validate_presence_of(:owner) }
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
it 'does not allow too deep nesting' do
ancestors = (1..21).to_a
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e66f37f2eec..31dc0134410 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -211,6 +211,7 @@ describe Project do
it { is_expected.to validate_presence_of(:creator) }
it { is_expected.to validate_presence_of(:namespace) }
it { is_expected.to validate_presence_of(:repository_storage) }
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
it 'validates build timeout constraints' do
is_expected.to validate_numericality_of(:build_timeout)
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 310caa92eb9..2e0b7a30480 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -115,7 +115,7 @@ describe API::Wikis do
end
[:title, :content, :format].each do |part|
- it "it updates with wiki with missing #{part}" do
+ it "updates with wiki with missing #{part}" do
payload.delete(part)
put(api(url, user), params: payload)
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index b80f75c70e6..5440a42348e 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -2707,7 +2707,7 @@ describe NotificationService, :mailer do
# User to be participant by default
# This user does not contain any record in notification settings table
# It should be treated with a :participating notification_level
- @u_lazy_participant = create(:user, username: 'lazy-participant')
+ @u_lazy_participant = create(:user, username: 'lazy-participant')
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index fe92b53cd91..9aa8c7f85ca 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -185,60 +185,20 @@ describe Projects::UpdatePagesService do
.and_return(metadata)
end
- shared_examples 'pages size limit exceeded' do
- it 'limits the maximum size of gitlab pages' do
- subject.execute
-
- expect(deploy_status.description)
- .to match(/artifacts for pages are too large/)
- expect(deploy_status).to be_script_failure
- expect(project.pages_metadatum).not_to be_deployed
- end
- end
-
context 'when maximum pages size is set to zero' do
before do
stub_application_setting(max_pages_size: 0)
end
- context 'when page size does not exceed internal maximum' do
- before do
- allow(metadata).to receive(:total_size).and_return(200.megabytes)
- end
-
- it 'updates pages correctly' do
- subject.execute
-
- expect(deploy_status.description).not_to be_present
- expect(project.pages_metadatum).to be_deployed
- end
- end
-
- context 'when pages size does exceed internal maximum' do
- before do
- allow(metadata).to receive(:total_size).and_return(2.terabytes)
- end
-
- it_behaves_like 'pages size limit exceeded'
- end
- end
-
- context 'when pages size is greater than max size setting' do
- before do
- stub_application_setting(max_pages_size: 200)
- allow(metadata).to receive(:total_size).and_return(201.megabytes)
- end
-
- it_behaves_like 'pages size limit exceeded'
+ it_behaves_like 'pages size limit is', ::Gitlab::Pages::MAX_SIZE
end
- context 'when max size setting is greater than internal max size' do
+ context 'when size is limited on the instance level' do
before do
- stub_application_setting(max_pages_size: 3.terabytes / 1.megabyte)
- allow(metadata).to receive(:total_size).and_return(2.terabytes)
+ stub_application_setting(max_pages_size: 100)
end
- it_behaves_like 'pages size limit exceeded'
+ it_behaves_like 'pages size limit is', 100.megabytes
end
end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index f070243f111..ea13e91860a 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -297,11 +297,11 @@ shared_examples 'thread comments' do |resource_name|
find("#{form_selector} .note-textarea").send_keys('a')
end
- it "should show a 'Comment & reopen #{resource_name}' button" do
+ it "shows a 'Comment & reopen #{resource_name}' button" do
expect(find("#{form_selector} .js-note-target-reopen")).to have_content "Comment & reopen #{resource_name}"
end
- it "should show a 'Start thread & reopen #{resource_name}' button when 'Start thread' is selected" do
+ it "shows a 'Start thread & reopen #{resource_name}' button when 'Start thread' is selected" do
find(toggle_selector).click
find("#{menu_selector} li", match: :first)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 6a23875f103..f2fa6af6402 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -154,7 +154,6 @@ module TestEnv
install_dir: gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
-
Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
start_gitaly(gitaly_dir)
end
diff --git a/spec/support/shared_examples/pages_size_limit_shared_examples.rb b/spec/support/shared_examples/pages_size_limit_shared_examples.rb
new file mode 100644
index 00000000000..c1e27194738
--- /dev/null
+++ b/spec/support/shared_examples/pages_size_limit_shared_examples.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+shared_examples 'pages size limit is' do |size_limit|
+ context "when size is below the limit" do
+ before do
+ allow(metadata).to receive(:total_size).and_return(size_limit - 1.megabyte)
+ end
+
+ it 'updates pages correctly' do
+ subject.execute
+
+ expect(deploy_status.description).not_to be_present
+ expect(project.pages_metadatum).to be_deployed
+ end
+ end
+
+ context "when size is above the limit" do
+ before do
+ allow(metadata).to receive(:total_size).and_return(size_limit + 1.megabyte)
+ end
+
+ it 'limits the maximum size of gitlab pages' do
+ subject.execute
+
+ expect(deploy_status.description)
+ .to match(/artifacts for pages are too large/)
+ expect(deploy_status).to be_script_failure
+ end
+ end
+end
diff --git a/spec/views/projects/commit/branches.html.haml_spec.rb b/spec/views/projects/commit/branches.html.haml_spec.rb
index 36da489a84f..0fe7165a790 100644
--- a/spec/views/projects/commit/branches.html.haml_spec.rb
+++ b/spec/views/projects/commit/branches.html.haml_spec.rb
@@ -11,7 +11,7 @@ describe 'projects/commit/branches.html.haml' do
context 'when branches and tags are available' do
before do
- assign(:branches, ['master', 'test-branch'])
+ assign(:branches, %w[master test-branch])
assign(:branches_limit_exceeded, false)
assign(:tags, ['tag1'])
assign(:tags_limit_exceeded, false)
@@ -35,7 +35,7 @@ describe 'projects/commit/branches.html.haml' do
context 'when branches are available but no tags' do
before do
- assign(:branches, ['master', 'test-branch'])
+ assign(:branches, %w[master test-branch])
assign(:branches_limit_exceeded, false)
assign(:tags, [])
assign(:tags_limit_exceeded, true)