summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorStan Hu <stanhu@gmail.com>2019-08-09 10:22:22 -0700
committerStan Hu <stanhu@gmail.com>2019-08-09 10:22:22 -0700
commit916f255bed04cc7ec68d2df26527b862f0ea73e8 (patch)
treebf0a2839600b927b42f9585b37b66fbce67f2cef /spec
parentf77ffdafad0a94c8a2af7070710fbfbe88994040 (diff)
parentfe214a217ca95c2c4a53c501f372367fecaf7f99 (diff)
downloadgitlab-ce-916f255bed04cc7ec68d2df26527b862f0ea73e8.tar.gz
Merge branch 'master' into sh-break-out-invited-group-members
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/metrics_controller_spec.rb9
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb46
-rw-r--r--spec/controllers/projects/starrers_controller_spec.rb110
-rw-r--r--spec/controllers/projects/variables_controller_spec.rb65
-rw-r--r--spec/factories/projects.rb4
-rw-r--r--spec/factories/services.rb12
-rw-r--r--spec/features/admin/admin_browses_logs_spec.rb2
-rw-r--r--spec/features/groups/members/search_members_spec.rb4
-rw-r--r--spec/features/groups/members/sort_members_spec.rb18
-rw-r--r--spec/features/project_variables_spec.rb23
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb14
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb8
-rw-r--r--spec/features/projects/members/sorting_spec.rb18
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb69
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb2
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb2
-rw-r--r--spec/features/user_opens_link_to_comment_spec.rb5
-rw-r--r--spec/finders/issues_finder_spec.rb4
-rw-r--r--spec/finders/starred_projects_finder_spec.rb41
-rw-r--r--spec/finders/users_star_projects_finder_spec.rb42
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/group_labels.json19
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/label.json11
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json16
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/project_label.json15
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json9
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json3
-rw-r--r--spec/frontend/fixtures/projects.rb2
-rw-r--r--spec/frontend/lib/utils/forms_spec.js74
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js35
-rw-r--r--spec/frontend/tracking_spec.js7
-rw-r--r--spec/helpers/users_helper_spec.rb2
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js2
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js2
-rw-r--r--spec/javascripts/jobs/components/empty_state_spec.js (renamed from spec/frontend/jobs/components/empty_state_spec.js)8
-rw-r--r--spec/javascripts/jobs/components/job_log_spec.js20
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js16
-rw-r--r--spec/javascripts/monitoring/store/actions_spec.js4
-rw-r--r--spec/javascripts/monitoring/utils_spec.js28
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js27
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb33
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb4
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb10
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_importer_spec.rb7
-rw-r--r--spec/lib/gitlab/highlight_spec.rb8
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/metrics/dashboard/defaults_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/dashboard/finder_spec.rb76
-rw-r--r--spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb80
-rw-r--r--spec/lib/gitlab/regex_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb28
-rw-r--r--spec/lib/gitlab/usage_data_counters/note_counter_spec.rb78
-rw-r--r--spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb12
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb3
-rw-r--r--spec/models/ci/build_spec.rb26
-rw-r--r--spec/models/ci/variable_spec.rb1
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb45
-rw-r--r--spec/models/concerns/has_environment_scope_spec.rb66
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb167
-rw-r--r--spec/models/project_spec.rb93
-rw-r--r--spec/models/prometheus_metric_spec.rb13
-rw-r--r--spec/presenters/blob_presenter_spec.rb52
-rw-r--r--spec/requests/api/group_labels_spec.rb15
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb2
-rw-r--r--spec/requests/api/labels_spec.rb111
-rw-r--r--spec/requests/api/projects_spec.rb101
-rw-r--r--spec/requests/api/services_spec.rb10
-rw-r--r--spec/requests/api/variables_spec.rb24
-rw-r--r--spec/serializers/variable_entity_spec.rb2
-rw-r--r--spec/services/create_snippet_service_spec.rb16
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb319
-rw-r--r--spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb145
-rw-r--r--spec/services/metrics/dashboard/dynamic_embed_service_spec.rb151
-rw-r--r--spec/services/notes/create_service_spec.rb38
-rw-r--r--spec/services/projects/destroy_service_spec.rb12
-rw-r--r--spec/services/self_monitoring/project/create_service_spec.rb71
-rw-r--r--spec/services/update_snippet_service_spec.rb17
-rw-r--r--spec/support/helpers/metrics_dashboard_helpers.rb8
-rw-r--r--spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/relative_positioning_shared_examples.rb6
-rw-r--r--spec/views/search/_filter.html.haml_spec.rb17
-rw-r--r--spec/views/search/_form.html.haml_spec.rb14
-rw-r--r--spec/views/search/show.html.haml_spec.rb37
84 files changed, 2167 insertions, 593 deletions
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 84027119491..7fb3578cd0a 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -5,12 +5,19 @@ require 'spec_helper'
describe MetricsController do
include StubENV
- let(:metrics_multiproc_dir) { Dir.mktmpdir }
+ let(:metrics_multiproc_dir) { @metrics_multiproc_dir }
let(:whitelisted_ip) { '127.0.0.1' }
let(:whitelisted_ip_range) { '10.0.0.0/24' }
let(:ip_in_whitelisted_range) { '10.0.0.1' }
let(:not_whitelisted_ip) { '10.0.1.1' }
+ around do |example|
+ Dir.mktmpdir do |path|
+ @metrics_multiproc_dir = path
+ example.run
+ end
+ end
+
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
allow(Prometheus::Client.configuration).to receive(:multiprocess_files_dir).and_return(metrics_multiproc_dir)
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 8872e8d38e7..b3852355d77 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -518,10 +518,10 @@ describe Projects::EnvironmentsController do
end
end
- shared_examples_for 'the default dynamic dashboard' do
+ shared_examples_for 'specified dashboard embed' do |expected_titles|
it_behaves_like '200 response'
- it 'contains only the Memory and CPU charts' do
+ it 'contains only the specified charts' do
get :metrics_dashboard, params: environment_params(dashboard_params)
dashboard = json_response['dashboard']
@@ -531,10 +531,14 @@ describe Projects::EnvironmentsController do
expect(dashboard['dashboard']).to be_nil
expect(dashboard['panel_groups'].length).to eq 1
expect(panel_group['group']).to be_nil
- expect(titles).to eq ['Memory Usage (Total)', 'Core Usage (Total)']
+ expect(titles).to eq expected_titles
end
end
+ shared_examples_for 'the default dynamic dashboard' do
+ it_behaves_like 'specified dashboard embed', ['Memory Usage (Total)', 'Core Usage (Total)']
+ end
+
shared_examples_for 'dashboard can be specified' do
context 'when dashboard is specified' do
let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
@@ -551,7 +555,7 @@ describe Projects::EnvironmentsController do
end
context 'when the specified dashboard is the default dashboard' do
- let(:dashboard_path) { ::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH }
+ let(:dashboard_path) { system_dashboard_path }
it_behaves_like 'the default dashboard'
end
@@ -564,12 +568,40 @@ describe Projects::EnvironmentsController do
it_behaves_like 'the default dynamic dashboard'
- context 'when the dashboard is specified' do
- let(:dashboard_params) { { format: :json, embedded: true, dashboard: '.gitlab/dashboards/fake.yml' } }
+ context 'when incomplete dashboard params are provided' do
+ let(:dashboard_params) { { format: :json, embedded: true, title: 'Title' } }
+
+ # The title param should be ignored.
+ it_behaves_like 'the default dynamic dashboard'
+ end
+
+ context 'when invalid params are provided' do
+ let(:dashboard_params) { { format: :json, embedded: true, metric_id: 16 } }
- # The dashboard param should be ignored.
+ # The superfluous param should be ignored.
it_behaves_like 'the default dynamic dashboard'
end
+
+ context 'when the dashboard is correctly specified' do
+ let(:dashboard_params) do
+ {
+ format: :json,
+ embedded: true,
+ dashboard: system_dashboard_path,
+ group: business_metric_title,
+ title: 'title',
+ y_label: 'y_label'
+ }
+ end
+
+ it_behaves_like 'error response', :not_found
+
+ context 'and exists' do
+ let!(:metric) { create(:prometheus_metric, project: project) }
+
+ it_behaves_like 'specified dashboard embed', ['title']
+ end
+ end
end
end
diff --git a/spec/controllers/projects/starrers_controller_spec.rb b/spec/controllers/projects/starrers_controller_spec.rb
new file mode 100644
index 00000000000..59d258e99ce
--- /dev/null
+++ b/spec/controllers/projects/starrers_controller_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::StarrersController do
+ let(:user) { create(:user) }
+ let(:private_user) { create(:user, private_profile: true) }
+ let(:admin) { create(:user, admin: true) }
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ user.toggle_star(project)
+ private_user.toggle_star(project)
+ end
+
+ describe 'GET index' do
+ def get_starrers
+ get :index,
+ params: {
+ namespace_id: project.namespace,
+ project_id: project
+ }
+ end
+
+ context 'when project is public' do
+ before do
+ project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
+
+ context 'when no user is logged in' do
+ before do
+ get_starrers
+ end
+
+ it 'only public starrers are visible' do
+ user_ids = assigns[:starrers].map { |s| s['user_id'] }
+ expect(user_ids).to include(user.id)
+ expect(user_ids).not_to include(private_user.id)
+ end
+
+ it 'public/private starrers counts are correct' do
+ expect(assigns[:public_count]).to eq(1)
+ expect(assigns[:private_count]).to eq(1)
+ end
+ end
+
+ context 'when private user is logged in' do
+ before do
+ sign_in(private_user)
+
+ get_starrers
+ end
+
+ it 'their star is also visible' do
+ user_ids = assigns[:starrers].map { |s| s['user_id'] }
+ expect(user_ids).to include(user.id, private_user.id)
+ end
+
+ it 'public/private starrers counts are correct' do
+ expect(assigns[:public_count]).to eq(1)
+ expect(assigns[:private_count]).to eq(1)
+ end
+ end
+
+ context 'when admin is logged in' do
+ before do
+ sign_in(admin)
+
+ get_starrers
+ end
+
+ it 'all stars are visible' do
+ user_ids = assigns[:starrers].map { |s| s['user_id'] }
+ expect(user_ids).to include(user.id, private_user.id)
+ end
+
+ it 'public/private starrers counts are correct' do
+ expect(assigns[:public_count]).to eq(1)
+ expect(assigns[:private_count]).to eq(1)
+ end
+ end
+ end
+
+ context 'when project is private' do
+ before do
+ project.update(visibility_level: Project::PRIVATE)
+ end
+
+ it 'starrers are not visible for non logged in users' do
+ get_starrers
+
+ expect(assigns[:starrers]).to be_blank
+ end
+
+ context 'when user is logged in' do
+ before do
+ sign_in(project.creator)
+ end
+
+ it 'only public starrers are visible' do
+ get_starrers
+
+ user_ids = assigns[:starrers].map { |s| s['user_id'] }
+ expect(user_ids).to include(user.id)
+ expect(user_ids).not_to include(private_user.id)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb
index a2a09e2580f..21e106660d0 100644
--- a/spec/controllers/projects/variables_controller_spec.rb
+++ b/spec/controllers/projects/variables_controller_spec.rb
@@ -36,5 +36,70 @@ describe Projects::VariablesController do
end
include_examples 'PATCH #update updates variables'
+
+ context 'with environment scope' do
+ let!(:variable) { create(:ci_variable, project: project, environment_scope: 'custom_scope') }
+
+ let(:variable_attributes) do
+ { id: variable.id,
+ key: variable.key,
+ secret_value: variable.value,
+ protected: variable.protected?.to_s,
+ environment_scope: variable.environment_scope }
+ end
+ let(:new_variable_attributes) do
+ { key: 'new_key',
+ secret_value: 'dummy_value',
+ protected: 'false',
+ environment_scope: 'new_scope' }
+ end
+
+ context 'with same key and different environment scope' do
+ let(:variables_attributes) do
+ [
+ variable_attributes,
+ new_variable_attributes.merge(key: variable.key)
+ ]
+ end
+
+ it 'does not update the existing variable' do
+ expect { subject }.not_to change { variable.reload.value }
+ end
+
+ it 'creates the new variable' do
+ expect { subject }.to change { owner.variables.count }.by(1)
+ end
+
+ it 'returns a successful response including all variables' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('variables')
+ end
+ end
+
+ context 'with same key and same environment scope' do
+ let(:variables_attributes) do
+ [
+ variable_attributes,
+ new_variable_attributes.merge(key: variable.key, environment_scope: variable.environment_scope)
+ ]
+ end
+
+ it 'does not update the existing variable' do
+ expect { subject }.not_to change { variable.reload.value }
+ end
+
+ it 'does not create the new variable' do
+ expect { subject }.not_to change { owner.variables.count }
+ end
+
+ it 'returns a bad request response' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index afe27aaf1fb..ea89555b0d5 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -325,10 +325,6 @@ FactoryBot.define do
jira_service
end
- factory :kubernetes_project, parent: :project do
- kubernetes_service
- end
-
factory :mock_deployment_project, parent: :project do
mock_deployment_service
end
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 5ef39b3e818..f3e662ad4f5 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -16,18 +16,6 @@ FactoryBot.define do
)
end
- factory :kubernetes_service do
- project
- type 'KubernetesService'
- active true
- properties({
- api_url: 'https://kubernetes.example.com',
- token: 'a' * 40
- })
-
- skip_deprecation_validation true
- end
-
factory :mock_deployment_service do
project
type 'MockDeploymentService'
diff --git a/spec/features/admin/admin_browses_logs_spec.rb b/spec/features/admin/admin_browses_logs_spec.rb
index 5a2df89aeb7..2b97362c8e9 100644
--- a/spec/features/admin/admin_browses_logs_spec.rb
+++ b/spec/features/admin/admin_browses_logs_spec.rb
@@ -11,7 +11,7 @@ describe 'Admin browses logs' do
visit admin_logs_path
expect(page).to have_link 'application.log'
- expect(page).to have_link 'githost.log'
+ expect(page).to have_link 'git_json.log'
expect(page).to have_link 'test.log'
expect(page).to have_link 'sidekiq.log'
expect(page).to have_link 'repocheck.log'
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index d2d084c9174..9c17aac09e8 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -19,9 +19,9 @@ describe 'Search group member' do
end
it 'renders member users' do
- page.within '.member-search-form' do
+ page.within '.user-search-form' do
fill_in 'search', with: member.name
- find('.member-search-btn').click
+ find('.user-search-btn').click
end
group_members_list = find(".card .content-list")
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index 510e7f2f05e..76709199942 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -19,7 +19,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by access level ascending' do
@@ -27,7 +27,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
it 'sorts by access level descending' do
@@ -35,7 +35,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
it 'sorts by last joined' do
@@ -43,7 +43,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
it 'sorts by oldest joined' do
@@ -51,7 +51,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
it 'sorts by name ascending' do
@@ -59,7 +59,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by name descending' do
@@ -67,7 +67,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
@@ -75,7 +75,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
@@ -83,7 +83,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end
def visit_members_list(sort:)
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 95685a3c7ff..9e3f8a843a1 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -17,4 +17,27 @@ describe 'Project variables', :js do
end
it_behaves_like 'variable list'
+
+ it 'adds new variable with a special environment scope' do
+ page.within('.js-ci-variable-list-section .js-row:last-child') do
+ find('.js-ci-variable-input-key').set('somekey')
+ find('.js-ci-variable-input-value').set('somevalue')
+
+ find('.js-variable-environment-toggle').click
+ find('.js-variable-environment-dropdown-wrapper .dropdown-input-field').set('review/*')
+ find('.js-variable-environment-dropdown-wrapper .js-dropdown-create-new-item').click
+
+ expect(find('input[name="variables[variables_attributes][][environment_scope]"]', visible: false).value).to eq('review/*')
+ end
+
+ click_button('Save variables')
+ wait_for_requests
+
+ visit page_path
+
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
+ expect(find('.js-ci-variable-input-key').value).to eq('somekey')
+ expect(page).to have_content('review/*')
+ end
+ end
end
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 6d0269dd96b..1b277e17b0c 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -50,6 +50,20 @@ describe 'User browses a job', :js do
expect(page).not_to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
end
+
+ it 'collapses the section header clicked' do
+ wait_for_requests
+ text_to_hide = "Cloning into '/nolith/ci-tests'"
+ text_to_show = 'Waiting for pod'
+
+ expect(page).to have_content(text_to_hide)
+ expect(page).to have_content(text_to_show)
+
+ first('.js-section-header.js-s-get-sources').click
+
+ expect(page).not_to have_content(text_to_hide)
+ expect(page).to have_content(text_to_show)
+ end
end
context 'when job trace contains sections' do
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 7b1fded1834..6e8d1a945e1 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -52,18 +52,18 @@ describe 'Projects > Members > Groups with access list', :js do
context 'search in existing members (yes, this filters the groups list as well)' do
it 'finds no results' do
- page.within '.member-search-form' do
+ page.within '.user-search-form' do
fill_in 'search', with: 'testing 123'
- find('.member-search-btn').click
+ find('.user-search-btn').click
end
expect(page).not_to have_selector('.group_member')
end
it 'finds results' do
- page.within '.member-search-form' do
+ page.within '.user-search-form' do
fill_in 'search', with: group.name
- find('.member-search-btn').click
+ find('.user-search-btn').click
end
expect(page).to have_selector('.group_member', count: 1)
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 3d756ee1b42..5a14d334088 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -18,7 +18,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by access level ascending' do
@@ -26,7 +26,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
it 'sorts by access level descending' do
@@ -34,7 +34,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
it 'sorts by last joined' do
@@ -42,7 +42,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
it 'sorts by oldest joined' do
@@ -50,7 +50,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
it 'sorts by name ascending' do
@@ -58,7 +58,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by name descending' do
@@ -66,7 +66,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
@@ -74,7 +74,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
@@ -82,7 +82,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(page).to have_css('.qa-user-sort-dropdowns .dropdown-toggle-text', text: 'Oldest sign in')
end
def visit_members_list(sort:)
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 71af75640de..5a60991c1bf 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -6,6 +6,21 @@ describe 'User searches for code' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
+ def submit_search(search, with_send_keys: false)
+ page.within('.search') do
+ field = find_field('search')
+ field.fill_in(with: search)
+
+ if with_send_keys
+ field.send_keys(:enter)
+ else
+ click_button("Go")
+ end
+ end
+
+ click_link('Code')
+ end
+
context 'when signed in' do
before do
project.add_maintainer(user)
@@ -15,12 +30,7 @@ describe 'User searches for code' do
it 'finds a file' do
visit(project_path(project))
- page.within('.search') do
- fill_in('search', with: 'application.js')
- click_button('Go')
- end
-
- click_link('Code')
+ submit_search('application.js')
expect(page).to have_selector('.file-content .code')
expect(page).to have_selector("span.line[lang='javascript']")
@@ -48,6 +58,50 @@ describe 'User searches for code' do
end
end
end
+
+ context 'search code within refs', :js do
+ let(:ref_name) { 'v1.0.0' }
+
+ before do
+ visit(project_tree_path(project, ref_name))
+ submit_search('gitlab-grack', with_send_keys: true)
+ end
+
+ it 'shows ref switcher in code result summary' do
+ expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ end
+ it 'persists branch name across search' do
+ find('.btn-search').click
+ expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ end
+
+ # this example is use to test the desgine that the refs is not
+ # only repersent the branch as well as the tags.
+ it 'ref swither list all the branchs and tags' do
+ find('.js-project-refs-dropdown').click
+ expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number')
+ expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0')
+ end
+
+ it 'search result changes when refs switched' do
+ expect(find('.search-results')).not_to have_content('path = gitlab-grack')
+ find('.js-project-refs-dropdown').click
+ find('.dropdown-page-one .dropdown-content').click_link('master')
+ expect(find('.search-results')).to have_content('path = gitlab-grack')
+ end
+ end
+
+ it 'no ref switcher shown in issue result summary', :js do
+ issue = create(:issue, title: 'test', project: project)
+ visit(project_tree_path(project))
+ submit_search('test', with_send_keys: true)
+ expect(page).to have_selector('.js-project-refs-dropdown')
+ page.within('.search-filter') do
+ click_link('Issues')
+ end
+ expect(find(:css, '.search-results')).to have_link(issue.title)
+ expect(page).not_to have_selector('.js-project-refs-dropdown')
+ end
end
context 'when signed out' do
@@ -58,8 +112,7 @@ describe 'User searches for code' do
end
it 'finds code' do
- fill_in('search', with: 'rspec')
- click_button('Go')
+ submit_search('rspec')
page.within('.results') do
expect(find(:css, '.search-results')).to have_content('Update capybara, rspec-rails, poltergeist to recent versions')
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index 29ce5425323..c781048d06d 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -22,7 +22,7 @@ describe 'User uses header search field', :js do
fill_in('search', with: 'gitlab')
find('#search').native.send_keys(:enter)
- page.within('.breadcrumbs-sub-title') do
+ page.within('.page-title') do
expect(page).to have_content('Search')
end
end
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index f5cda15b38a..fbd7da3c643 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -22,7 +22,7 @@ describe 'User uses search filters', :js do
wait_for_requests
- page.within('.search-holder') do
+ page.within('.search-page-form') do
click_link(group.name)
end
diff --git a/spec/features/user_opens_link_to_comment_spec.rb b/spec/features/user_opens_link_to_comment_spec.rb
index f1e07e55799..9533a4fe40d 100644
--- a/spec/features/user_opens_link_to_comment_spec.rb
+++ b/spec/features/user_opens_link_to_comment_spec.rb
@@ -18,8 +18,13 @@ describe 'User opens link to comment', :js do
visit Gitlab::UrlBuilder.build(note)
+ wait_for_requests
+
expect(page.find('#discussion-filter-dropdown')).to have_content('Show all activity')
expect(page).not_to have_content('Something went wrong while fetching comments')
+
+ # Auto-switching to show all notes shouldn't be persisted
+ expect(user.reload.notes_filter_for(note.noteable)).to eq(UserPreference::NOTES_FILTERS[:only_activity])
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index bcde730c40b..879ff01f294 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -113,13 +113,13 @@ describe IssuesFinder do
let(:params) { { milestone_title: 'Any' } }
it 'returns issues with any assigned milestone' do
- expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
+ expect(issues).to contain_exactly(issue1)
end
it 'returns issues with any assigned milestone (deprecated)' do
params[:milestone_title] = Milestone::Any.title
- expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
+ expect(issues).to contain_exactly(issue1)
end
end
diff --git a/spec/finders/starred_projects_finder_spec.rb b/spec/finders/starred_projects_finder_spec.rb
new file mode 100644
index 00000000000..7aa8251c3ab
--- /dev/null
+++ b/spec/finders/starred_projects_finder_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe StarredProjectsFinder do
+ let(:project1) { create(:project, :public, :empty_repo) }
+ let(:project2) { create(:project, :public, :empty_repo) }
+ let(:other_project) { create(:project, :public, :empty_repo) }
+
+ let(:user) { create(:user) }
+ let(:other_user) { create(:user) }
+
+ before do
+ user.toggle_star(project1)
+ user.toggle_star(project2)
+ end
+
+ describe '#execute' do
+ let(:finder) { described_class.new(user, params: {}, current_user: current_user) }
+
+ subject { finder.execute }
+
+ describe 'as same user' do
+ let(:current_user) { user }
+
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
+
+ describe 'as other user' do
+ let(:current_user) { other_user }
+
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
+
+ describe 'as no user' do
+ let(:current_user) { nil }
+
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
+ end
+end
diff --git a/spec/finders/users_star_projects_finder_spec.rb b/spec/finders/users_star_projects_finder_spec.rb
new file mode 100644
index 00000000000..fb1d8088f44
--- /dev/null
+++ b/spec/finders/users_star_projects_finder_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UsersStarProjectsFinder do
+ let(:project) { create(:project, :public, :empty_repo) }
+
+ let(:user) { create(:user) }
+ let(:private_user) { create(:user, private_profile: true) }
+ let(:other_user) { create(:user) }
+
+ before do
+ user.toggle_star(project)
+ private_user.toggle_star(project)
+ end
+
+ describe '#execute' do
+ let(:finder) { described_class.new(project, {}, current_user: current_user) }
+ let(:public_stars) { user.users_star_projects }
+ let(:private_stars) { private_user.users_star_projects }
+
+ subject { finder.execute }
+
+ describe 'as same user' do
+ let(:current_user) { private_user }
+
+ it { is_expected.to match_array(private_stars + public_stars) }
+ end
+
+ describe 'as other user' do
+ let(:current_user) { other_user }
+
+ it { is_expected.to match_array(public_stars) }
+ end
+
+ describe 'as no user' do
+ let(:current_user) { nil }
+
+ it { is_expected.to match_array(public_stars) }
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/public_api/v4/group_labels.json b/spec/fixtures/api/schemas/public_api/v4/group_labels.json
deleted file mode 100644
index fbde45f2904..00000000000
--- a/spec/fixtures/api/schemas/public_api/v4/group_labels.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "array",
- "items": {
- "type": "object",
- "properties" : {
- "id" : { "type": "integer" },
- "name" : { "type": "string "},
- "color" : { "type": "string "},
- "text_color" : { "type": "string "},
- "description" : { "type": "string "},
- "open_issues_count" : { "type": "integer "},
- "closed_issues_count" : { "type": "integer "},
- "open_merge_requests_count" : { "type": "integer "},
- "subscribed" : { "type": "boolean" },
- "priority" : { "type": "null" }
- },
- "additionalProperties": false
- }
-}
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/label.json b/spec/fixtures/api/schemas/public_api/v4/labels/label.json
new file mode 100644
index 00000000000..a116b33572d
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/label.json
@@ -0,0 +1,11 @@
+{
+ "type": "object",
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "color": { "type": "string" },
+ "text_color": { "type": "string" },
+ "description": { "type": ["string", "null"] },
+ "subscribed": { "type": "boolean" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json b/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json
new file mode 100644
index 00000000000..2331932e07d
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/label_with_counts.json
@@ -0,0 +1,16 @@
+{
+ "type": "object",
+ "properties": {
+ "allOf": [
+ { "$ref": "label.json" },
+ {
+ "type": "object",
+ "properties": {
+ "open_issues_count": { "type": "integer" },
+ "closed_issues_count": { "type": "integer" },
+ "open_merge_requests_count": { "type": "integer" }
+ }
+ }
+ ]
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json b/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json
new file mode 100644
index 00000000000..a9a065ee71f
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/project_label.json
@@ -0,0 +1,15 @@
+{
+ "type": "object",
+ "properties": {
+ "allOf": [
+ { "$ref": "label.json" },
+ {
+ "type": "object",
+ "properties": {
+ "priority": { "type": ["integer", "null"] },
+ "is_project_label": { "type": "boolean" }
+ }
+ }
+ ]
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json b/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json
new file mode 100644
index 00000000000..87b90b2b3b5
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/labels/project_label_with_counts.json
@@ -0,0 +1,9 @@
+{
+ "type": "object",
+ "properties": {
+ "allOf": [
+ { "$ref": "project_label.json" },
+ { "$ref": "label_with_counts.json" }
+ ]
+ }
+}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
index 33393805464..9c1be32645a 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
@@ -16,7 +16,8 @@
"unit": { "type": "string" },
"label": { "type": "string" },
"track": { "type": "string" },
- "prometheus_endpoint_path": { "type": "string" }
+ "prometheus_endpoint_path": { "type": "string" },
+ "metric_id": { "type": "number" }
},
"additionalProperties": false
}
diff --git a/spec/frontend/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb
index b6c29003e57..91e3b65215a 100644
--- a/spec/frontend/fixtures/projects.rb
+++ b/spec/frontend/fixtures/projects.rb
@@ -18,8 +18,6 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
- stub_licensed_features(variable_environment_scope: true)
-
project.add_maintainer(admin)
sign_in(admin)
allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
diff --git a/spec/frontend/lib/utils/forms_spec.js b/spec/frontend/lib/utils/forms_spec.js
new file mode 100644
index 00000000000..cac17235f0d
--- /dev/null
+++ b/spec/frontend/lib/utils/forms_spec.js
@@ -0,0 +1,74 @@
+import { serializeForm } from '~/lib/utils/forms';
+
+describe('lib/utils/forms', () => {
+ const createDummyForm = inputs => {
+ const form = document.createElement('form');
+
+ form.innerHTML = inputs
+ .map(({ type, name, value }) => {
+ let str = ``;
+ if (type === 'select') {
+ str = `<select name="${name}">`;
+ value.forEach(v => {
+ if (v.length > 0) {
+ str += `<option value="${v}"></option> `;
+ }
+ });
+ str += `</select>`;
+ } else {
+ str = `<input type="${type}" name="${name}" value="${value}" checked/>`;
+ }
+ return str;
+ })
+ .join('');
+
+ return form;
+ };
+
+ describe('serializeForm', () => {
+ it('returns an object of key values from inputs', () => {
+ const form = createDummyForm([
+ { type: 'text', name: 'foo', value: 'foo-value' },
+ { type: 'text', name: 'bar', value: 'bar-value' },
+ ]);
+
+ const data = serializeForm(form);
+
+ expect(data).toEqual({
+ foo: 'foo-value',
+ bar: 'bar-value',
+ });
+ });
+
+ it('works with select', () => {
+ const form = createDummyForm([
+ { type: 'select', name: 'foo', value: ['foo-value1', 'foo-value2'] },
+ { type: 'text', name: 'bar', value: 'bar-value1' },
+ ]);
+
+ const data = serializeForm(form);
+
+ expect(data).toEqual({
+ foo: 'foo-value1',
+ bar: 'bar-value1',
+ });
+ });
+
+ it('works with multiple inputs of the same name', () => {
+ const form = createDummyForm([
+ { type: 'checkbox', name: 'foo', value: 'foo-value3' },
+ { type: 'checkbox', name: 'foo', value: 'foo-value2' },
+ { type: 'checkbox', name: 'foo', value: 'foo-value1' },
+ { type: 'text', name: 'bar', value: 'bar-value2' },
+ { type: 'text', name: 'bar', value: 'bar-value1' },
+ ]);
+
+ const data = serializeForm(form);
+
+ expect(data).toEqual({
+ foo: ['foo-value3', 'foo-value2', 'foo-value1'],
+ bar: ['bar-value2', 'bar-value1'],
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index c771984a137..a986bc49f28 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -34,6 +34,41 @@ describe('URL utility', () => {
});
});
+ describe('getParameterValues', () => {
+ beforeEach(() => {
+ setWindowLocation({
+ href: 'https://gitlab.com?test=passing&multiple=1&multiple=2',
+ // make our fake location act like real window.location.toString
+ // URL() (used in getParameterValues) does this if passed an object
+ toString() {
+ return this.href;
+ },
+ });
+ });
+
+ it('returns empty array for no params', () => {
+ expect(urlUtils.getParameterValues()).toEqual([]);
+ });
+
+ it('returns empty array for non-matching params', () => {
+ expect(urlUtils.getParameterValues('notFound')).toEqual([]);
+ });
+
+ it('returns single match', () => {
+ expect(urlUtils.getParameterValues('test')).toEqual(['passing']);
+ });
+
+ it('returns multiple matches', () => {
+ expect(urlUtils.getParameterValues('multiple')).toEqual(['1', '2']);
+ });
+
+ it('accepts url as second arg', () => {
+ const url = 'https://gitlab.com?everything=works';
+ expect(urlUtils.getParameterValues('everything', url)).toEqual(['works']);
+ expect(urlUtils.getParameterValues('test', url)).toEqual([]);
+ });
+ });
+
describe('mergeUrlParams', () => {
it('adds w', () => {
expect(urlUtils.mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag');
diff --git a/spec/frontend/tracking_spec.js b/spec/frontend/tracking_spec.js
index 7e462e9a6ce..cd0bf50f8e9 100644
--- a/spec/frontend/tracking_spec.js
+++ b/spec/frontend/tracking_spec.js
@@ -31,13 +31,6 @@ describe('Tracking', () => {
expect(snowplowSpy).not.toHaveBeenCalled();
});
-
- it('skips tracking if ', () => {
- window.snowplow = false;
- Tracking.event('_category_', '_eventName_');
-
- expect(snowplowSpy).not.toHaveBeenCalled();
- });
});
describe('tracking interface events', () => {
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index f3649495493..a6623bc7941 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -27,7 +27,7 @@ describe UsersHelper do
context 'with public profile' do
it 'includes all the expected tabs' do
- expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ expect(tabs).to include(:activity, :groups, :contributed, :projects, :starred, :snippets)
end
end
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index e6a969bd855..b2fe315f6c6 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -224,7 +224,7 @@ describe('AjaxFormVariableList', () => {
describe('maskableRegex', () => {
it('takes in the regex provided by the data attribute', () => {
- expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/-]{8,}$');
+ expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$');
expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex);
});
});
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index 064113e879a..c8d6f789ed0 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -162,7 +162,7 @@ describe('VariableList', () => {
});
it('has a regex provided via a data attribute', () => {
- expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/-]{8,}$');
+ expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$');
});
it('allows values that are 8 characters long', done => {
diff --git a/spec/frontend/jobs/components/empty_state_spec.js b/spec/javascripts/jobs/components/empty_state_spec.js
index dfba5a936ee..c6eac4e27b3 100644
--- a/spec/frontend/jobs/components/empty_state_spec.js
+++ b/spec/javascripts/jobs/components/empty_state_spec.js
@@ -105,7 +105,7 @@ describe('Empty State', () => {
});
describe('with playbale action and not scheduled job', () => {
- it('renders manual variables form', () => {
+ beforeEach(() => {
vm = mountComponent(Component, {
...props,
content,
@@ -117,9 +117,15 @@ describe('Empty State', () => {
method: 'post',
},
});
+ });
+ it('renders manual variables form', () => {
expect(vm.$el.querySelector('.js-manual-vars-form')).not.toBeNull();
});
+
+ it('does not render the empty state action', () => {
+ expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull();
+ });
});
describe('with playbale action and scheduled job', () => {
diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js
index 7e2ec2ec3f7..3485eec7763 100644
--- a/spec/javascripts/jobs/components/job_log_spec.js
+++ b/spec/javascripts/jobs/components/job_log_spec.js
@@ -98,5 +98,25 @@ describe('Job Log', () => {
.then(done)
.catch(done.fail);
});
+
+ it('toggles hidden class to the sibilings rows when header section is clicked', done => {
+ vm.$nextTick()
+ .then(() => {
+ const { section } = vm.$el.querySelector('.js-section-header').dataset;
+ vm.$el.querySelector('.js-section-header').click();
+
+ vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
+ expect(el.classList.contains('hidden')).toEqual(true);
+ });
+
+ vm.$el.querySelector('.js-section-header').click();
+
+ vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
+ expect(el.classList.contains('hidden')).toEqual(false);
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
});
});
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index d3e10194d92..36f650d5933 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -307,7 +307,7 @@ describe('Dashboard', () => {
});
spyOn(component.$store, 'dispatch').and.stub();
- const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff');
+ const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
@@ -319,7 +319,7 @@ describe('Dashboard', () => {
Vue.nextTick()
.then(() => {
expect(component.$store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalledWith(component.selectedTimeWindow);
+ expect(getTimeDiffSpy).toHaveBeenCalled();
done();
})
@@ -327,7 +327,17 @@ describe('Dashboard', () => {
});
it('shows a specific time window selected from the url params', done => {
- spyOnDependency(Dashboard, 'getParameterValues').and.returnValue(['thirtyMinutes']);
+ const start = 1564439536;
+ const end = 1564441336;
+ 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'),
diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js
index 677455275de..955a39e03a5 100644
--- a/spec/javascripts/monitoring/store/actions_spec.js
+++ b/spec/javascripts/monitoring/store/actions_spec.js
@@ -313,8 +313,8 @@ describe('Monitoring store actions', () => {
it('commits prometheus query result', done => {
const commit = jasmine.createSpy();
const params = {
- start: '1557216349.469',
- end: '1557218149.469',
+ start: '2019-08-06T12:40:02.184Z',
+ end: '2019-08-06T20:40:02.184Z',
};
const metric = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics[0];
const state = storeState();
diff --git a/spec/javascripts/monitoring/utils_spec.js b/spec/javascripts/monitoring/utils_spec.js
index 5570d57b8b2..e22e8cdc03d 100644
--- a/spec/javascripts/monitoring/utils_spec.js
+++ b/spec/javascripts/monitoring/utils_spec.js
@@ -3,28 +3,38 @@ import { timeWindows } from '~/monitoring/constants';
import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data';
describe('getTimeDiff', () => {
+ function secondsBetween({ start, end }) {
+ return (new Date(end) - new Date(start)) / 1000;
+ }
+
+ function minutesBetween(timeRange) {
+ return secondsBetween(timeRange) / 60;
+ }
+
+ function hoursBetween(timeRange) {
+ return minutesBetween(timeRange) / 60;
+ }
+
it('defaults to an 8 hour (28800s) difference', () => {
const params = getTimeDiff();
- expect(params.end - params.start).toEqual(28800);
+ expect(hoursBetween(params)).toEqual(8);
});
it('accepts time window as an argument', () => {
- const params = getTimeDiff(timeWindows.thirtyMinutes);
+ const params = getTimeDiff('thirtyMinutes');
- expect(params.end - params.start).not.toEqual(28800);
+ expect(minutesBetween(params)).toEqual(30);
});
it('returns a value for every defined time window', () => {
const nonDefaultWindows = Object.keys(timeWindows).filter(window => window !== 'eightHours');
- nonDefaultWindows.forEach(window => {
- const params = getTimeDiff(timeWindows[window]);
- const diff = params.end - params.start;
+ nonDefaultWindows.forEach(timeWindow => {
+ const params = getTimeDiff(timeWindow);
- // Ensure we're not returning the default, 28800 (the # of seconds in 8 hrs)
- expect(diff).not.toEqual(28800);
- expect(typeof diff).toEqual('number');
+ // Ensure we're not returning the default
+ expect(hoursBetween(params)).not.toEqual(8);
});
});
});
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index c461c28a37b..e55aa0e965a 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -892,4 +892,31 @@ describe('Actions Notes Store', () => {
});
});
});
+
+ describe('filterDiscussion', () => {
+ const path = 'some-discussion-path';
+ const filter = 0;
+
+ beforeEach(() => {
+ dispatch.and.returnValue(new Promise(() => {}));
+ });
+
+ it('fetches discussions with filter and persistFilter false', () => {
+ actions.filterDiscussion({ dispatch }, { path, filter, persistFilter: false });
+
+ expect(dispatch.calls.allArgs()).toEqual([
+ ['setLoadingState', true],
+ ['fetchDiscussions', { path, filter, persistFilter: false }],
+ ]);
+ });
+
+ it('fetches discussions with filter and persistFilter true', () => {
+ actions.filterDiscussion({ dispatch }, { path, filter, persistFilter: true });
+
+ expect(dispatch.calls.allArgs()).toEqual([
+ ['setLoadingState', true],
+ ['fetchDiscussions', { path, filter, persistFilter: true }],
+ ]);
+ });
+ });
});
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index 1bc0335cfc0..326703eea05 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -105,6 +105,17 @@ describe Banzai::Filter::CommitReferenceFilter do
expect(doc.css('a').first[:href]).to eq(url)
end
+
+ context "a doc with many (29) strings that could be SHAs" do
+ let!(:oids) { noteable.commits.collect(&:id) }
+
+ it 'makes only a single request to Gitaly' do
+ expect(Gitlab::GitalyClient).to receive(:allow_n_plus_1_calls).exactly(0).times
+ expect(Gitlab::Git::Commit).to receive(:batch_by_oid).once.and_call_original
+
+ reference_filter("A big list of SHAs #{oids.join(", ")}", noteable: noteable)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index eaf06ed8992..b6b3de4bc4a 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -209,7 +209,7 @@ describe Gitlab::Ci::Ansi2html do
let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
let(:section_start_html) do
- '<div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer"' \
+ '<div class="js-section-start fa fa-caret-down pr-2 cursor-pointer"' \
" data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{class_name(section_name)}\"" \
' role="button"></div>'
end
@@ -233,8 +233,8 @@ describe Gitlab::Ci::Ansi2html do
it 'prints light red' do
text = "#{section_start}\e[91mHello\e[0m\nLine 1\nLine 2\nLine 3\n#{section_end}"
- header = %{<span class="term-fg-l-red section js-section-header section-header js-s-#{class_name(section_name)}">Hello</span>}
- line_break = %{<span class="section js-section-header section-header js-s-#{class_name(section_name)}"><br/></span>}
+ header = %{<span class="term-fg-l-red section js-section-header section-header cursor-pointer js-s-#{class_name(section_name)}">Hello</span>}
+ line_break = %{<span class="section js-section-header section-header cursor-pointer js-s-#{class_name(section_name)}"><br/></span>}
output_line = %{<span class="section line js-s-#{class_name(section_name)}">Line 1<br/>Line 2<br/>Line 3<br/></span>}
html = "#{section_start_html}#{header}#{line_break}#{output_line}#{section_end_html}"
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 42a2a9fda2e..f712f47a558 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -91,5 +91,38 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(policy).to be_satisfied_by(pipeline, seed)
end
end
+
+ context 'when using project ci variables in environment scope' do
+ let(:ci_build) do
+ build(:ci_build, pipeline: pipeline,
+ project: project,
+ ref: 'master',
+ stage: 'review',
+ environment: 'test/$CI_JOB_STAGE/1')
+ end
+
+ before do
+ create(:ci_variable, project: project,
+ key: 'SCOPED_VARIABLE',
+ value: 'my-value-1')
+
+ create(:ci_variable, project: project,
+ key: 'SCOPED_VARIABLE',
+ value: 'my-value-2',
+ environment_scope: 'test/review/*')
+ end
+
+ it 'is satisfied by scoped variable match' do
+ policy = described_class.new(['$SCOPED_VARIABLE == "my-value-2"'])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied when matching against overridden variable' do
+ policy = described_class.new(['$SCOPED_VARIABLE == "my-value-1"'])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index e7670c9d523..1d404915617 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -13,7 +13,8 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
child_src: "'self' https://child.example.com",
default_src: "'self' https://other.example.com",
script_src: "'self' https://script.exammple.com ",
- worker_src: "data: https://worker.example.com"
+ worker_src: "data: https://worker.example.com",
+ report_uri: "http://example.com"
}
}
end
@@ -46,6 +47,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(policy.directives['default-src']).to eq(expected_config(:default_src))
expect(policy.directives['child-src']).to eq(expected_config(:child_src))
expect(policy.directives['worker-src']).to eq(expected_config(:worker_src))
+ expect(policy.directives['report-uri']).to eq(expected_config(:report_uri))
end
it 'ignores malformed policy statements' do
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 2759412add8..eced96a4c77 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -14,6 +14,16 @@ describe Gitlab::CurrentSettings do
end
end
+ describe '.expire_current_application_settings', :use_clean_rails_memory_store_caching, :request_store do
+ include_context 'with settings in cache'
+
+ it 'expires the cache' do
+ described_class.expire_current_application_settings
+
+ expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).not_to eq(0)
+ end
+ end
+
describe '#current_application_settings', :use_clean_rails_memory_store_caching do
it 'allows keys to be called directly' do
db_settings = create(:application_setting,
diff --git a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
index 23ae026fb14..0e5419e6c5e 100644
--- a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
@@ -6,6 +6,7 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do
let(:importer) { described_class.new(project, client) }
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }
+ let(:released_at) { Time.new(2017, 1, 1, 12, 00) }
let(:release) do
double(
@@ -13,7 +14,8 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do
tag_name: '1.0',
body: 'This is my release',
created_at: created_at,
- updated_at: updated_at
+ updated_at: updated_at,
+ published_at: released_at
)
end
@@ -23,7 +25,8 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do
tag_name: '1.0',
description: 'This is my release',
created_at: created_at,
- updated_at: updated_at
+ updated_at: updated_at,
+ released_at: released_at
}
expect(importer).to receive(:build_releases).and_return([release_hash])
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index a410e4eab45..4676db6b8d8 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -62,14 +62,6 @@ describe Gitlab::Highlight do
expect(lines[2].text).to eq(' """')
end
- context 'since param is present' do
- it 'highlights with the LC starting from "since" param' do
- lines = described_class.highlight(file_name, content, since: 2).lines
-
- expect(lines[0]).to include('LC2')
- end
- end
-
context 'diff highlighting' do
let(:file_name) { 'test.diff' }
let(:content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index ada8c649ff6..fddb5066d6f 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -277,7 +277,6 @@ project:
- bugzilla_service
- gitlab_issue_tracker_service
- external_wiki_service
-- kubernetes_service
- mock_ci_service
- mock_deployment_service
- mock_monitoring_service
diff --git a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
new file mode 100644
index 00000000000..420b246b3f5
--- /dev/null
+++ b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Metrics::Dashboard::Defaults do
+ it { is_expected.to be_const_defined(:DEFAULT_PANEL_TYPE) }
+ it { is_expected.to be_const_defined(:DEFAULT_PANEL_WEIGHT) }
+end
diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
index e57c7326320..ce1bb49f5c9 100644
--- a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
@@ -5,10 +5,9 @@ require 'spec_helper'
describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_caching do
include MetricsDashboardHelpers
- set(:project) { build(:project) }
+ set(:project) { create(:project) }
set(:user) { create(:user) }
set(:environment) { create(:environment, project: project) }
- let(:system_dashboard_path) { ::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH}
before do
project.add_maintainer(user)
@@ -52,9 +51,80 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
end
context 'when the dashboard is expected to be embedded' do
- let(:service_call) { described_class.find(project, user, environment, dashboard_path: nil, embedded: true) }
+ let(:service_call) { described_class.find(project, user, environment, **params) }
+ let(:params) { { embedded: true } }
it_behaves_like 'valid embedded dashboard service response'
+
+ context 'when params are incomplete' do
+ let(:params) { { embedded: true, dashboard_path: system_dashboard_path } }
+
+ it_behaves_like 'valid embedded dashboard service response'
+ end
+
+ context 'when the panel is specified' do
+ context 'as a custom metric' do
+ let(:params) do
+ { embedded: true,
+ dashboard_path: system_dashboard_path,
+ group: business_metric_title,
+ title: 'title',
+ y_label: 'y_label' }
+ end
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+
+ context 'when the metric exists' do
+ before do
+ create(:prometheus_metric, project: project)
+ end
+
+ it_behaves_like 'valid embedded dashboard service response'
+ end
+ end
+
+ context 'as a project-defined panel' do
+ let(:dashboard_path) { '.gitlab/dashboard/test.yml' }
+ let(:params) do
+ { embedded: true,
+ dashboard_path: dashboard_path,
+ group: 'Group A',
+ title: 'Super Chart A1',
+ y_label: 'y_label' }
+ end
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+
+ context 'when the metric exists' do
+ let(:project) { project_with_dashboard(dashboard_path) }
+
+ it_behaves_like 'valid embedded dashboard service response'
+ end
+ end
+ end
+ end
+ end
+
+ describe '.find_raw' do
+ let(:dashboard) { YAML.load_file(Rails.root.join('config', 'prometheus', 'common_metrics.yml')) }
+ let(:params) { {} }
+
+ subject { described_class.find_raw(project, **params) }
+
+ it { is_expected.to eq dashboard }
+
+ context 'when the system dashboard is specified' do
+ let(:params) { { dashboard_path: system_dashboard_path } }
+
+ it { is_expected.to eq dashboard }
+ end
+
+ context 'when an existing project dashboard is specified' do
+ let(:dashboard) { YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')) }
+ let(:params) { { dashboard_path: '.gitlab/dashboards/test.yml' } }
+ let(:project) { project_with_dashboard(params[:dashboard_path]) }
+
+ it { is_expected.to eq dashboard }
end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
new file mode 100644
index 00000000000..095d0a2df78
--- /dev/null
+++ b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Metrics::Dashboard::ServiceSelector do
+ include MetricsDashboardHelpers
+
+ describe '#call' do
+ let(:arguments) { {} }
+
+ subject { described_class.call(arguments) }
+
+ it { is_expected.to be Metrics::Dashboard::SystemDashboardService }
+
+ context 'when just the dashboard path is provided' do
+ let(:arguments) { { dashboard_path: '.gitlab/dashboards/test.yml' } }
+
+ it { is_expected.to be Metrics::Dashboard::ProjectDashboardService }
+
+ context 'when the path is for the system dashboard' do
+ let(:arguments) { { dashboard_path: system_dashboard_path } }
+
+ it { is_expected.to be Metrics::Dashboard::SystemDashboardService }
+ end
+ end
+
+ context 'when the embedded flag is provided' do
+ let(:arguments) { { embedded: true } }
+
+ it { is_expected.to be Metrics::Dashboard::DefaultEmbedService }
+
+ context 'when an incomplete set of dashboard identifiers are provided' do
+ let(:arguments) { { embedded: true, dashboard_path: '.gitlab/dashboards/test.yml' } }
+
+ it { is_expected.to be Metrics::Dashboard::DefaultEmbedService }
+ end
+
+ context 'when all the chart identifiers are provided' do
+ let(:arguments) do
+ {
+ embedded: true,
+ dashboard_path: '.gitlab/dashboards/test.yml',
+ group: 'Important Metrics',
+ title: 'Total Requests',
+ y_label: 'req/sec'
+ }
+ end
+
+ it { is_expected.to be Metrics::Dashboard::DynamicEmbedService }
+ end
+
+ context 'when all chart params expect dashboard_path are provided' do
+ let(:arguments) do
+ {
+ embedded: true,
+ group: 'Important Metrics',
+ title: 'Total Requests',
+ y_label: 'req/sec'
+ }
+ end
+
+ it { is_expected.to be Metrics::Dashboard::DynamicEmbedService }
+ end
+
+ context 'with a system dashboard and "custom" group' do
+ let(:arguments) do
+ {
+ embedded: true,
+ dashboard_path: system_dashboard_path,
+ group: business_metric_title,
+ title: 'Total Requests',
+ y_label: 'req/sec'
+ }
+ end
+
+ it { is_expected.to be Metrics::Dashboard::CustomMetricEmbedService }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index a1079e54975..ba295386a55 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -32,6 +32,14 @@ describe Gitlab::Regex do
it { is_expected.not_to match('/') }
end
+ describe '.environment_scope_regex' do
+ subject { described_class.environment_scope_regex }
+
+ it { is_expected.to match('foo') }
+ it { is_expected.to match('foo*Z') }
+ it { is_expected.not_to match('!!()()') }
+ end
+
describe '.environment_slug_regex' do
subject { described_class.environment_slug_regex }
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index 98286eb432d..5621d3d17d1 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe Gitlab::SidekiqLogging::StructuredLogger do
describe '#call' do
let(:timestamp) { Time.iso8601('2018-01-01T12:00:00Z') }
- let(:created_at) { timestamp }
- let(:scheduling_latency_s) { 0.0 }
+ let(:created_at) { timestamp - 1.second }
+ let(:scheduling_latency_s) { 1.0 }
let(:job) do
{
@@ -153,5 +153,29 @@ describe Gitlab::SidekiqLogging::StructuredLogger do
end
end
end
+
+ context 'with Gitaly and Rugged calls' do
+ let(:timing_data) do
+ {
+ gitaly_calls: 10,
+ gitaly_duration: 10000,
+ rugged_calls: 1,
+ rugged_duration_ms: 5000
+ }
+ end
+
+ before do
+ job.merge!(timing_data)
+ end
+
+ it 'logs with Gitaly and Rugged timing data' do
+ Timecop.freeze(timestamp) do
+ expect(logger).to receive(:info).with(start_payload.except('args')).ordered
+ expect(logger).to receive(:info).with(end_payload.except('args')).ordered
+
+ subject.call(job, 'test_queue') { }
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
new file mode 100644
index 00000000000..1669a22879f
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UsageDataCounters::NoteCounter, :clean_gitlab_redis_shared_state do
+ shared_examples 'a note usage counter' do |event, noteable_type|
+ describe ".count(#{event})" do
+ it "increments the Note #{event} counter by 1" do
+ expect do
+ described_class.count(event, noteable_type)
+ end.to change { described_class.read(event, noteable_type) }.by 1
+ end
+ end
+
+ describe ".read(#{event})" do
+ event_count = 5
+
+ it "returns the total number of #{event} events" do
+ event_count.times do
+ described_class.count(event, noteable_type)
+ end
+
+ expect(described_class.read(event, noteable_type)).to eq(event_count)
+ end
+ end
+ end
+
+ it_behaves_like 'a note usage counter', :create, 'Snippet'
+
+ describe '.totals' do
+ let(:combinations) do
+ [
+ [:create, 'Snippet', 3]
+ ]
+ end
+
+ let(:expected_totals) do
+ { snippet_comment: 3 }
+ end
+
+ before do
+ combinations.each do |event, noteable_type, n|
+ n.times do
+ described_class.count(event, noteable_type)
+ end
+ end
+ end
+
+ it 'can report all totals' do
+ expect(described_class.totals).to include(expected_totals)
+ end
+ end
+
+ describe 'unknown events or noteable_type' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:unknown_event_error) { Gitlab::UsageDataCounters::BaseCounter::UnknownEvent }
+
+ where(:event, :noteable_type, :expected_count, :should_raise) do
+ :create | 'Snippet' | 1 | false
+ :wibble | 'Snippet' | 0 | true
+ :create | 'Issue' | 0 | false
+ :wibble | 'Issue' | 0 | false
+ end
+
+ with_them do
+ it "handles event" do
+ if should_raise
+ expect { described_class.count(event, noteable_type) }.to raise_error(unknown_event_error)
+ else
+ described_class.count(event, noteable_type)
+
+ expect(described_class.read(event, noteable_type)).to eq(expected_count)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb
new file mode 100644
index 00000000000..65381ed36d1
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UsageDataCounters::SnippetCounter do
+ it_behaves_like 'a redis usage counter', 'Snippet', :create
+ it_behaves_like 'a redis usage counter', 'Snippet', :update
+
+ it_behaves_like 'a redis usage counter with totals', :snippet,
+ create: 3,
+ update: 2
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 297c4f0b683..bf36273251b 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -62,6 +62,9 @@ describe Gitlab::UsageData do
))
expect(subject).to include(
+ snippet_create: a_kind_of(Integer),
+ snippet_update: a_kind_of(Integer),
+ snippet_comment: a_kind_of(Integer),
wiki_pages_create: a_kind_of(Integer),
wiki_pages_update: a_kind_of(Integer),
wiki_pages_delete: a_kind_of(Integer),
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b7e005e3883..4aac4b640f4 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2340,6 +2340,32 @@ describe Ci::Build do
it_behaves_like 'containing environment variables'
end
end
+
+ context 'when project has an environment specific variable' do
+ let(:environment_specific_variable) do
+ { key: 'MY_STAGING_ONLY_VARIABLE', value: 'environment_specific_variable', public: false, masked: false }
+ end
+
+ before do
+ create(:ci_variable, environment_specific_variable.slice(:key, :value)
+ .merge(project: project, environment_scope: 'stag*'))
+ end
+
+ it_behaves_like 'containing environment variables'
+
+ context 'when environment scope does not match build environment' do
+ it { is_expected.not_to include(environment_specific_variable) }
+ end
+
+ context 'when environment scope matches build environment' do
+ before do
+ create(:environment, name: 'staging', project: project)
+ build.update!(environment: 'staging')
+ end
+
+ it { is_expected.to include(environment_specific_variable) }
+ end
+ end
end
context 'when build started manually' do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index a231c7eaed8..3ff547456c6 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -10,6 +10,7 @@ describe Ci::Variable do
describe 'validations' do
it { is_expected.to include_module(Presentable) }
it { is_expected.to include_module(Maskable) }
+ it { is_expected.to include_module(HasEnvironmentScope) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) }
end
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index eeacdadab9c..da46effe411 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -7,6 +7,7 @@ describe CacheableAttributes do
Class.new do
include ActiveModel::Model
extend ActiveModel::Callbacks
+ include ActiveModel::AttributeMethods
define_model_callbacks :commit
include CacheableAttributes
@@ -34,44 +35,60 @@ describe CacheableAttributes do
end
end
+ before do
+ stub_const("MinimalTestClass", minimal_test_class)
+ end
+
shared_context 'with defaults' do
before do
- minimal_test_class.define_singleton_method(:defaults) do
+ MinimalTestClass.define_singleton_method(:defaults) do
{ foo: 'a', bar: 'b', baz: 'c' }
end
end
end
+ describe '.expire', :use_clean_rails_memory_store_caching, :request_store do
+ it 'wipes the cache' do
+ obj = MinimalTestClass.new
+ obj.cache!
+ expect(MinimalTestClass.cached).not_to eq(nil)
+
+ MinimalTestClass.expire
+
+ expect(MinimalTestClass.cached).to eq(nil)
+ end
+ end
+
describe '.current_without_cache' do
it 'defaults to last' do
- expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.last)
+ expect(MinimalTestClass.current_without_cache).to eq(MinimalTestClass.last)
end
it 'can be overridden' do
- minimal_test_class.define_singleton_method(:current_without_cache) do
+ MinimalTestClass.define_singleton_method(:current_without_cache) do
first
end
- expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.first)
+ expect(MinimalTestClass.current_without_cache).to eq(MinimalTestClass.first)
end
end
describe '.cache_key' do
it 'excludes cache attributes' do
- expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Rails.version}")
+ expect(MinimalTestClass.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Rails.version}")
end
end
describe '.defaults' do
it 'defaults to {}' do
- expect(minimal_test_class.defaults).to eq({})
+ expect(MinimalTestClass.defaults).to eq({})
end
context 'with defaults defined' do
include_context 'with defaults'
it 'can be overridden' do
- expect(minimal_test_class.defaults).to eq({ foo: 'a', bar: 'b', baz: 'c' })
+ expect(MinimalTestClass.defaults).to eq({ foo: 'a', bar: 'b', baz: 'c' })
end
end
end
@@ -81,13 +98,13 @@ describe CacheableAttributes do
context 'without any attributes given' do
it 'intializes a new object with the defaults' do
- expect(minimal_test_class.build_from_defaults.attributes).to eq(minimal_test_class.defaults.stringify_keys)
+ expect(MinimalTestClass.build_from_defaults.attributes).to eq(MinimalTestClass.defaults.stringify_keys)
end
end
context 'with attributes given' do
it 'intializes a new object with the given attributes merged into the defaults' do
- expect(minimal_test_class.build_from_defaults(foo: 'd').attributes['foo']).to eq('d')
+ expect(MinimalTestClass.build_from_defaults(foo: 'd').attributes['foo']).to eq('d')
end
end
@@ -108,8 +125,8 @@ describe CacheableAttributes do
describe '.current', :use_clean_rails_memory_store_caching do
context 'redis unavailable' do
before do
- allow(minimal_test_class).to receive(:last).and_return(:last)
- expect(Rails.cache).to receive(:read).with(minimal_test_class.cache_key).and_raise(Redis::BaseError)
+ allow(MinimalTestClass).to receive(:last).and_return(:last)
+ expect(Rails.cache).to receive(:read).with(MinimalTestClass.cache_key).and_raise(Redis::BaseError)
end
context 'in production environment' do
@@ -120,7 +137,7 @@ describe CacheableAttributes do
it 'returns an uncached record and logs a warning' do
expect(Rails.logger).to receive(:warn).with("Cached record for TestClass couldn't be loaded, falling back to uncached record: Redis::BaseError")
- expect(minimal_test_class.current).to eq(:last)
+ expect(MinimalTestClass.current).to eq(:last)
end
end
@@ -132,7 +149,7 @@ describe CacheableAttributes do
it 'returns an uncached record and logs a warning' do
expect(Rails.logger).not_to receive(:warn)
- expect { minimal_test_class.current }.to raise_error(Redis::BaseError)
+ expect { MinimalTestClass.current }.to raise_error(Redis::BaseError)
end
end
end
@@ -202,7 +219,7 @@ describe CacheableAttributes do
describe '.cached', :use_clean_rails_memory_store_caching do
context 'when cache is cold' do
it 'returns nil' do
- expect(minimal_test_class.cached).to be_nil
+ expect(MinimalTestClass.cached).to be_nil
end
end
diff --git a/spec/models/concerns/has_environment_scope_spec.rb b/spec/models/concerns/has_environment_scope_spec.rb
new file mode 100644
index 00000000000..a6e1ba59263
--- /dev/null
+++ b/spec/models/concerns/has_environment_scope_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe HasEnvironmentScope do
+ subject { build(:ci_variable) }
+
+ it { is_expected.to allow_value('*').for(:environment_scope) }
+ it { is_expected.to allow_value('review/*').for(:environment_scope) }
+ it { is_expected.not_to allow_value('').for(:environment_scope) }
+ it { is_expected.not_to allow_value('!!()()').for(:environment_scope) }
+
+ it do
+ is_expected.to validate_uniqueness_of(:key)
+ .scoped_to(:project_id, :environment_scope)
+ .with_message(/\(\w+\) has already been taken/)
+ end
+
+ describe '.on_environment' do
+ let(:project) { create(:project) }
+
+ it 'returns scoped objects' do
+ variable1 = create(:ci_variable, project: project, environment_scope: '*')
+ variable2 = create(:ci_variable, project: project, environment_scope: 'product/*')
+ create(:ci_variable, project: project, environment_scope: 'staging/*')
+
+ expect(project.variables.on_environment('product/canary-1')).to eq([variable1, variable2])
+ end
+
+ it 'returns only the most relevant object if relevant_only is true' do
+ create(:ci_variable, project: project, environment_scope: '*')
+ variable2 = create(:ci_variable, project: project, environment_scope: 'product/*')
+ create(:ci_variable, project: project, environment_scope: 'staging/*')
+
+ expect(project.variables.on_environment('product/canary-1', relevant_only: true)).to eq([variable2])
+ end
+
+ it 'returns scopes ordered by lowest precedence first' do
+ create(:ci_variable, project: project, environment_scope: '*')
+ create(:ci_variable, project: project, environment_scope: 'production*')
+ create(:ci_variable, project: project, environment_scope: 'production')
+
+ result = project.variables.on_environment('production').map(&:environment_scope)
+
+ expect(result).to eq(['*', 'production*', 'production'])
+ end
+ end
+
+ describe '#environment_scope=' do
+ context 'when the new environment_scope is nil' do
+ it 'strips leading and trailing whitespaces' do
+ subject.environment_scope = nil
+
+ expect(subject.environment_scope).to eq('')
+ end
+ end
+
+ context 'when the new environment_scope has leadind and trailing whitespaces' do
+ it 'strips leading and trailing whitespaces' do
+ subject.environment_scope = ' * '
+
+ expect(subject.environment_scope).to eq('*')
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
deleted file mode 100644
index d33bbb0470f..00000000000
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe KubernetesService, :use_clean_rails_memory_store_caching do
- include KubernetesHelpers
- include ReactiveCachingHelpers
-
- let(:project) { create(:kubernetes_project) }
- let(:service) { create(:kubernetes_service, project: project) }
-
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- end
-
- describe 'Validations' do
- context 'when service is active' do
- before do
- subject.active = true
- subject.skip_deprecation_validation = true
- end
-
- it { is_expected.not_to validate_presence_of(:namespace) }
- it { is_expected.to validate_presence_of(:api_url) }
- it { is_expected.to validate_presence_of(:token) }
-
- context 'namespace format' do
- before do
- subject.project = project
- subject.api_url = "http://example.com"
- subject.token = "test"
- end
-
- {
- 'foo' => true,
- '1foo' => true,
- 'foo1' => true,
- 'foo-bar' => true,
- '-foo' => false,
- 'foo-' => false,
- 'a' * 63 => true,
- 'a' * 64 => false,
- 'a.b' => false,
- 'a*b' => false,
- 'FOO' => true
- }.each do |namespace, validity|
- it "validates #{namespace} as #{validity ? 'valid' : 'invalid'}" do
- subject.namespace = namespace
-
- expect(subject.valid?).to eq(validity)
- end
- end
- end
- end
-
- context 'when service is inactive' do
- before do
- subject.project = project
- subject.active = false
- end
-
- it { is_expected.not_to validate_presence_of(:api_url) }
- it { is_expected.not_to validate_presence_of(:token) }
- end
-
- context 'with a deprecated service' do
- let(:kubernetes_service) { create(:kubernetes_service) }
-
- before do
- kubernetes_service.update_attribute(:active, false)
- kubernetes_service.skip_deprecation_validation = false
- kubernetes_service.properties['namespace'] = "foo"
- end
-
- it 'does not update attributes' do
- expect(kubernetes_service.save).to be_falsy
- end
-
- it 'includes an error with a deprecation message' do
- kubernetes_service.valid?
- expect(kubernetes_service.errors[:base].first).to match(/Kubernetes service integration has been disabled/)
- end
- end
-
- context 'with an active and deprecated service' do
- let(:kubernetes_service) { create(:kubernetes_service) }
-
- before do
- kubernetes_service.skip_deprecation_validation = false
- kubernetes_service.active = false
- kubernetes_service.properties['namespace'] = 'foo'
- kubernetes_service.save
- end
-
- it 'deactivates the service' do
- expect(kubernetes_service.active?).to be_falsy
- end
-
- it 'does not include a deprecation message as error' do
- expect(kubernetes_service.errors.messages.count).to eq(0)
- end
-
- it 'updates attributes' do
- expect(kubernetes_service.properties['namespace']).to eq("foo")
- end
- end
- end
-
- describe '#initialize_properties' do
- context 'without a project' do
- it 'leaves the namespace unset' do
- expect(described_class.new.namespace).to be_nil
- end
- end
- end
-
- describe '#fields' do
- let(:kube_namespace) do
- subject.fields.find { |h| h[:name] == 'namespace' }
- end
-
- context 'as template' do
- before do
- subject.template = true
- end
-
- it 'sets the namespace to the default' do
- expect(kube_namespace).not_to be_nil
- expect(kube_namespace[:placeholder]).to eq(subject.class::TEMPLATE_PLACEHOLDER)
- end
- end
-
- context 'with associated project' do
- before do
- subject.project = project
- end
-
- it 'sets the namespace to the default' do
- expect(kube_namespace).not_to be_nil
- expect(kube_namespace[:placeholder]).to match(/\A#{Gitlab::PathRegex::PATH_REGEX_STR}-\d+\z/)
- end
- end
- end
-
- describe "#deprecated?" do
- let(:kubernetes_service) { create(:kubernetes_service) }
-
- it 'returns true' do
- expect(kubernetes_service.deprecated?).to be_truthy
- end
- end
-
- describe "#deprecation_message" do
- let(:kubernetes_service) { create(:kubernetes_service) }
-
- it 'indicates the service is deprecated' do
- expect(kubernetes_service.deprecation_message).to match(/Kubernetes service integration has been disabled/)
- end
-
- context 'if the service is not active' do
- it 'returns a message' do
- kubernetes_service.update_attribute(:active, false)
- expect(kubernetes_service.deprecation_message).to match(/Fields on this page are not used by GitLab/)
- end
- end
- end
-end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index dde766c3813..29a589eba20 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2648,9 +2648,10 @@ describe Project do
describe '#ci_variables_for' do
let(:project) { create(:project) }
+ let(:environment_scope) { '*' }
let!(:ci_variable) do
- create(:ci_variable, value: 'secret', project: project)
+ create(:ci_variable, value: 'secret', project: project, environment_scope: environment_scope)
end
let!(:protected_variable) do
@@ -2695,6 +2696,96 @@ describe Project do
it_behaves_like 'ref is protected'
end
+
+ context 'when environment name is specified' do
+ let(:environment) { 'review/name' }
+
+ subject do
+ project.ci_variables_for(ref: 'ref', environment: environment)
+ end
+
+ context 'when environment scope is exactly matched' do
+ let(:environment_scope) { 'review/name' }
+
+ it { is_expected.to contain_exactly(ci_variable) }
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ let(:environment_scope) { 'review/*' }
+
+ it { is_expected.to contain_exactly(ci_variable) }
+ end
+
+ context 'when environment scope does not match' do
+ let(:environment_scope) { 'review/*/special' }
+
+ it { is_expected.not_to contain_exactly(ci_variable) }
+ end
+
+ context 'when environment scope has _' do
+ let(:environment_scope) { '*_*' }
+
+ it 'does not treat it as wildcard' do
+ is_expected.not_to contain_exactly(ci_variable)
+ end
+
+ context 'when environment name contains underscore' do
+ let(:environment) { 'foo_bar/test' }
+ let(:environment_scope) { 'foo_bar/*' }
+
+ it 'matches literally for _' do
+ is_expected.to contain_exactly(ci_variable)
+ end
+ end
+ end
+
+ # The environment name and scope cannot have % at the moment,
+ # but we're considering relaxing it and we should also make sure
+ # it doesn't break in case some data sneaked in somehow as we're
+ # not checking this integrity in database level.
+ context 'when environment scope has %' do
+ it 'does not treat it as wildcard' do
+ ci_variable.update_attribute(:environment_scope, '*%*')
+
+ is_expected.not_to contain_exactly(ci_variable)
+ end
+
+ context 'when environment name contains a percent' do
+ let(:environment) { 'foo%bar/test' }
+
+ it 'matches literally for _' do
+ ci_variable.update(environment_scope: 'foo%bar/*')
+
+ is_expected.to contain_exactly(ci_variable)
+ end
+ end
+ end
+
+ context 'when variables with the same name have different environment scopes' do
+ let!(:partially_matched_variable) do
+ create(:ci_variable,
+ key: ci_variable.key,
+ value: 'partial',
+ environment_scope: 'review/*',
+ project: project)
+ end
+
+ let!(:perfectly_matched_variable) do
+ create(:ci_variable,
+ key: ci_variable.key,
+ value: 'prefect',
+ environment_scope: 'review/name',
+ project: project)
+ end
+
+ it 'puts variables matching environment scope more in the end' do
+ is_expected.to eq(
+ [ci_variable,
+ partially_matched_variable,
+ perfectly_matched_variable])
+ end
+ end
+ end
end
describe '#any_lfs_file_locks?', :request_store do
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
index 3610408c138..a123ff5a2a6 100644
--- a/spec/models/prometheus_metric_spec.rb
+++ b/spec/models/prometheus_metric_spec.rb
@@ -150,4 +150,17 @@ describe PrometheusMetric do
expect(subject.to_query_metric.queries).to eq(queries)
end
end
+
+ describe '#to_metric_hash' do
+ it 'returns a hash suitable for inclusion on a metrics dashboard' do
+ expected_output = {
+ query_range: subject.query,
+ unit: subject.unit,
+ label: subject.legend,
+ metric_id: subject.id
+ }
+
+ expect(subject.to_metric_hash).to eq(expected_output)
+ end
+ end
end
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
index 95db2ba6a0d..eacf383be7d 100644
--- a/spec/presenters/blob_presenter_spec.rb
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -28,70 +28,24 @@ describe BlobPresenter, :seed_helper do
subject { described_class.new(blob) }
it 'returns highlighted content' do
- expect(Gitlab::Highlight)
- .to receive(:highlight)
- .with(
- 'files/ruby/regex.rb',
- git_blob.data,
- since: nil,
- plain: nil,
- language: nil
- )
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: nil)
subject.highlight
end
it 'returns plain content when :plain is true' do
- expect(Gitlab::Highlight)
- .to receive(:highlight)
- .with(
- 'files/ruby/regex.rb',
- git_blob.data,
- since: nil,
- plain: true,
- language: nil
- )
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: nil)
subject.highlight(plain: true)
end
- context '"since" and "to" are present' do
- before do
- allow(git_blob)
- .to receive(:data)
- .and_return("line one\nline two\nline 3\nline 4")
- end
-
- it 'returns limited highlighted content' do
- expect(Gitlab::Highlight)
- .to receive(:highlight)
- .with(
- 'files/ruby/regex.rb',
- "line two\nline 3\n",
- since: 2,
- language: nil,
- plain: nil
- )
-
- subject.highlight(since: 2, to: 3)
- end
- end
-
context 'gitlab-language contains a match' do
before do
allow(blob).to receive(:language_from_gitattributes).and_return('ruby')
end
it 'passes language to inner call' do
- expect(Gitlab::Highlight)
- .to receive(:highlight)
- .with(
- 'files/ruby/regex.rb',
- git_blob.data,
- since: nil,
- plain: nil,
- language: 'ruby'
- )
+ expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby')
subject.highlight
end
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index fcea57d9df7..0be4e2e9121 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -14,12 +14,25 @@ describe API::GroupLabels do
get api("/groups/#{group.id}/labels", user)
expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/group_labels')
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
+ expect(json_response).to all(match_schema('public_api/v4/labels/label'))
expect(json_response.size).to eq(2)
expect(json_response.map {|r| r['name'] }).to contain_exactly('feature', 'bug')
end
+
+ context 'when the with_counts parameter is set' do
+ it 'includes counts in the response' do
+ get api("/groups/#{group.id}/labels", user), params: { with_counts: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response).to all(match_schema('public_api/v4/labels/label_with_counts'))
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |r| r['open_issues_count'] }).to contain_exactly(0, 0)
+ end
+ end
end
describe 'POST /groups/:id/labels' do
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index f11d8259d4a..f7ca6fd1e0a 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -389,7 +389,7 @@ describe API::Issues do
it 'returns an array of issues with any milestone' do
get api("#{base_url}/issues", user), params: { milestone: any_milestone_title }
- expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
+ expect_paginated_array_response([issue.id, closed_issue.id])
end
context 'without sort params' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 518181e4d93..ad0974f55a3 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -11,65 +11,76 @@ describe API::Labels do
end
describe 'GET /projects/:id/labels' do
- it 'returns all available labels to the project' do
- group = create(:group)
- group_label = create(:group_label, title: 'feature', group: group)
- project.update(group: group)
- create(:labeled_issue, project: project, labels: [group_label], author: user)
- create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed)
- create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project )
+ let(:group) { create(:group) }
+ let!(:group_label) { create(:group_label, title: 'feature', group: group) }
- expected_keys = %w(
- id name color text_color description
- open_issues_count closed_issues_count open_merge_requests_count
- subscribed priority is_project_label
- )
+ before do
+ project.update!(group: group)
+ end
+ it 'returns all available labels to the project' do
get api("/projects/#{project.id}/labels", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
+ expect(json_response).to all(match_schema('public_api/v4/labels/project_label'))
expect(json_response.size).to eq(3)
- expect(json_response.first.keys).to match_array expected_keys
expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name])
+ end
- label1_response = json_response.find { |l| l['name'] == label1.title }
- group_label_response = json_response.find { |l| l['name'] == group_label.title }
- priority_label_response = json_response.find { |l| l['name'] == priority_label.title }
-
- expect(label1_response['open_issues_count']).to eq(0)
- expect(label1_response['closed_issues_count']).to eq(1)
- expect(label1_response['open_merge_requests_count']).to eq(0)
- expect(label1_response['name']).to eq(label1.name)
- expect(label1_response['color']).to be_present
- expect(label1_response['text_color']).to be_present
- expect(label1_response['description']).to be_nil
- expect(label1_response['priority']).to be_nil
- expect(label1_response['subscribed']).to be_falsey
- expect(label1_response['is_project_label']).to be_truthy
-
- expect(group_label_response['open_issues_count']).to eq(1)
- expect(group_label_response['closed_issues_count']).to eq(0)
- expect(group_label_response['open_merge_requests_count']).to eq(0)
- expect(group_label_response['name']).to eq(group_label.name)
- expect(group_label_response['color']).to be_present
- expect(group_label_response['text_color']).to be_present
- expect(group_label_response['description']).to be_nil
- expect(group_label_response['priority']).to be_nil
- expect(group_label_response['subscribed']).to be_falsey
- expect(group_label_response['is_project_label']).to be_falsey
-
- expect(priority_label_response['open_issues_count']).to eq(0)
- expect(priority_label_response['closed_issues_count']).to eq(0)
- expect(priority_label_response['open_merge_requests_count']).to eq(1)
- expect(priority_label_response['name']).to eq(priority_label.name)
- expect(priority_label_response['color']).to be_present
- expect(priority_label_response['text_color']).to be_present
- expect(priority_label_response['description']).to be_nil
- expect(priority_label_response['priority']).to eq(3)
- expect(priority_label_response['subscribed']).to be_falsey
- expect(priority_label_response['is_project_label']).to be_truthy
+ context 'when the with_counts parameter is set' do
+ before do
+ create(:labeled_issue, project: project, labels: [group_label], author: user)
+ create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed)
+ create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project )
+ end
+
+ it 'includes counts in the response' do
+ get api("/projects/#{project.id}/labels", user), params: { with_counts: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to all(match_schema('public_api/v4/labels/project_label_with_counts'))
+ expect(json_response.size).to eq(3)
+ expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name])
+
+ label1_response = json_response.find { |l| l['name'] == label1.title }
+ group_label_response = json_response.find { |l| l['name'] == group_label.title }
+ priority_label_response = json_response.find { |l| l['name'] == priority_label.title }
+
+ expect(label1_response).to include('open_issues_count' => 0,
+ 'closed_issues_count' => 1,
+ 'open_merge_requests_count' => 0,
+ 'name' => label1.name,
+ 'description' => nil,
+ 'color' => a_string_matching(/^#\h{6}$/),
+ 'text_color' => a_string_matching(/^#\h{6}$/),
+ 'priority' => nil,
+ 'subscribed' => false,
+ 'is_project_label' => true)
+
+ expect(group_label_response).to include('open_issues_count' => 1,
+ 'closed_issues_count' => 0,
+ 'open_merge_requests_count' => 0,
+ 'name' => group_label.name,
+ 'description' => nil,
+ 'color' => a_string_matching(/^#\h{6}$/),
+ 'text_color' => a_string_matching(/^#\h{6}$/),
+ 'priority' => nil,
+ 'subscribed' => false,
+ 'is_project_label' => false)
+
+ expect(priority_label_response).to include('open_issues_count' => 0,
+ 'closed_issues_count' => 0,
+ 'open_merge_requests_count' => 1,
+ 'name' => priority_label.name,
+ 'description' => nil,
+ 'color' => a_string_matching(/^#\h{6}$/),
+ 'text_color' => a_string_matching(/^#\h{6}$/),
+ 'priority' => 3,
+ 'subscribed' => false,
+ 'is_project_label' => true)
+ end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 5b3a2412aff..1d7ca85cdd2 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -838,6 +838,28 @@ describe API::Projects do
end
end
+ describe 'GET /users/:user_id/starred_projects/' do
+ before do
+ user3.update(starred_projects: [project, project2, project3])
+ end
+
+ it 'returns error when user not found' do
+ get api('/users/9999/starred_projects/')
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
+ it 'returns projects filtered by user' do
+ get api("/users/#{user3.id}/starred_projects/", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, project2.id, project3.id)
+ end
+ end
+
describe 'POST /projects/user/:id' do
it 'creates new project without path but with name and return 201' do
expect { post api("/projects/user/#{user.id}", admin), params: { name: 'Foo Project' } }.to change { Project.count }.by(1)
@@ -2148,6 +2170,85 @@ describe API::Projects do
end
end
+ describe 'GET /projects/:id/starrers' do
+ shared_examples_for 'project starrers response' do
+ it 'returns an array of starrers' do
+ get api("/projects/#{public_project.id}/starrers", current_user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response[0]['starred_since']).to be_present
+ expect(json_response[0]['user']).to be_present
+ end
+
+ it 'returns the proper security headers' do
+ get api('/projects/1/starrers', current_user)
+
+ expect(response).to include_security_headers
+ end
+ end
+
+ let(:public_project) { create(:project, :public) }
+ let(:private_user) { create(:user, private_profile: true) }
+
+ before do
+ user.update(starred_projects: [public_project])
+ private_user.update(starred_projects: [public_project])
+ end
+
+ it 'returns not_found(404) for not existing project' do
+ get api("/projects/9999999999/starrers", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'public project without user' do
+ it_behaves_like 'project starrers response' do
+ let(:current_user) { nil }
+ end
+
+ it 'returns only starrers with a public profile' do
+ get api("/projects/#{public_project.id}/starrers", nil)
+
+ user_ids = json_response.map { |s| s['user']['id'] }
+ expect(user_ids).to include(user.id)
+ expect(user_ids).not_to include(private_user.id)
+ end
+ end
+
+ context 'public project with user with private profile' do
+ it_behaves_like 'project starrers response' do
+ let(:current_user) { private_user }
+ end
+
+ it 'returns current user with a private profile' do
+ get api("/projects/#{public_project.id}/starrers", private_user)
+
+ user_ids = json_response.map { |s| s['user']['id'] }
+ expect(user_ids).to include(user.id, private_user.id)
+ end
+ end
+
+ context 'private project' do
+ context 'with unauthorized user' do
+ it 'returns not_found for existing but unauthorized project' do
+ get api("/projects/#{project3.id}/starrers", user3)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'without user' do
+ it 'returns not_found for existing but unauthorized project' do
+ get api("/projects/#{project3.id}/starrers", nil)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
describe 'GET /projects/:id/languages' do
context 'with an authorized user' do
it_behaves_like 'languages and percentages JSON response' do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 91cb8760a04..76a70ab6e9e 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -10,10 +10,7 @@ describe API::Services do
end
Service.available_services_names.each do |service|
- # TODO: Remove below `if: (service != "kubernetes")` in the next release
- # KubernetesService was deprecated and it can't be updated. Right now it's
- # only readable. It should be completely removed in the next iteration.
- describe "PUT /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do
+ describe "PUT /projects/:id/services/#{service.dasherize}" do
include_context service
it "updates #{service} settings" do
@@ -62,10 +59,7 @@ describe API::Services do
end
end
- # TODO: Remove below `if: (service != "kubernetes")` in the next release
- # KubernetesService was deprecated and it can't be updated. Right now it's
- # only readable. It should be completely removed in the next iteration.
- describe "DELETE /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do
+ describe "DELETE /projects/:id/services/#{service.dasherize}" do
include_context service
before do
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 55b1419a004..69f105b71a8 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -106,6 +106,30 @@ describe API::Variables do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'creates variable with a specific environment scope' do
+ expect do
+ post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'VALUE_2', environment_scope: 'review/*' }
+ end.to change { project.variables.reload.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['environment_scope']).to eq('review/*')
+ end
+
+ it 'allows duplicated variable key given different environment scopes' do
+ variable = create(:ci_variable, project: project)
+
+ expect do
+ post api("/projects/#{project.id}/variables", user), params: { key: variable.key, value: 'VALUE_2', environment_scope: 'review/*' }
+ end.to change { project.variables.reload.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['key']).to eq(variable.key)
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['environment_scope']).to eq('review/*')
+ end
end
context 'authorized user with invalid permissions' do
diff --git a/spec/serializers/variable_entity_spec.rb b/spec/serializers/variable_entity_spec.rb
index effc0022633..10664ff66ec 100644
--- a/spec/serializers/variable_entity_spec.rb
+++ b/spec/serializers/variable_entity_spec.rb
@@ -8,7 +8,7 @@ describe VariableEntity do
subject { entity.as_json }
it 'contains required fields' do
- expect(subject).to include(:id, :key, :value, :protected)
+ expect(subject).to include(:id, :key, :value, :protected, :environment_scope)
end
end
end
diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb
index f6b6989b955..9b83f65a17e 100644
--- a/spec/services/create_snippet_service_spec.rb
+++ b/spec/services/create_snippet_service_spec.rb
@@ -36,6 +36,22 @@ describe CreateSnippetService do
end
end
+ describe 'usage counter' do
+ let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
+
+ it 'increments count' do
+ expect do
+ create_snippet(nil, @admin, @opts)
+ end.to change { counter.read(:create) }.by 1
+ end
+
+ it 'does not increment count if create fails' do
+ expect do
+ create_snippet(nil, @admin, {})
+ end.not_to change { counter.read(:create) }
+ end
+ end
+
def create_snippet(project, user, opts)
CreateSnippetService.new(project, user, opts).execute
end
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index fb12877fa05..e3a728f2566 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -31,7 +31,159 @@ describe Issuable::BulkUpdateService do
end
end
- context 'with project issuables' do
+ shared_examples 'updating labels' do
+ def create_issue_with_labels(labels)
+ create(:labeled_issue, project: project, labels: labels)
+ end
+
+ let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
+ let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
+ let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
+ let(:issue_no_labels) { create(:issue, project: project) }
+ let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }
+
+ let(:labels) { [] }
+ let(:add_labels) { [] }
+ let(:remove_labels) { [] }
+
+ let(:bulk_update_params) do
+ {
+ label_ids: labels.map(&:id),
+ add_label_ids: add_labels.map(&:id),
+ remove_label_ids: remove_labels.map(&:id)
+ }
+ end
+
+ before do
+ bulk_update(issues, bulk_update_params)
+ end
+
+ context 'when label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_no_labels] }
+ let(:labels) { [bug, regression] }
+
+ it 'updates the labels of all issues passed to the labels passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
+
+ context 'when those label IDs are empty' do
+ let(:labels) { [] }
+
+ it 'updates the issues passed to have no labels' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+ end
+ end
+ end
+
+ context 'when add_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:add_labels) { [bug, regression, merge_requests] }
+
+ it 'adds those label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
+ end
+
+ context 'when remove_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:remove_labels) { [bug, regression, merge_requests] }
+
+ it 'removes those label IDs from all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
+ end
+
+ context 'when add_label_ids and remove_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:add_labels) { [bug] }
+ let(:remove_labels) { [merge_requests] }
+
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
+
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
+ end
+
+ context 'when add_label_ids and label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] }
+ let(:labels) { [merge_requests] }
+ let(:add_labels) { [regression] }
+
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id))
+ end
+
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_no_labels.label_ids).to be_empty
+ end
+ end
+
+ context 'when remove_label_ids and label_ids are passed' do
+ let(:issues) { [issue_no_labels, issue_bug_and_regression] }
+ let(:labels) { [merge_requests] }
+ let(:remove_labels) { [regression] }
+
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(regression.id)
+ end
+
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id)
+ end
+ end
+
+ context 'when add_label_ids, remove_label_ids, and label_ids are passed' do
+ let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] }
+ let(:labels) { [regression] }
+ let(:add_labels) { [bug] }
+ let(:remove_labels) { [merge_requests] }
+
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
+
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
+ end
+
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(regression.id)
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
+ end
+ end
+
+ context 'with issuables at a project level' do
describe 'close issues' do
let(:issues) { create_list(:issue, 2, project: project) }
@@ -178,159 +330,11 @@ describe Issuable::BulkUpdateService do
end
describe 'updating labels' do
- def create_issue_with_labels(labels)
- create(:labeled_issue, project: project, labels: labels)
- end
-
let(:bug) { create(:label, project: project) }
let(:regression) { create(:label, project: project) }
let(:merge_requests) { create(:label, project: project) }
- let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
- let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
- let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
- let(:issue_no_labels) { create(:issue, project: project) }
- let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }
-
- let(:labels) { [] }
- let(:add_labels) { [] }
- let(:remove_labels) { [] }
-
- let(:bulk_update_params) do
- {
- label_ids: labels.map(&:id),
- add_label_ids: add_labels.map(&:id),
- remove_label_ids: remove_labels.map(&:id)
- }
- end
-
- before do
- bulk_update(issues, bulk_update_params)
- end
-
- context 'when label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_no_labels] }
- let(:labels) { [bug, regression] }
-
- it 'updates the labels of all issues passed to the labels passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
- end
-
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
-
- context 'when those label IDs are empty' do
- let(:labels) { [] }
-
- it 'updates the issues passed to have no labels' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
- end
- end
- end
-
- context 'when add_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:add_labels) { [bug, regression, merge_requests] }
-
- it 'adds those label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
- end
-
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
- end
-
- context 'when remove_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:remove_labels) { [bug, regression, merge_requests] }
-
- it 'removes those label IDs from all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
- end
-
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
- end
-
- context 'when add_label_ids and remove_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:add_labels) { [bug] }
- let(:remove_labels) { [merge_requests] }
-
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
- end
-
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
- end
-
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
- end
-
- context 'when add_label_ids and label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] }
- let(:labels) { [merge_requests] }
- let(:add_labels) { [regression] }
-
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id))
- end
-
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
- end
-
- it 'does not update issues not passed in' do
- expect(issue_no_labels.label_ids).to be_empty
- end
- end
-
- context 'when remove_label_ids and label_ids are passed' do
- let(:issues) { [issue_no_labels, issue_bug_and_regression] }
- let(:labels) { [merge_requests] }
- let(:remove_labels) { [regression] }
-
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(regression.id)
- end
-
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
- end
-
- it 'does not update issues not passed in' do
- expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id)
- end
- end
-
- context 'when add_label_ids, remove_label_ids, and label_ids are passed' do
- let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] }
- let(:labels) { [regression] }
- let(:add_labels) { [bug] }
- let(:remove_labels) { [merge_requests] }
-
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
- end
-
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
- end
-
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(regression.id)
- end
-
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
- end
+ it_behaves_like 'updating labels'
end
describe 'subscribe to issues' do
@@ -360,7 +364,7 @@ describe Issuable::BulkUpdateService do
end
end
- context 'with group issuables ' do
+ context 'with issuables at a group level' do
let(:group) { create(:group) }
describe 'updating milestones' do
@@ -387,5 +391,18 @@ describe Issuable::BulkUpdateService do
it_behaves_like 'updates milestones'
end
end
+
+ describe 'updating labels' do
+ let(:project) { create(:project, :repository, group: group) }
+ let(:bug) { create(:group_label, group: group) }
+ let(:regression) { create(:group_label, group: group) }
+ let(:merge_requests) { create(:group_label, group: group) }
+
+ before do
+ group.add_reporter(user)
+ end
+
+ it_behaves_like 'updating labels'
+ end
end
end
diff --git a/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
new file mode 100644
index 00000000000..53b7497ae21
--- /dev/null
+++ b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Metrics::Dashboard::CustomMetricEmbedService do
+ include MetricsDashboardHelpers
+
+ set(:project) { build(:project) }
+ set(:user) { create(:user) }
+ set(:environment) { create(:environment, project: project) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ let(:dashboard_path) { system_dashboard_path }
+ let(:group) { business_metric_title }
+ let(:title) { 'title' }
+ let(:y_label) { 'y_label' }
+
+ describe '.valid_params?' do
+ let(:valid_params) do
+ {
+ embedded: true,
+ dashboard_path: dashboard_path,
+ group: group,
+ title: title,
+ y_label: y_label
+ }
+ end
+
+ subject { described_class.valid_params?(params) }
+
+ let(:params) { valid_params }
+
+ it { is_expected.to be_truthy }
+
+ context 'not embedded' do
+ let(:params) { valid_params.except(:embedded) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'non-system dashboard' do
+ let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'undefined dashboard' do
+ let(:params) { valid_params.except(:dashboard_path) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'non-custom metric group' do
+ let(:group) { 'Different Group' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'missing group' do
+ let(:group) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'missing title' do
+ let(:title) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'undefined y-axis label' do
+ let(:params) { valid_params.except(:y_label) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#get_dashboard' do
+ let(:service_params) do
+ [
+ project,
+ user,
+ {
+ embedded: true,
+ environment: environment,
+ dashboard_path: dashboard_path,
+ group: group,
+ title: title,
+ y_label: y_label
+ }
+ ]
+ end
+
+ let(:service_call) { described_class.new(*service_params).get_dashboard }
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ it_behaves_like 'raises error for users with insufficient permissions'
+
+ context 'the custom metric exists' do
+ let!(:metric) { create(:prometheus_metric, project: project) }
+
+ it_behaves_like 'valid embedded dashboard service response'
+
+ it 'does not cache the unprocessed dashboard' do
+ expect(Gitlab::Metrics::Dashboard::Cache).not_to receive(:fetch)
+
+ described_class.new(*service_params).get_dashboard
+ end
+
+ context 'multiple metrics meet criteria' do
+ let!(:metric_2) { create(:prometheus_metric, project: project, query: 'avg(metric_2)') }
+
+ it_behaves_like 'valid embedded dashboard service response'
+
+ it 'includes both metrics' do
+ result = service_call
+ included_queries = all_queries(result[:dashboard])
+
+ expect(included_queries).to include('avg(metric_2)', 'avg(metric)')
+ end
+ end
+ end
+
+ context 'when the metric exists in another project' do
+ let!(:metric) { create(:prometheus_metric, project: create(:project)) }
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ end
+ end
+
+ private
+
+ def all_queries(dashboard)
+ dashboard[:panel_groups].flat_map do |group|
+ group[:panels].flat_map do |panel|
+ panel[:metrics].map do |metric|
+ metric[:query_range]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
new file mode 100644
index 00000000000..a0f7315f750
--- /dev/null
+++ b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Metrics::Dashboard::DynamicEmbedService, :use_clean_rails_memory_store_caching do
+ include MetricsDashboardHelpers
+
+ set(:project) { build(:project) }
+ set(:user) { create(:user) }
+ set(:environment) { create(:environment, project: project) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
+ let(:group) { 'Group A' }
+ let(:title) { 'Super Chart A1' }
+ let(:y_label) { 'y_label' }
+
+ describe '.valid_params?' do
+ let(:valid_params) do
+ {
+ embedded: true,
+ dashboard_path: dashboard_path,
+ group: group,
+ title: title,
+ y_label: y_label
+ }
+ end
+
+ subject { described_class.valid_params?(params) }
+
+ let(:params) { valid_params }
+
+ it { is_expected.to be_truthy }
+
+ context 'not embedded' do
+ let(:params) { valid_params.except(:embedded) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'undefined dashboard' do
+ let(:params) { valid_params.except(:dashboard_path) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'missing dashboard' do
+ let(:dashboard) { '' }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'missing group' do
+ let(:group) { '' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'missing title' do
+ let(:title) { '' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'undefined y-axis label' do
+ let(:params) { valid_params.except(:y_label) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#get_dashboard' do
+ let(:service_params) do
+ [
+ project,
+ user,
+ {
+ environment: environment,
+ dashboard_path: dashboard_path,
+ group: group,
+ title: title,
+ y_label: y_label
+ }
+ ]
+ end
+
+ let(:service_call) { described_class.new(*service_params).get_dashboard }
+
+ context 'when the dashboard does not exist' do
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ end
+
+ context 'when the dashboard is exists' do
+ let(:project) { project_with_dashboard(dashboard_path) }
+
+ it_behaves_like 'valid embedded dashboard service response'
+ it_behaves_like 'raises error for users with insufficient permissions'
+
+ it 'caches the unprocessed dashboard for subsequent calls' do
+ expect(YAML).to receive(:safe_load).once.and_call_original
+
+ described_class.new(*service_params).get_dashboard
+ described_class.new(*service_params).get_dashboard
+ end
+
+ context 'when the specified group is not present on the dashboard' do
+ let(:group) { 'Group Not Found' }
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ end
+
+ context 'when the specified title is not present on the dashboard' do
+ let(:title) { 'Title Not Found' }
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ end
+
+ context 'when the specified y-axis label is not present on the dashboard' do
+ let(:y_label) { 'Y-Axis Not Found' }
+
+ it_behaves_like 'misconfigured dashboard service response', :not_found
+ end
+ end
+
+ shared_examples 'uses system dashboard' do
+ it 'uses the default dashboard' do
+ expect(Gitlab::Metrics::Dashboard::Finder)
+ .to receive(:find_raw)
+ .with(project, dashboard_path: system_dashboard_path)
+ .once
+
+ service_call
+ end
+ end
+
+ context 'when the dashboard is nil' do
+ let(:dashboard_path) { nil }
+
+ it_behaves_like 'uses system dashboard'
+ end
+
+ context 'when the dashboard is not present' do
+ let(:dashboard_path) { '' }
+
+ it_behaves_like 'uses system dashboard'
+ end
+ end
+end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 46abd8f356a..cd4ea9c401d 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -365,5 +365,43 @@ describe Notes::CreateService do
.and change { existing_note.updated_at }
end
end
+
+ describe "usage counter" do
+ let(:counter) { Gitlab::UsageDataCounters::NoteCounter }
+
+ context 'snippet note' do
+ let(:snippet) { create(:project_snippet, project: project) }
+ let(:opts) { { note: 'reply', noteable_type: 'Snippet', noteable_id: snippet.id, project: project } }
+
+ it 'increments usage counter' do
+ expect do
+ note = described_class.new(project, user, opts).execute
+
+ expect(note).to be_valid
+ end.to change { counter.read(:create, opts[:noteable_type]) }.by 1
+ end
+
+ it 'does not increment usage counter when creation fails' do
+ expect do
+ note = described_class.new(project, user, { note: '' }).execute
+
+ expect(note).to be_invalid
+ end.not_to change { counter.read(:create, opts[:noteable_type]) }
+ end
+ end
+
+ context 'issue note' do
+ let(:issue) { create(:issue, project: project) }
+ let(:opts) { { note: 'reply', noteable_type: 'Issue', noteable_id: issue.id, project: project } }
+
+ it 'does not increment usage counter' do
+ expect do
+ note = described_class.new(project, user, opts).execute
+
+ expect(note).to be_valid
+ end.not_to change { counter.read(:create, opts[:noteable_type]) }
+ end
+ end
+ end
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index e436af77ed4..9a6f64b825a 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -241,6 +241,18 @@ describe Projects::DestroyService do
expect(destroy_project(project, user)).to be false
end
end
+
+ context 'when registry is disabled' do
+ before do
+ stub_container_registry_config(enabled: false)
+ end
+
+ it 'does not attempting to remove any tags' do
+ expect(Projects::ContainerRepository::DestroyService).not_to receive(:new)
+
+ destroy_project(project, user)
+ end
+ end
end
context 'when there are tags for legacy root repository' do
diff --git a/spec/services/self_monitoring/project/create_service_spec.rb b/spec/services/self_monitoring/project/create_service_spec.rb
index 7d4faba526b..def20448bd9 100644
--- a/spec/services/self_monitoring/project/create_service_spec.rb
+++ b/spec/services/self_monitoring/project/create_service_spec.rb
@@ -30,15 +30,13 @@ describe SelfMonitoring::Project::CreateService do
context 'with admin users' do
let(:project) { result[:project] }
+ let(:group) { result[:group] }
+ let(:application_setting) { Gitlab::CurrentSettings.current_application_settings }
let!(:user) { create(:user, :admin) }
before do
- allow(ApplicationSetting)
- .to receive(:current)
- .and_return(
- ApplicationSetting.build_from_defaults(allow_local_requests_from_web_hooks_and_services: true)
- )
+ application_setting.allow_local_requests_from_web_hooks_and_services = true
end
shared_examples 'has prometheus service' do |listen_address|
@@ -55,6 +53,15 @@ describe SelfMonitoring::Project::CreateService do
it_behaves_like 'has prometheus service', 'http://localhost:9090'
+ it 'creates group' do
+ expect(result[:status]).to eq(:success)
+ expect(group).to be_persisted
+ expect(group.name).to eq(described_class::GROUP_NAME)
+ expect(group.path).to start_with(described_class::GROUP_PATH)
+ expect(group.path.split('-').last.length).to eq(8)
+ expect(group.visibility_level).to eq(described_class::VISIBILITY_LEVEL)
+ end
+
it 'creates project with internal visibility' do
expect(result[:status]).to eq(:success)
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
@@ -62,7 +69,7 @@ describe SelfMonitoring::Project::CreateService do
end
it 'creates project with internal visibility even when internal visibility is restricted' do
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
+ application_setting.restricted_visibility_levels = [Gitlab::VisibilityLevel::INTERNAL]
expect(result[:status]).to eq(:success)
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
@@ -71,8 +78,8 @@ describe SelfMonitoring::Project::CreateService do
it 'creates project with correct name and description' do
expect(result[:status]).to eq(:success)
- expect(project.name).to eq(described_class::DEFAULT_NAME)
- expect(project.description).to eq(described_class::DEFAULT_DESCRIPTION)
+ expect(project.name).to eq(described_class::PROJECT_NAME)
+ expect(project.description).to eq(described_class::PROJECT_DESCRIPTION)
end
it 'adds all admins as maintainers' do
@@ -81,25 +88,53 @@ describe SelfMonitoring::Project::CreateService do
create(:user)
expect(result[:status]).to eq(:success)
- expect(project.owner).to eq(user)
- expect(project.members.collect(&:user)).to contain_exactly(user, admin1, admin2)
- expect(project.members.collect(&:access_level)).to contain_exactly(
- Gitlab::Access::MAINTAINER,
+ expect(project.owner).to eq(group)
+ expect(group.members.collect(&:user)).to contain_exactly(user, admin1, admin2)
+ expect(group.members.collect(&:access_level)).to contain_exactly(
+ Gitlab::Access::OWNER,
Gitlab::Access::MAINTAINER,
Gitlab::Access::MAINTAINER
)
end
+ it 'saves the project id' do
+ expect(result[:status]).to eq(:success)
+ expect(application_setting.instance_administration_project_id).to eq(project.id)
+ end
+
+ it 'returns error when saving project ID fails' do
+ allow(application_setting).to receive(:update) { false }
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:failed_step]).to eq(:save_project_id)
+ expect(result[:message]).to eq('Could not save project ID')
+ end
+
+ it 'does not fail when a project already exists' do
+ expect(result[:status]).to eq(:success)
+
+ second_result = subject.execute
+
+ expect(second_result[:status]).to eq(:success)
+ expect(second_result[:project]).to eq(project)
+ expect(second_result[:group]).to eq(group)
+ end
+
context 'when local requests from hooks and services are not allowed' do
before do
- allow(ApplicationSetting)
- .to receive(:current)
- .and_return(
- ApplicationSetting.build_from_defaults(allow_local_requests_from_web_hooks_and_services: false)
- )
+ application_setting.allow_local_requests_from_web_hooks_and_services = false
end
it_behaves_like 'has prometheus service', 'http://localhost:9090'
+
+ it 'does not overwrite the existing whitelist' do
+ application_setting.outbound_local_requests_whitelist = ['example.com']
+
+ expect(result[:status]).to eq(:success)
+ expect(application_setting.outbound_local_requests_whitelist).to contain_exactly(
+ 'example.com', 'localhost'
+ )
+ end
end
context 'with non default prometheus address' do
@@ -175,7 +210,7 @@ describe SelfMonitoring::Project::CreateService do
expect(result).to eq({
status: :error,
message: 'Could not add admins as members',
- failed_step: :add_project_members
+ failed_step: :add_group_members
})
end
end
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
index 23ea4e003f8..0678f54c195 100644
--- a/spec/services/update_snippet_service_spec.rb
+++ b/spec/services/update_snippet_service_spec.rb
@@ -40,6 +40,23 @@ describe UpdateSnippetService do
end
end
+ describe 'usage counter' do
+ let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
+ let(:snippet) { create_snippet(nil, @user, @opts) }
+
+ it 'increments count' do
+ expect do
+ update_snippet(nil, @admin, snippet, @opts)
+ end.to change { counter.read(:update) }.by 1
+ end
+
+ it 'does not increment count if create fails' do
+ expect do
+ update_snippet(nil, @admin, snippet, { title: '' })
+ end.not_to change { counter.read(:update) }
+ end
+ end
+
def create_snippet(project, user, opts)
CreateSnippetService.new(project, user, opts).execute
end
diff --git a/spec/support/helpers/metrics_dashboard_helpers.rb b/spec/support/helpers/metrics_dashboard_helpers.rb
index 1511a2f6b49..0e86b6dfda7 100644
--- a/spec/support/helpers/metrics_dashboard_helpers.rb
+++ b/spec/support/helpers/metrics_dashboard_helpers.rb
@@ -18,6 +18,14 @@ module MetricsDashboardHelpers
project.repository.refresh_method_caches([:metrics_dashboard])
end
+ def system_dashboard_path
+ Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH
+ end
+
+ def business_metric_title
+ PrometheusMetricEnums.group_details[:business][:group_title]
+ end
+
shared_examples_for 'misconfigured dashboard service response' do |status_code|
it 'returns an appropriate message and status code' do
result = service_call
diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
index fb22498f84f..26ed86bfe26 100644
--- a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
@@ -41,7 +41,15 @@ shared_examples 'issuable notes filter' do
get :discussions, params: params.merge(notes_filter: notes_filter)
- expect(user.reload.notes_filter_for(issuable)).to eq(0)
+ expect(user.reload.notes_filter_for(issuable)).to eq(UserPreference::NOTES_FILTERS[:all_notes])
+ end
+
+ it 'does not set notes filter when persist_filter param is false' do
+ notes_filter = UserPreference::NOTES_FILTERS[:only_comments]
+
+ get :discussions, params: params.merge(notes_filter: notes_filter, persist_filter: false)
+
+ expect(user.reload.notes_filter_for(issuable)).to eq(UserPreference::NOTES_FILTERS[:all_notes])
end
it 'returns only user comments' do
diff --git a/spec/support/shared_examples/relative_positioning_shared_examples.rb b/spec/support/shared_examples/relative_positioning_shared_examples.rb
index 9837ba806db..b7382cea93c 100644
--- a/spec/support/shared_examples/relative_positioning_shared_examples.rb
+++ b/spec/support/shared_examples/relative_positioning_shared_examples.rb
@@ -17,8 +17,8 @@ RSpec.shared_examples 'a class that supports relative positioning' do
describe '.move_nulls_to_end' do
it 'moves items with null relative_position to the end' do
- skip("#{item1} has a default relative position") if item1.relative_position
- skip("#{item2} has a default relative position") if item2.relative_position
+ item1.update!(relative_position: nil)
+ item2.update!(relative_position: nil)
described_class.move_nulls_to_end([item1, item2])
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do
end
it 'moves the item near the start position when there are no existing positions' do
- skip("#{item1} has a default relative position") if item1.relative_position
+ item1.update!(relative_position: nil)
described_class.move_nulls_to_end([item1])
diff --git a/spec/views/search/_filter.html.haml_spec.rb b/spec/views/search/_filter.html.haml_spec.rb
new file mode 100644
index 00000000000..d2cd636f8c6
--- /dev/null
+++ b/spec/views/search/_filter.html.haml_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'search/_filter' do
+ context 'when the search page is opened' do
+ it 'displays the correct elements' do
+ render
+
+ expect(rendered).to have_selector('label[for="dashboard_search_group"]')
+ expect(rendered).to have_selector('button#dashboard_search_group')
+
+ expect(rendered).to have_selector('label[for="dashboard_search_project"]')
+ expect(rendered).to have_selector('button#dashboard_search_project')
+ end
+ end
+end
diff --git a/spec/views/search/_form.html.haml_spec.rb b/spec/views/search/_form.html.haml_spec.rb
new file mode 100644
index 00000000000..69f40895d86
--- /dev/null
+++ b/spec/views/search/_form.html.haml_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'search/_form' do
+ context 'when the search page is opened' do
+ it 'displays the correct elements' do
+ render
+
+ expect(rendered).to have_selector('.search-field-holder.form-group')
+ expect(rendered).to have_selector('label[for="dashboard_search"]')
+ end
+ end
+end
diff --git a/spec/views/search/show.html.haml_spec.rb b/spec/views/search/show.html.haml_spec.rb
new file mode 100644
index 00000000000..483b913f2cc
--- /dev/null
+++ b/spec/views/search/show.html.haml_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'search/show' do
+ let(:search_term) { nil }
+
+ before do
+ stub_template "search/_category.html.haml" => 'Category Partial'
+ stub_template "search/_results.html.haml" => 'Results Partial'
+
+ @search_term = search_term
+
+ render
+ end
+
+ context 'when the search page is opened' do
+ it 'displays the title' do
+ expect(rendered).to have_selector('h1.page-title', text: 'Search')
+ expect(rendered).not_to have_selector('h1.page-title code')
+ end
+
+ it 'does not render partials' do
+ expect(rendered).not_to render_template('search/_category')
+ expect(rendered).not_to render_template('search/_results')
+ end
+ end
+
+ context 'when search term is supplied' do
+ let(:search_term) { 'Search Foo' }
+
+ it 'renders partials' do
+ expect(rendered).to render_template('search/_category')
+ expect(rendered).to render_template('search/_results')
+ end
+ end
+end