summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb34
-rw-r--r--spec/factories/application_settings.rb1
-rw-r--r--spec/features/groups/group_settings_spec.rb21
-rw-r--r--spec/features/groups/share_lock_spec.rb31
-rw-r--r--spec/features/groups_spec.rb6
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb191
-rw-r--r--spec/features/projects/merge_requests/user_views_diffs_spec.rb4
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb12
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb75
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb4
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb16
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js103
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js92
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js88
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/helpers.js49
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js75
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js131
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js65
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js87
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js69
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js (renamed from spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js)8
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js52
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/mock_data.js11
-rw-r--r--spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb35
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb24
-rw-r--r--spec/migrations/fill_file_store_spec.rb43
-rw-r--r--spec/models/application_setting_spec.rb126
-rw-r--r--spec/models/internal_id_spec.rb37
-rw-r--r--spec/requests/api/jobs_spec.rb8
-rw-r--r--spec/requests/api/runner_spec.rb2
-rw-r--r--spec/requests/api/settings_spec.rb26
-rw-r--r--spec/requests/lfs_http_spec.rb2
-rw-r--r--spec/rubocop/cop/line_break_around_conditional_block_spec.rb12
-rw-r--r--spec/services/application_settings/update_service_spec.rb88
-rw-r--r--spec/services/check_gcp_project_billing_service_spec.rb32
-rw-r--r--spec/uploaders/object_storage_spec.rb2
-rw-r--r--spec/workers/check_gcp_project_billing_worker_spec.rb116
41 files changed, 1282 insertions, 501 deletions
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index 715bb9f5e52..271ba37aed4 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -77,8 +77,6 @@ describe Projects::Clusters::GcpController do
end
it 'has new object' do
- expect(controller).to receive(:authorize_google_project_billing)
-
go
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
@@ -137,33 +135,15 @@ describe Projects::Clusters::GcpController do
context 'when access token is valid' do
before do
stub_google_api_validate_token
- allow_any_instance_of(described_class).to receive(:authorize_google_project_billing)
- end
-
- context 'when google project billing is enabled' do
- before do
- redis_double = double.as_null_object
- allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
- allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
- end
-
- it 'creates a new cluster' do
- expect(ClusterProvisionWorker).to receive(:perform_async)
- expect { go }.to change { Clusters::Cluster.count }
- .and change { Clusters::Providers::Gcp.count }
- expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
- expect(project.clusters.first).to be_gcp
- expect(project.clusters.first).to be_kubernetes
- end
end
- context 'when google project billing is not enabled' do
- it 'renders the cluster form with an error' do
- go
-
- expect(response).to set_flash.now[:alert]
- expect(response).to render_template('new')
- end
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+ expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+ expect(project.clusters.first).to be_gcp
+ expect(project.clusters.first).to be_kubernetes
end
end
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
index 3ecc90b6573..00c063c49f8 100644
--- a/spec/factories/application_settings.rb
+++ b/spec/factories/application_settings.rb
@@ -1,4 +1,5 @@
FactoryBot.define do
factory :application_setting do
+ default_projects_limit 42
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 1ce30015e81..bf329b0bb94 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -83,7 +83,7 @@ feature 'Edit group settings' do
attach_file(:group_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif'))
- expect { click_button 'Save group' }.to change { group.reload.avatar? }.to(true)
+ expect { save_group }.to change { group.reload.avatar? }.to(true)
end
it 'uploads new group avatar' do
@@ -97,10 +97,19 @@ feature 'Edit group settings' do
expect(page).not_to have_link('Remove avatar')
end
end
-end
-def update_path(new_group_path)
- visit edit_group_path(group)
- fill_in 'group_path', with: new_group_path
- click_button 'Save group'
+ def update_path(new_group_path)
+ visit edit_group_path(group)
+
+ page.within('.gs-advanced') do
+ fill_in 'group_path', with: new_group_path
+ click_button 'Change group path'
+ end
+ end
+
+ def save_group
+ page.within('.gs-general') do
+ click_button 'Save group'
+ end
+ end
end
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index 8842d1391aa..cefbc15e068 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -15,9 +15,8 @@ feature 'Group share with group lock' do
context 'when enabling the parent group share with group lock' do
scenario 'the subgroup share with group lock becomes enabled' do
visit edit_group_path(root_group)
- check 'group_share_with_group_lock'
- click_on 'Save group'
+ enable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_truthy
end
@@ -26,16 +25,15 @@ feature 'Group share with group lock' do
context 'when disabling the parent group share with group lock (which was already enabled)' do
background do
visit edit_group_path(root_group)
- check 'group_share_with_group_lock'
- click_on 'Save group'
+
+ enable_group_lock
end
context 'and the subgroup share with group lock is enabled' do
scenario 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+ disable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_truthy
end
@@ -44,19 +42,32 @@ feature 'Group share with group lock' do
context 'but the subgroup share with group lock is disabled' do
background do
visit edit_group_path(subgroup)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+
+ disable_group_lock
end
scenario 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+ disable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_falsey
end
end
end
end
+
+ def enable_group_lock
+ page.within('.gs-permissions') do
+ check 'group_share_with_group_lock'
+ click_on 'Save group'
+ end
+ end
+
+ def disable_group_lock
+ page.within('.gs-permissions') do
+ uncheck 'group_share_with_group_lock'
+ click_on 'Save group'
+ end
+ end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index c1f3d94bc20..236768b5d7f 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -141,8 +141,10 @@ feature 'Group' do
end
it 'saves new settings' do
- fill_in 'group_name', with: new_name
- click_button 'Save group'
+ page.within('.gs-general') do
+ fill_in 'group_name', with: new_name
+ click_button 'Save group'
+ end
expect(page).to have_content 'successfully updated'
expect(find('#group_name').value).to eq(new_name)
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index a8a627d8806..c85b82b2090 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -22,152 +22,123 @@ feature 'Gcp Cluster', :js do
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
end
- context 'when user has a GCP project with billing enabled' do
+ context 'when user does not have a cluster and visits cluster index page' do
before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(true)
- end
-
- context 'when user does not have a cluster and visits cluster index page' do
- before do
- visit project_clusters_path(project)
-
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
- end
-
- context 'when user filled form with valid parameters' do
- before do
- allow_any_instance_of(GoogleApi::CloudPlatform::Client)
- .to receive(:projects_zones_clusters_create) do
- OpenStruct.new(
- self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
- status: 'RUNNING'
- )
- end
-
- allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
-
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
- end
+ visit project_clusters_path(project)
- it 'user sees a cluster details page and creation status' do
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+ click_link 'Add Kubernetes cluster'
+ click_link 'Create on Google Kubernetes Engine'
+ end
- Clusters::Cluster.last.provider.make_created!
+ context 'when user filled form with valid parameters' do
+ subject { click_button 'Create Kubernetes cluster' }
- expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
+ before do
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
end
- it 'user sees a error if something worng during creation' do
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+ sleep 2 # wait for ajax
+ execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
- expect(page).to have_content('Something wrong!')
- end
+ fill_in 'cluster[name]', with: 'dev-cluster'
+ fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
+ fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
+ fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
end
- context 'when user filled form with invalid parameters' do
- before do
- click_button 'Create Kubernetes cluster'
- end
-
- it 'user sees a validation error' do
- expect(page).to have_css('#error_explanation')
- end
+ it 'users sees a form with the GCP token' do
+ expect(page).to have_selector(:css, 'form[data-token="token"]')
end
- end
- context 'when user does have a cluster and visits cluster page' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ it 'user sees a cluster details page and creation status' do
+ subject
- before do
- visit project_cluster_path(project, cluster)
- end
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_created!
- it 'user sees a cluster details page' do
- expect(page).to have_button('Save changes')
- expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
+ expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
end
- context 'when user disables the cluster' do
- before do
- page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
- page.within('#cluster-integration') { click_button 'Save changes' }
- end
+ it 'user sees a error if something wrong during creation' do
+ subject
- it 'user sees the successful message' do
- expect(page).to have_content('Kubernetes cluster was successfully updated.')
- end
- end
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
- context 'when user changes cluster parameters' do
- before do
- fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- page.within('#js-cluster-details') { click_button 'Save changes' }
- end
+ Clusters::Cluster.last.provider.make_errored!('Something wrong!')
- it 'user sees the successful message' do
- expect(page).to have_content('Kubernetes cluster was successfully updated.')
- expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
- end
+ expect(page).to have_content('Something wrong!')
end
+ end
- context 'when user destroy the cluster' do
- before do
- page.accept_confirm do
- click_link 'Remove integration'
- end
- end
+ context 'when user filled form with invalid parameters' do
+ before do
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+ click_button 'Create Kubernetes cluster'
+ end
- it 'user sees creation form with the successful message' do
- expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
- expect(page).to have_link('Add Kubernetes cluster')
- end
+ it 'user sees a validation error' do
+ expect(page).to have_css('#error_explanation')
end
end
end
- context 'when user does not have a GCP project with billing enabled' do
- before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(false)
-
- visit project_clusters_path(project)
-
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ context 'when user does have a cluster and visits cluster page' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
+ before do
+ visit project_cluster_path(project, cluster)
end
- it 'user sees form with error' do
- expect(page).to have_content('Please enable billing for one of your projects to be able to create a Kubernetes cluster, then try again.')
+ it 'user sees a cluster details page' do
+ expect(page).to have_button('Save changes')
+ expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
end
- end
- context 'when gcp billing status is not in redis' do
- before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(nil)
+ context 'when user disables the cluster' do
+ before do
+ page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
+ page.within('#cluster-integration') { click_button 'Save changes' }
+ end
- visit project_clusters_path(project)
+ it 'user sees the successful message' do
+ expect(page).to have_content('Kubernetes cluster was successfully updated.')
+ end
+ end
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ context 'when user changes cluster parameters' do
+ before do
+ fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
+ end
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
+ it 'user sees the successful message' do
+ expect(page).to have_content('Kubernetes cluster was successfully updated.')
+ expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
+ end
end
- it 'user sees form with error' do
- expect(page).to have_content('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
+ context 'when user destroy the cluster' do
+ before do
+ page.accept_confirm do
+ click_link 'Remove integration'
+ end
+ end
+
+ it 'user sees creation form with the successful message' do
+ expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
+ expect(page).to have_link('Add Kubernetes cluster')
+ end
end
end
end
diff --git a/spec/features/projects/merge_requests/user_views_diffs_spec.rb b/spec/features/projects/merge_requests/user_views_diffs_spec.rb
index 295eb02b625..d36aafdbc54 100644
--- a/spec/features/projects/merge_requests/user_views_diffs_spec.rb
+++ b/spec/features/projects/merge_requests/user_views_diffs_spec.rb
@@ -26,6 +26,10 @@ describe 'User views diffs', :js do
expect(page).to have_css('#inline-diff-btn', count: 1)
end
+ it 'hides loading spinner after load' do
+ expect(page).not_to have_selector('.mr-loading-status .loading', visible: true)
+ end
+
context 'when in the inline view' do
include_examples 'unfold diffs'
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index 8a9255db9e8..ee5734a9bf1 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -44,6 +44,18 @@ describe 'Projects > User sees sidebar' do
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'CI / CD'
expect(page).not_to have_content 'Merge Requests'
+ expect(page).not_to have_content 'Operations'
+ end
+ end
+
+ it 'shows build tab if builds are public' do
+ project.public_builds = true
+ project.save
+
+ visit project_path(project)
+
+ within('.nav-sidebar') do
+ expect(page).to have_content 'CI / CD'
end
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index e473739a6aa..bbdd98a7623 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -19,6 +19,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
visit project_path(project)
find('.shortcuts-wiki').click
+ click_link "Create your first page"
end
context "while creating a new wiki page" do
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 9989e1ffda7..706894f4b32 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -8,6 +8,7 @@ describe "User creates wiki page" do
sign_in(user)
visit(project_wikis_path(project))
+ click_link "Create your first page"
end
context "when wiki is empty" do
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index e019e3ce5a5..272dac127dd 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -11,6 +11,7 @@ describe 'User updates wiki page' do
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
+ click_link "Create your first page"
end
context 'in a user namespace' do
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
new file mode 100644
index 00000000000..83ffbb4a94e
--- /dev/null
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe 'User views empty wiki' do
+ let(:user) { create(:user) }
+
+ shared_examples 'empty wiki and accessible issues' do
+ it 'show "issue tracker" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content('This project has no wiki pages')
+ expect(element).to have_link("issue tracker", href: project_issues_path(project))
+ expect(element).to have_link("Suggest wiki improvement", href: new_project_issue_path(project))
+ end
+ end
+
+ shared_examples 'empty wiki and non-accessible issues' do
+ it 'does not show "issue tracker" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content('This project has no wiki pages')
+ expect(element).to have_no_link('Suggest wiki improvement')
+ end
+ end
+
+ context 'when user is logged out and issue tracker is public' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ it_behaves_like 'empty wiki and accessible issues'
+ end
+
+ context 'when user is logged in and not a member' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ before do
+ sign_in(user)
+ end
+
+ it_behaves_like 'empty wiki and accessible issues'
+ end
+
+ context 'when issue tracker is private' do
+ let(:project) { create(:project, :public, :wiki_repo, :issues_private) }
+
+ it_behaves_like 'empty wiki and non-accessible issues'
+ end
+
+ context 'when issue tracker is disabled' do
+ let(:project) { create(:project, :public, :wiki_repo, :issues_disabled) }
+
+ it_behaves_like 'empty wiki and non-accessible issues'
+ end
+
+ context 'when user is logged in and a memeber' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ it 'show "create first page" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ element.click_link 'Create your first page'
+
+ expect(page).to have_button('Create page')
+ end
+ end
+end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 6661714222a..1de7d9a56a8 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -18,6 +18,7 @@ describe 'User views a wiki page' do
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
+ click_link "Create your first page"
click_on('New page')
@@ -140,6 +141,7 @@ describe 'User views a wiki page' do
visit(project_path(project))
find('.shortcuts-wiki').click
+ click_link "Create your first page"
expect(page).to have_content('Home · Create Page')
end
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index e8884bc1a00..c8db82a562f 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -14,7 +14,9 @@ feature 'User uploads avatar to group' do
visible: false
)
- click_button 'Save group'
+ page.within('.gs-general') do
+ click_button 'Save group'
+ end
visit group_path(group)
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 7bede0b0d48..5478e38ce70 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -26,18 +26,23 @@ describe 'Users > User browses projects on user page', :js do
end
end
+ it 'hides loading spinner after load', :js do
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ wait_for_requests
+
+ expect(page).not_to have_selector('.loading-status .loading', visible: true)
+ end
+
it 'paginates projects', :js do
project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since)
project2 = create(:project, namespace: user.namespace, updated_at: 1.minute.since)
allow(Project).to receive(:default_per_page).and_return(1)
sign_in(user)
-
visit user_path(user)
-
- page.within('.user-profile-nav') do
- click_link('Personal projects')
- end
+ click_nav_link('Personal projects')
wait_for_requests
@@ -92,7 +97,6 @@ describe 'Users > User browses projects on user page', :js do
click_nav_link('Personal projects')
expect(title).to start_with(user.name)
-
expect(page).to have_content(private_project.name)
expect(page).to have_content(public_project.name)
expect(page).to have_content(internal_project.name)
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
new file mode 100644
index 00000000000..21805ef0b28
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
@@ -0,0 +1,103 @@
+import Vue from 'vue';
+import GkeMachineTypeDropdown from '~/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import {
+ SET_PROJECT,
+ SET_PROJECT_BILLING_STATUS,
+ SET_ZONE,
+ SET_MACHINE_TYPES,
+} from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import {
+ selectedZoneMock,
+ selectedProjectMock,
+ selectedMachineTypeMock,
+ gapiMachineTypesResponseMock,
+} from '../mock_data';
+
+const componentConfig = {
+ fieldId: 'cluster_provider_gcp_attributes_gcp_machine_type',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_machine_type]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching machine types',
+ DISABLED_NO_PROJECT: 'Select project and zone to choose machine type',
+ DISABLED_NO_ZONE: 'Select zone to choose machine type',
+ DEFAULT: 'Select machine type',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeMachineTypeDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeMachineTypeDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('shows various toggle text depending on state', () => {
+ it('returns disabled state toggle text when no project and zone are selected', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_PROJECT);
+ });
+
+ it('returns disabled state toggle text when no zone is selected', () => {
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_ZONE);
+ });
+
+ it('returns loading toggle text', () => {
+ vm.isLoading = true;
+
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_PROJECT);
+
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+ vm.$store.commit(SET_ZONE, selectedZoneMock);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ });
+
+ it('returns machine type name if machine type selected', () => {
+ vm.setItem(selectedMachineTypeMock);
+
+ expect(vm.toggleText).toBe(selectedMachineTypeMock);
+ });
+ });
+
+ describe('form input', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+ vm.$store.commit(SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items);
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
new file mode 100644
index 00000000000..d4fcb2dc8ff
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
@@ -0,0 +1,92 @@
+import Vue from 'vue';
+import GkeProjectIdDropdown from '~/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import { SET_PROJECTS } from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { emptyProjectMock, selectedProjectMock } from '../mock_data';
+
+const componentConfig = {
+ docsUrl: 'https://console.cloud.google.com/home/dashboard',
+ fieldId: 'cluster_provider_gcp_attributes_gcp_project_id',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_project_id]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching projects',
+ VALIDATING_PROJECT_BILLING: 'Validating project billing status',
+ DEFAULT: 'Select project',
+ EMPTY: 'No projects found',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeProjectIdDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeProjectIdDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('toggleText', () => {
+ it('returns loading toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns project billing validation text', () => {
+ vm.setIsValidatingProjectBilling(true);
+ expect(vm.toggleText).toBe(LABELS.VALIDATING_PROJECT_BILLING);
+ });
+
+ it('returns default toggle text', done =>
+ vm.$nextTick().then(() => {
+ vm.setItem(emptyProjectMock);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ done();
+ }));
+
+ it('returns project name if project selected', done =>
+ vm.$nextTick().then(() => {
+ expect(vm.toggleText).toBe(selectedProjectMock.name);
+ done();
+ }));
+
+ it('returns empty toggle text', done =>
+ vm.$nextTick().then(() => {
+ vm.$store.commit(SET_PROJECTS, null);
+ vm.setItem(emptyProjectMock);
+
+ expect(vm.toggleText).toBe(LABELS.EMPTY);
+ done();
+ }));
+ });
+
+ describe('selectItem', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
new file mode 100644
index 00000000000..89a4a7ea2ce
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
@@ -0,0 +1,88 @@
+import Vue from 'vue';
+import GkeZoneDropdown from '~/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import {
+ SET_PROJECT,
+ SET_ZONES,
+ SET_PROJECT_BILLING_STATUS,
+} from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { selectedZoneMock, selectedProjectMock, gapiZonesResponseMock } from '../mock_data';
+
+const componentConfig = {
+ fieldId: 'cluster_provider_gcp_attributes_gcp_zone',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_zone]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching zones',
+ DISABLED: 'Select project to choose zone',
+ DEFAULT: 'Select zone',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeZoneDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeZoneDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('toggleText', () => {
+ it('returns disabled state toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED);
+ });
+
+ it('returns loading toggle text', () => {
+ vm.isLoading = true;
+
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED);
+
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ });
+
+ it('returns project name if project selected', () => {
+ vm.setItem(selectedZoneMock);
+
+ expect(vm.toggleText).toBe(selectedZoneMock);
+ });
+ });
+
+ describe('selectItem', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+ vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items);
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js b/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js
new file mode 100644
index 00000000000..6df511e9157
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js
@@ -0,0 +1,49 @@
+import {
+ gapiProjectsResponseMock,
+ gapiZonesResponseMock,
+ gapiMachineTypesResponseMock,
+} from './mock_data';
+
+// eslint-disable-next-line import/prefer-default-export
+export const gapi = () => ({
+ client: {
+ cloudbilling: {
+ projects: {
+ getBillingInfo: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { billingEnabled: true },
+ });
+ }),
+ },
+ },
+ cloudresourcemanager: {
+ projects: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiProjectsResponseMock },
+ });
+ }),
+ },
+ },
+ compute: {
+ zones: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiZonesResponseMock },
+ });
+ }),
+ },
+ machineTypes: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiMachineTypesResponseMock },
+ });
+ }),
+ },
+ },
+ },
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js b/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js
new file mode 100644
index 00000000000..d9f5dbc636f
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js
@@ -0,0 +1,75 @@
+export const emptyProjectMock = {
+ projectId: '',
+ name: '',
+};
+
+export const selectedProjectMock = {
+ projectId: 'gcp-project-123',
+ name: 'gcp-project',
+};
+
+export const selectedZoneMock = 'us-central1-a';
+
+export const selectedMachineTypeMock = 'n1-standard-2';
+
+export const gapiProjectsResponseMock = {
+ projects: [
+ {
+ projectNumber: '1234',
+ projectId: 'gcp-project-123',
+ lifecycleState: 'ACTIVE',
+ name: 'gcp-project',
+ createTime: '2017-12-16T01:48:29.129Z',
+ parent: {
+ type: 'organization',
+ id: '12345',
+ },
+ },
+ ],
+};
+
+export const gapiZonesResponseMock = {
+ kind: 'compute#zoneList',
+ id: 'projects/gitlab-internal-153318/zones',
+ items: [
+ {
+ kind: 'compute#zone',
+ id: '2000',
+ creationTimestamp: '1969-12-31T16:00:00.000-08:00',
+ name: 'us-central1-a',
+ description: 'us-central1-a',
+ status: 'UP',
+ region:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/regions/us-central1',
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a',
+ availableCpuPlatforms: ['Intel Skylake', 'Intel Broadwell', 'Intel Sandy Bridge'],
+ },
+ ],
+ selfLink: 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones',
+};
+
+export const gapiMachineTypesResponseMock = {
+ kind: 'compute#machineTypeList',
+ id: 'projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
+ items: [
+ {
+ kind: 'compute#machineType',
+ id: '3002',
+ creationTimestamp: '1969-12-31T16:00:00.000-08:00',
+ name: 'n1-standard-2',
+ description: '2 vCPUs, 7.5 GB RAM',
+ guestCpus: 2,
+ memoryMb: 7680,
+ imageSpaceGb: 10,
+ maximumPersistentDisks: 64,
+ maximumPersistentDisksSizeGb: '65536',
+ zone: 'us-central1-a',
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes/n1-standard-2',
+ isSharedCpu: false,
+ },
+ ],
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
+};
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js
new file mode 100644
index 00000000000..9d892b8185b
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js
@@ -0,0 +1,131 @@
+import testAction from 'spec/helpers/vuex_action_helper';
+import * as actions from '~/projects/gke_cluster_dropdowns/store/actions';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import { gapi } from '../helpers';
+import { selectedProjectMock, selectedZoneMock, selectedMachineTypeMock } from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Actions', () => {
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ describe('setProject', () => {
+ it('should set project', done => {
+ testAction(
+ actions.setProject,
+ selectedProjectMock,
+ { selectedProject: {} },
+ [{ type: 'SET_PROJECT', payload: selectedProjectMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setZone', () => {
+ it('should set zone', done => {
+ testAction(
+ actions.setZone,
+ selectedZoneMock,
+ { selectedZone: '' },
+ [{ type: 'SET_ZONE', payload: selectedZoneMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setMachineType', () => {
+ it('should set machine type', done => {
+ testAction(
+ actions.setMachineType,
+ selectedMachineTypeMock,
+ { selectedMachineType: '' },
+ [{ type: 'SET_MACHINE_TYPE', payload: selectedMachineTypeMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setIsValidatingProjectBilling', () => {
+ it('should set machine type', done => {
+ testAction(
+ actions.setIsValidatingProjectBilling,
+ true,
+ { isValidatingProjectBilling: null },
+ [{ type: 'SET_IS_VALIDATING_PROJECT_BILLING', payload: true }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('async fetch methods', () => {
+ window.gapi = gapi();
+
+ describe('fetchProjects', () => {
+ it('fetches projects from Google API', done => {
+ store
+ .dispatch('fetchProjects')
+ .then(() => {
+ expect(store.state.projects[0].projectId).toEqual(selectedProjectMock.projectId);
+ expect(store.state.projects[0].name).toEqual(selectedProjectMock.name);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('validateProjectBilling', () => {
+ it('checks project billing status from Google API', done => {
+ testAction(
+ actions.validateProjectBilling,
+ true,
+ {
+ selectedProject: selectedProjectMock,
+ selectedZone: '',
+ selectedMachineType: '',
+ projectHasBillingEnabled: null,
+ },
+ [
+ { type: 'SET_ZONE', payload: '' },
+ { type: 'SET_MACHINE_TYPE', payload: '' },
+ { type: 'SET_PROJECT_BILLING_STATUS', payload: true },
+ ],
+ [{ type: 'setIsValidatingProjectBilling', payload: false }],
+ done,
+ );
+ });
+ });
+
+ describe('fetchZones', () => {
+ it('fetches zones from Google API', done => {
+ store
+ .dispatch('fetchZones')
+ .then(() => {
+ expect(store.state.zones[0].name).toEqual(selectedZoneMock);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('fetchMachineTypes', () => {
+ it('fetches machine types from Google API', done => {
+ store
+ .dispatch('fetchMachineTypes')
+ .then(() => {
+ expect(store.state.machineTypes[0].name).toEqual(selectedMachineTypeMock);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js
new file mode 100644
index 00000000000..6f89158f807
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js
@@ -0,0 +1,65 @@
+import * as getters from '~/projects/gke_cluster_dropdowns/store/getters';
+import { selectedProjectMock, selectedZoneMock, selectedMachineTypeMock } from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Getters', () => {
+ let state;
+
+ describe('valid states', () => {
+ beforeEach(() => {
+ state = {
+ selectedProject: selectedProjectMock,
+ selectedZone: selectedZoneMock,
+ selectedMachineType: selectedMachineTypeMock,
+ };
+ });
+
+ describe('hasProject', () => {
+ it('should return true when project is selected', () => {
+ expect(getters.hasProject(state)).toEqual(true);
+ });
+ });
+
+ describe('hasZone', () => {
+ it('should return true when zone is selected', () => {
+ expect(getters.hasZone(state)).toEqual(true);
+ });
+ });
+
+ describe('hasMachineType', () => {
+ it('should return true when machine type is selected', () => {
+ expect(getters.hasMachineType(state)).toEqual(true);
+ });
+ });
+ });
+
+ describe('invalid states', () => {
+ beforeEach(() => {
+ state = {
+ selectedProject: {
+ projectId: '',
+ name: '',
+ },
+ selectedZone: '',
+ selectedMachineType: '',
+ };
+ });
+
+ describe('hasProject', () => {
+ it('should return false when project is not selected', () => {
+ expect(getters.hasProject(state)).toEqual(false);
+ });
+ });
+
+ describe('hasZone', () => {
+ it('should return false when zone is not selected', () => {
+ expect(getters.hasZone(state)).toEqual(false);
+ });
+ });
+
+ describe('hasMachineType', () => {
+ it('should return false when machine type is not selected', () => {
+ expect(getters.hasMachineType(state)).toEqual(false);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js
new file mode 100644
index 00000000000..7f8c4f314e4
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js
@@ -0,0 +1,87 @@
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import * as types from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import {
+ selectedProjectMock,
+ selectedZoneMock,
+ selectedMachineTypeMock,
+ gapiProjectsResponseMock,
+ gapiZonesResponseMock,
+ gapiMachineTypesResponseMock,
+} from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Mutations', () => {
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ describe('SET_PROJECT', () => {
+ it('should set GCP project as selectedProject', () => {
+ const projectToSelect = gapiProjectsResponseMock.projects[0];
+
+ store.commit(types.SET_PROJECT, projectToSelect);
+
+ expect(store.state.selectedProject.projectId).toEqual(selectedProjectMock.projectId);
+ expect(store.state.selectedProject.name).toEqual(selectedProjectMock.name);
+ });
+ });
+
+ describe('SET_PROJECT_BILLING_STATUS', () => {
+ it('should set project billing status', () => {
+ store.commit(types.SET_PROJECT_BILLING_STATUS, true);
+
+ expect(store.state.projectHasBillingEnabled).toBeTruthy();
+ });
+ });
+
+ describe('SET_ZONE', () => {
+ it('should set GCP zone as selectedZone', () => {
+ const zoneToSelect = gapiZonesResponseMock.items[0].name;
+
+ store.commit(types.SET_ZONE, zoneToSelect);
+
+ expect(store.state.selectedZone).toEqual(selectedZoneMock);
+ });
+ });
+
+ describe('SET_MACHINE_TYPE', () => {
+ it('should set GCP machine type as selectedMachineType', () => {
+ const machineTypeToSelect = gapiMachineTypesResponseMock.items[0].name;
+
+ store.commit(types.SET_MACHINE_TYPE, machineTypeToSelect);
+
+ expect(store.state.selectedMachineType).toEqual(selectedMachineTypeMock);
+ });
+ });
+
+ describe('SET_PROJECTS', () => {
+ it('should set Google API Projects response as projects', () => {
+ expect(store.state.projects.length).toEqual(0);
+
+ store.commit(types.SET_PROJECTS, gapiProjectsResponseMock.projects);
+
+ expect(store.state.projects.length).toEqual(gapiProjectsResponseMock.projects.length);
+ });
+ });
+
+ describe('SET_ZONES', () => {
+ it('should set Google API Zones response as zones', () => {
+ expect(store.state.zones.length).toEqual(0);
+
+ store.commit(types.SET_ZONES, gapiZonesResponseMock.items);
+
+ expect(store.state.zones.length).toEqual(gapiZonesResponseMock.items.length);
+ });
+ });
+
+ describe('SET_MACHINE_TYPES', () => {
+ it('should set Google API Machine Types response as machineTypes', () => {
+ expect(store.state.machineTypes.length).toEqual(0);
+
+ store.commit(types.SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items);
+
+ expect(store.state.machineTypes.length).toEqual(gapiMachineTypesResponseMock.items.length);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
new file mode 100644
index 00000000000..ba897f4660d
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
@@ -0,0 +1,69 @@
+import Vue from 'vue';
+
+import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const defaultLabel = 'Select';
+const customLabel = 'Select project';
+
+const createComponent = config => {
+ const Component = Vue.extend(dropdownButtonComponent);
+
+ return mountComponent(Component, config);
+};
+
+describe('DropdownButtonComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('dropdownToggleText', () => {
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(defaultLabel);
+ });
+
+ it('returns custom toggle text when provided via props', () => {
+ const vmEmptyLabels = createComponent({ toggleText: customLabel });
+
+ expect(vmEmptyLabels.toggleText).toBe(customLabel);
+ vmEmptyLabels.$destroy();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element of type `button`', () => {
+ expect(vm.$el.nodeName).toBe('BUTTON');
+ });
+
+ it('renders component container element with required data attributes', () => {
+ expect(vm.$el.dataset.abilityName).toBe(vm.abilityName);
+ expect(vm.$el.dataset.fieldName).toBe(vm.fieldName);
+ expect(vm.$el.dataset.issueUpdate).toBe(vm.updatePath);
+ expect(vm.$el.dataset.labels).toBe(vm.labelsPath);
+ expect(vm.$el.dataset.namespacePath).toBe(vm.namespace);
+ expect(vm.$el.dataset.showAny).not.toBeDefined();
+ });
+
+ it('renders dropdown toggle text element', () => {
+ const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text');
+ expect(dropdownToggleTextEl).not.toBeNull();
+ expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel);
+ });
+
+ it('renders dropdown button icon', () => {
+ const dropdownIconEl = vm.$el.querySelector('.dropdown-toggle-icon i.fa');
+
+ expect(dropdownIconEl).not.toBeNull();
+ expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js
index 88733922a59..445ab0cb40e 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js
@@ -1,17 +1,17 @@
import Vue from 'vue';
-import dropdownHiddenInputComponent from '~/vue_shared/components/sidebar/labels_select/dropdown_hidden_input.vue';
+import dropdownHiddenInputComponent from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockLabels } from './mock_data';
-const createComponent = (name = 'label_id[]', label = mockLabels[0]) => {
+const createComponent = (name = 'label_id[]', value = mockLabels[0].id) => {
const Component = Vue.extend(dropdownHiddenInputComponent);
return mountComponent(Component, {
name,
- label,
+ value,
});
};
@@ -31,7 +31,7 @@ describe('DropdownHiddenInputComponent', () => {
expect(vm.$el.nodeName).toBe('INPUT');
expect(vm.$el.getAttribute('type')).toBe('hidden');
expect(vm.$el.getAttribute('name')).toBe(vm.name);
- expect(vm.$el.getAttribute('value')).toBe(`${vm.label.id}`);
+ expect(vm.$el.getAttribute('value')).toBe(`${vm.value}`);
});
});
});
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
new file mode 100644
index 00000000000..551520721e5
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
@@ -0,0 +1,52 @@
+import Vue from 'vue';
+
+import dropdownSearchInputComponent from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const componentConfig = {
+ placeholderText: 'Search something',
+};
+
+const createComponent = (config = componentConfig) => {
+ const Component = Vue.extend(dropdownSearchInputComponent);
+
+ return mountComponent(Component, config);
+};
+
+describe('DropdownSearchInputComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('template', () => {
+ it('renders input element with type `search`', () => {
+ const inputEl = vm.$el.querySelector('input.dropdown-input-field');
+
+ expect(inputEl).not.toBeNull();
+ expect(inputEl.getAttribute('type')).toBe('search');
+ });
+
+ it('renders search icon element', () => {
+ expect(vm.$el.querySelector('.fa-search.dropdown-input-search')).not.toBeNull();
+ });
+
+ it('renders clear search icon element', () => {
+ expect(
+ vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'),
+ ).not.toBeNull();
+ });
+
+ it('displays custom placeholder text', () => {
+ const inputEl = vm.$el.querySelector('input.dropdown-input-field');
+
+ expect(inputEl.getAttribute('placeholder')).toBe(componentConfig.placeholderText);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/dropdown/mock_data.js b/spec/javascripts/vue_shared/components/dropdown/mock_data.js
new file mode 100644
index 00000000000..b09d42da401
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/mock_data.js
@@ -0,0 +1,11 @@
+export const mockLabels = [
+ {
+ id: 26,
+ title: 'Foo Label',
+ description: 'Foobar',
+ color: '#BADA55',
+ text_color: '#FFFFFF',
+ },
+];
+
+export default mockLabels;
diff --git a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
new file mode 100644
index 00000000000..f47b9dd3498
--- /dev/null
+++ b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::GrapeLogging::Loggers::QueueDurationLogger do
+ subject { described_class.new }
+
+ describe ".parameters" do
+ let(:start_time) { Time.new(2018, 01, 01) }
+
+ describe 'when no proxy time is available' do
+ let(:mock_request) { OpenStruct.new(env: {}) }
+
+ it 'returns an empty hash' do
+ expect(subject.parameters(mock_request, nil)).to eq({})
+ end
+ end
+
+ describe 'when a proxy time is available' do
+ let(:mock_request) do
+ OpenStruct.new(
+ env: {
+ 'HTTP_GITLAB_WORKHORSE_PROXY_START' => (start_time - 1.hour).to_i * (10**9)
+ }
+ )
+ end
+
+ it 'returns the correct duration in ms' do
+ Timecop.freeze(start_time) do
+ subject.before
+
+ expect(subject.parameters(mock_request, nil)).to eq( { 'queue_duration': 1.hour.to_f * 1000 })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index db9d9158b29..27cb3198e5b 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -50,30 +50,6 @@ describe GoogleApi::CloudPlatform::Client do
end
end
- describe '#projects_list' do
- subject { client.projects_list }
- let(:projects) { double }
-
- before do
- allow_any_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService)
- .to receive(:fetch_all).and_return(projects)
- end
-
- it { is_expected.to eq(projects) }
- end
-
- describe '#projects_get_billing_info' do
- subject { client.projects_get_billing_info('project') }
- let(:billing_info) { double }
-
- before do
- allow_any_instance_of(Google::Apis::CloudbillingV1::CloudbillingService)
- .to receive(:get_project_billing_info).and_return(billing_info)
- end
-
- it { is_expected.to eq(billing_info) }
- end
-
describe '#projects_zones_clusters_get' do
subject { client.projects_zones_clusters_get(spy, spy, spy) }
let(:gke_cluster) { double }
diff --git a/spec/migrations/fill_file_store_spec.rb b/spec/migrations/fill_file_store_spec.rb
new file mode 100644
index 00000000000..5ff7aa56ce2
--- /dev/null
+++ b/spec/migrations/fill_file_store_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180424151928_fill_file_store')
+
+describe FillFileStore, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:builds) { table(:ci_builds) }
+ let(:job_artifacts) { table(:ci_job_artifacts) }
+ let(:lfs_objects) { table(:lfs_objects) }
+ let(:uploads) { table(:uploads) }
+
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ builds.create!(id: 1)
+
+ ##
+ # Create rows that have nullfied `file_store` column
+ job_artifacts.create!(project_id: 123, job_id: 1, file_type: 1, file_store: nil)
+ lfs_objects.create!(oid: 123, size: 10, file: 'file_name', file_store: nil)
+ uploads.create!(size: 10, path: 'path', uploader: 'uploader', mount_point: 'file_name', store: nil)
+ end
+
+ it 'correctly migrates nullified file_store/store column' do
+ expect(job_artifacts.where(file_store: nil).count).to eq(1)
+ expect(lfs_objects.where(file_store: nil).count).to eq(1)
+ expect(uploads.where(store: nil).count).to eq(1)
+
+ expect(job_artifacts.where(file_store: 1).count).to eq(0)
+ expect(lfs_objects.where(file_store: 1).count).to eq(0)
+ expect(uploads.where(store: 1).count).to eq(0)
+
+ migrate!
+
+ expect(job_artifacts.where(file_store: nil).count).to eq(0)
+ expect(lfs_objects.where(file_store: nil).count).to eq(0)
+ expect(uploads.where(store: nil).count).to eq(0)
+
+ expect(job_artifacts.where(file_store: 1).count).to eq(1)
+ expect(lfs_objects.where(file_store: 1).count).to eq(1)
+ expect(uploads.where(store: 1).count).to eq(1)
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 7e47043a1cb..f8f07205623 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -391,68 +391,6 @@ describe ApplicationSetting do
end
describe 'performance bar settings' do
- describe 'performance_bar_allowed_group_id=' do
- context 'with a blank path' do
- before do
- setting.performance_bar_allowed_group_id = create(:group).full_path
- end
-
- it 'persists nil for a "" path and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = ''
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- end
- end
-
- context 'with an invalid path' do
- it 'does not persist an invalid group path' do
- setting.performance_bar_allowed_group_id = 'foo'
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- end
- end
-
- context 'with a path to an existing group' do
- let(:group) { create(:group) }
-
- it 'persists a valid group path and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = group.full_path
-
- expect(setting.performance_bar_allowed_group_id).to eq(group.id)
- end
-
- context 'when the given path is the same' do
- context 'with a blank path' do
- before do
- setting.performance_bar_allowed_group_id = nil
- end
-
- it 'clears the cached allowed user IDs' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = ''
- end
- end
-
- context 'with a valid path' do
- before do
- setting.performance_bar_allowed_group_id = group.full_path
- end
-
- it 'clears the cached allowed user IDs' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = group.full_path
- end
- end
- end
- end
- end
-
describe 'performance_bar_allowed_group' do
context 'with no performance_bar_allowed_group_id saved' do
it 'returns nil' do
@@ -464,11 +402,11 @@ describe ApplicationSetting do
let(:group) { create(:group) }
before do
- setting.performance_bar_allowed_group_id = group.full_path
+ setting.update!(performance_bar_allowed_group_id: group.id)
end
it 'returns the group' do
- expect(setting.performance_bar_allowed_group).to eq(group)
+ expect(setting.reload.performance_bar_allowed_group).to eq(group)
end
end
end
@@ -478,67 +416,11 @@ describe ApplicationSetting do
let(:group) { create(:group) }
before do
- setting.performance_bar_allowed_group_id = group.full_path
+ setting.update!(performance_bar_allowed_group_id: group.id)
end
it 'returns true' do
- expect(setting.performance_bar_enabled).to be_truthy
- end
- end
- end
-
- describe 'performance_bar_enabled=' do
- context 'when the performance bar is enabled' do
- let(:group) { create(:group) }
-
- before do
- setting.performance_bar_allowed_group_id = group.full_path
- end
-
- context 'when passing true' do
- it 'does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = true
-
- expect(setting.performance_bar_allowed_group_id).to eq(group.id)
- expect(setting.performance_bar_enabled).to be_truthy
- end
- end
-
- context 'when passing false' do
- it 'disables the performance bar and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = false
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
- end
- end
-
- context 'when the performance bar is disabled' do
- context 'when passing true' do
- it 'does nothing and does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = true
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
- end
-
- context 'when passing false' do
- it 'does nothing and does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = false
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
+ expect(setting.reload.performance_bar_enabled).to be_truthy
end
end
end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 8ef91e8fab5..581fd0293cc 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -5,7 +5,7 @@ describe InternalId do
let(:usage) { :issues }
let(:issue) { build(:issue, project: project) }
let(:scope) { { project: project } }
- let(:init) { ->(s) { s.project.issues.maximum(:iid) } }
+ let(:init) { ->(s) { s.project.issues.size } }
context 'validations' do
it { is_expected.to validate_presence_of(:usage) }
@@ -39,29 +39,6 @@ describe InternalId do
end
end
- context 'with an InternalId record present and existing issues with a higher internal id' do
- # This can happen if the old NonatomicInternalId is still in use
- before do
- issues = Array.new(rand(1..10)).map { create(:issue, project: project) }
-
- issue = issues.last
- issue.iid = issues.map { |i| i.iid }.max + 1
- issue.save
- end
-
- let(:maximum_iid) { project.issues.map { |i| i.iid }.max }
-
- it 'updates last_value to the maximum internal id present' do
- subject
-
- expect(described_class.find_by(project: project, usage: described_class.usages[usage.to_s]).last_value).to eq(maximum_iid + 1)
- end
-
- it 'returns next internal id correctly' do
- expect(subject).to eq(maximum_iid + 1)
- end
- end
-
context 'with concurrent inserts on table' do
it 'looks up the record if it was created concurrently' do
args = { **scope, usage: described_class.usages[usage.to_s] }
@@ -104,8 +81,7 @@ describe InternalId do
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
- let(:maximum_iid) { nil }
- subject { id.increment_and_save!(maximum_iid) }
+ subject { id.increment_and_save! }
it 'returns incremented iid' do
value = id.last_value
@@ -126,14 +102,5 @@ describe InternalId do
expect(subject).to eq(1)
end
end
-
- context 'with maximum_iid given' do
- let(:id) { create(:internal_id, last_value: 1) }
- let(:maximum_iid) { id.last_value + 10 }
-
- it 'returns maximum_iid instead' do
- expect(subject).to eq(12)
- end
- end
end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 0a2963452e4..45082e644ca 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -13,7 +13,10 @@ describe API::Jobs do
ref: project.default_branch)
end
- let!(:job) { create(:ci_build, :success, pipeline: pipeline) }
+ let!(:job) do
+ create(:ci_build, :success, pipeline: pipeline,
+ artifacts_expire_at: 1.day.since)
+ end
let(:user) { create(:user) }
let(:api_user) { user }
@@ -43,6 +46,7 @@ describe API::Jobs do
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
+ expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
it 'returns pipeline data' do
@@ -128,6 +132,7 @@ describe API::Jobs do
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
+ expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
it 'returns pipeline data' do
@@ -201,6 +206,7 @@ describe API::Jobs do
expect(Time.parse(json_response['created_at'])).to be_like_time(job.created_at)
expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
+ expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
expect(json_response['duration']).to eq(job.duration)
end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 827c6dd4af1..6aadf839dbd 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -1225,7 +1225,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
before do
fog_connection.directories.get('artifacts').files.create(
- key: 'tmp/upload/12312300',
+ key: 'tmp/uploads/12312300',
body: 'content'
)
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 8b22d1e72f3..aead8978dd4 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -24,10 +24,15 @@ describe API::Settings, 'Settings' do
expect(json_response['ecdsa_key_restriction']).to eq(0)
expect(json_response['ed25519_key_restriction']).to eq(0)
expect(json_response['circuitbreaker_failure_count_threshold']).not_to be_nil
+ expect(json_response['performance_bar_allowed_group_id']).to be_nil
+ expect(json_response).not_to have_key('performance_bar_allowed_group_path')
+ expect(json_response).not_to have_key('performance_bar_enabled')
end
end
describe "PUT /application/settings" do
+ let(:group) { create(:group) }
+
context "custom repository storage type set in the config" do
before do
storages = { 'custom' => 'tmp/tests/custom_repositories' }
@@ -56,7 +61,8 @@ describe API::Settings, 'Settings' do
ed25519_key_restriction: 256,
circuitbreaker_check_interval: 2,
enforce_terms: true,
- terms: 'Hello world!'
+ terms: 'Hello world!',
+ performance_bar_allowed_group_path: group.full_path
expect(response).to have_gitlab_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
@@ -80,9 +86,27 @@ describe API::Settings, 'Settings' do
expect(json_response['circuitbreaker_check_interval']).to eq(2)
expect(json_response['enforce_terms']).to be(true)
expect(json_response['terms']).to eq('Hello world!')
+ expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
end
end
+ it "supports legacy performance_bar_allowed_group_id" do
+ put api("/application/settings", admin),
+ performance_bar_allowed_group_id: group.full_path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
+ end
+
+ it "supports legacy performance_bar_enabled" do
+ put api("/application/settings", admin),
+ performance_bar_enabled: false,
+ performance_bar_allowed_group_id: group.full_path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['performance_bar_allowed_group_id']).to be_nil
+ end
+
context "missing koding_url value when koding_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), koding_enabled: true
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index f80abb06fca..79672fe1cc5 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1089,7 +1089,7 @@ describe 'Git LFS API and storage' do
context 'with valid remote_id' do
before do
fog_connection.directories.get('lfs-objects').files.create(
- key: 'tmp/upload/12312300',
+ key: 'tmp/uploads/12312300',
body: 'content'
)
end
diff --git a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
index 7ddf9141fcd..03eeffe6483 100644
--- a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
+++ b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
@@ -256,6 +256,18 @@ describe RuboCop::Cop::LineBreakAroundConditionalBlock do
expect(cop.offenses).to be_empty
end
+ it "doesn't flag violation for #{conditional} followed by a comment" do
+ source = <<~RUBY
+ #{conditional} condition
+ do_something
+ end
+ # a short comment
+ RUBY
+ inspect_source(source)
+
+ expect(cop.offenses).to be_empty
+ end
+
it "doesn't flag violation for #{conditional} followed by an end" do
source = <<~RUBY
class Foo
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index fb07ecc6ae8..6337ee7d724 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ApplicationSettings::UpdateService do
- let(:application_settings) { Gitlab::CurrentSettings.current_application_settings }
+ let(:application_settings) { create(:application_setting) }
let(:admin) { create(:user, :admin) }
let(:params) { {} }
@@ -54,4 +54,90 @@ describe ApplicationSettings::UpdateService do
end
end
end
+
+ describe 'performance bar settings' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:params_performance_bar_enabled,
+ :params_performance_bar_allowed_group_path,
+ :previous_performance_bar_allowed_group_id,
+ :expected_performance_bar_allowed_group_id) do
+ true | '' | nil | nil
+ true | '' | 42_000_000 | nil
+ true | nil | nil | nil
+ true | nil | 42_000_000 | nil
+ true | 'foo' | nil | nil
+ true | 'foo' | 42_000_000 | nil
+ true | 'group_a' | nil | 42_000_000
+ true | 'group_b' | 42_000_000 | 43_000_000
+ true | 'group_a' | 42_000_000 | 42_000_000
+ false | '' | nil | nil
+ false | '' | 42_000_000 | nil
+ false | nil | nil | nil
+ false | nil | 42_000_000 | nil
+ false | 'foo' | nil | nil
+ false | 'foo' | 42_000_000 | nil
+ false | 'group_a' | nil | nil
+ false | 'group_b' | 42_000_000 | nil
+ false | 'group_a' | 42_000_000 | nil
+ end
+
+ with_them do
+ let(:params) do
+ {
+ performance_bar_enabled: params_performance_bar_enabled,
+ performance_bar_allowed_group_path: params_performance_bar_allowed_group_path
+ }
+ end
+
+ before do
+ if previous_performance_bar_allowed_group_id == 42_000_000 || params_performance_bar_allowed_group_path == 'group_a'
+ create(:group, id: 42_000_000, path: 'group_a')
+ end
+
+ if expected_performance_bar_allowed_group_id == 43_000_000 || params_performance_bar_allowed_group_path == 'group_b'
+ create(:group, id: 43_000_000, path: 'group_b')
+ end
+
+ application_settings.update!(performance_bar_allowed_group_id: previous_performance_bar_allowed_group_id)
+ end
+
+ it 'sets performance_bar_allowed_group_id when present and performance_bar_enabled == true' do
+ expect(application_settings.performance_bar_allowed_group_id).to eq(previous_performance_bar_allowed_group_id)
+
+ if previous_performance_bar_allowed_group_id != expected_performance_bar_allowed_group_id
+ expect { subject.execute }
+ .to change(application_settings, :performance_bar_allowed_group_id)
+ .from(previous_performance_bar_allowed_group_id).to(expected_performance_bar_allowed_group_id)
+ else
+ expect { subject.execute }
+ .not_to change(application_settings, :performance_bar_allowed_group_id)
+ end
+ end
+ end
+
+ context 'when :performance_bar_allowed_group_path is not present' do
+ let(:group) { create(:group) }
+
+ before do
+ application_settings.update!(performance_bar_allowed_group_id: group.id)
+ end
+
+ it 'does not change the performance bar settings' do
+ expect { subject.execute }
+ .not_to change(application_settings, :performance_bar_allowed_group_id)
+ end
+ end
+
+ context 'when :performance_bar_enabled is not present' do
+ let(:group) { create(:group) }
+ let(:params) { { performance_bar_allowed_group_path: group.full_path } }
+
+ it 'implicitely defaults to true' do
+ expect { subject.execute }
+ .to change(application_settings, :performance_bar_allowed_group_id)
+ .from(nil).to(group.id)
+ end
+ end
+ end
end
diff --git a/spec/services/check_gcp_project_billing_service_spec.rb b/spec/services/check_gcp_project_billing_service_spec.rb
deleted file mode 100644
index 3e68d906e71..00000000000
--- a/spec/services/check_gcp_project_billing_service_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'spec_helper'
-
-describe CheckGcpProjectBillingService do
- include GoogleApi::CloudPlatformHelpers
-
- let(:service) { described_class.new }
- let(:project_id) { 'test-project-1234' }
-
- describe '#execute' do
- before do
- stub_cloud_platform_projects_list(project_id: project_id)
- end
-
- subject { service.execute('bogustoken') }
-
- context 'google account has a billing enabled gcp project' do
- before do
- stub_cloud_platform_projects_get_billing_info(project_id, true)
- end
-
- it { is_expected.to all(satisfy { |project| project.project_id == project_id }) }
- end
-
- context 'google account does not have a billing enabled gcp project' do
- before do
- stub_cloud_platform_projects_get_billing_info(project_id, false)
- end
-
- it { is_expected.to eq([]) }
- end
- end
-end
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index e7277b337f6..4165a005063 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -618,7 +618,7 @@ describe ObjectStorage do
let!(:fog_file) do
fog_connection.directories.get('uploads').files.create(
- key: 'tmp/upload/test/123123',
+ key: 'tmp/uploads/test/123123',
body: 'content'
)
end
diff --git a/spec/workers/check_gcp_project_billing_worker_spec.rb b/spec/workers/check_gcp_project_billing_worker_spec.rb
deleted file mode 100644
index 526ecf75921..00000000000
--- a/spec/workers/check_gcp_project_billing_worker_spec.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require 'spec_helper'
-
-describe CheckGcpProjectBillingWorker do
- describe '.perform' do
- let(:token) { 'bogustoken' }
-
- subject { described_class.new.perform('token_key') }
-
- before do
- allow(described_class).to receive(:get_billing_state)
- allow_any_instance_of(described_class).to receive(:update_billing_change_counter)
- end
-
- context 'when there is a token in redis' do
- before do
- allow(described_class).to receive(:get_session_token).and_return(token)
- end
-
- context 'when there is no lease' do
- before do
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
- end
-
- it 'calls the service' do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
-
- subject
- end
-
- it 'stores billing status in redis' do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- expect(described_class).to receive(:set_billing_state).with(token, true)
-
- subject
- end
- end
-
- context 'when there is a lease' do
- before do
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return(false)
- end
-
- it 'does not call the service' do
- expect(CheckGcpProjectBillingService).not_to receive(:new)
-
- subject
- end
- end
- end
-
- context 'when there is no token in redis' do
- before do
- allow(described_class).to receive(:get_session_token).and_return(nil)
- end
-
- it 'does not call the service' do
- expect(CheckGcpProjectBillingService).not_to receive(:new)
-
- subject
- end
- end
- end
-
- describe 'billing change counter' do
- subject { described_class.new.perform('token_key') }
-
- before do
- allow(described_class).to receive(:get_session_token).and_return('bogustoken')
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
- allow(described_class).to receive(:set_billing_state)
- end
-
- context 'when previous state was false' do
- before do
- expect(described_class).to receive(:get_billing_state).and_return(false)
- end
-
- context 'when the current state is false' do
- before do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([])
- end
-
- it 'increments the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
-
- context 'when the current state is true' do
- before do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- end
-
- it 'increments the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
- end
-
- context 'when previous state was true' do
- before do
- expect(described_class).to receive(:get_billing_state).and_return(true)
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- end
-
- it 'increment the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
- end
-end