diff options
33 files changed, 206 insertions, 313 deletions
diff --git a/app/assets/javascripts/create_cluster/init_create_cluster.js b/app/assets/javascripts/create_cluster/init_create_cluster.js index 7c984582fd8..2b09771d772 100644 --- a/app/assets/javascripts/create_cluster/init_create_cluster.js +++ b/app/assets/javascripts/create_cluster/init_create_cluster.js @@ -6,7 +6,7 @@ const newClusterViews = [':clusters:new', ':clusters:create_gcp', ':clusters:cre const isProjectLevelCluster = page => page.startsWith('project:clusters'); -export default (document, gon) => { +export default document => { const { page } = document.body.dataset; const isNewClusterView = newClusterViews.some(view => page.endsWith(view)); @@ -19,17 +19,15 @@ export default (document, gon) => { initGkeDropdowns(); - if (gon.features.createEksClusters) { - import(/* webpackChunkName: 'eks_cluster' */ '~/create_cluster/eks_cluster') - .then(({ default: initCreateEKSCluster }) => { - const el = document.querySelector('.js-create-eks-cluster-form-container'); + import(/* webpackChunkName: 'eks_cluster' */ '~/create_cluster/eks_cluster') + .then(({ default: initCreateEKSCluster }) => { + const el = document.querySelector('.js-create-eks-cluster-form-container'); - if (el) { - initCreateEKSCluster(el); - } - }) - .catch(() => {}); - } + if (el) { + initCreateEKSCluster(el); + } + }) + .catch(() => {}); if (isProjectLevelCluster(page)) { initGkeNamespace(); diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 9da8ad229fe..0295f36732c 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -12,9 +12,6 @@ class Clusters::ClustersController < Clusters::BaseController before_action :authorize_update_cluster!, only: [:update] before_action :authorize_admin_cluster!, only: [:destroy, :clear_cache] before_action :update_applications_status, only: [:cluster_status] - before_action only: [:new, :create_gcp] do - push_frontend_feature_flag(:create_eks_clusters) - end before_action only: [:show] do push_frontend_feature_flag(:enable_cluster_application_elastic_stack) push_frontend_feature_flag(:enable_cluster_application_crossplane) @@ -42,8 +39,6 @@ class Clusters::ClustersController < Clusters::BaseController end def new - return unless Feature.enabled?(:create_eks_clusters) - if params[:provider] == 'aws' @aws_role = current_user.aws_role || Aws::Role.new @aws_role.ensure_role_external_id! @@ -113,6 +108,7 @@ class Clusters::ClustersController < Clusters::BaseController generate_gcp_authorize_url validate_gcp_token user_cluster + params[:provider] = 'gcp' render :new, locals: { active_tab: 'create' } end @@ -277,8 +273,7 @@ class Clusters::ClustersController < Clusters::BaseController end def generate_gcp_authorize_url - params = Feature.enabled?(:create_eks_clusters) ? { provider: :gke } : {} - state = generate_session_key_redirect(clusterable.new_path(params).to_s) + state = generate_session_key_redirect(clusterable.new_path(provider: :gcp).to_s) @authorize_url = GoogleApi::CloudPlatform::Client.new( nil, callback_google_api_auth_url, diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb index 1b880a7aab1..b995df12e56 100644 --- a/app/services/projects/container_repository/cleanup_tags_service.rb +++ b/app/services/projects/container_repository/cleanup_tags_service.rb @@ -26,13 +26,13 @@ module Projects def delete_tags(tags_to_delete, tags_by_digest) deleted_digests = group_by_digest(tags_to_delete).select do |digest, tags| - delete_tag_digest(digest, tags, tags_by_digest[digest]) + delete_tag_digest(tags, tags_by_digest[digest]) end deleted_digests.values.flatten end - def delete_tag_digest(digest, tags, other_tags) + def delete_tag_digest(tags, other_tags) # Issue: https://gitlab.com/gitlab-org/gitlab-foss/issues/21405 # we have to remove all tags due # to Docker Distribution bug unable diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb index 48bd9394dc5..af58d3780b0 100644 --- a/app/services/projects/container_repository/delete_tags_service.rb +++ b/app/services/projects/container_repository/delete_tags_service.rb @@ -24,32 +24,36 @@ module Projects dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path) return error('could not generate manifest') if dummy_manifest.nil? - # update the manifests of the tags with the new dummy image - deleted_tags = [] - tag_digests = [] + deleted_tags = replace_tag_manifests(container_repository, dummy_manifest, tag_names) + + # Deletes the dummy image + # All created tag digests are the same since they all have the same dummy image. + # a single delete is sufficient to remove all tags with it + if deleted_tags.any? && container_repository.delete_tag_by_digest(deleted_tags.values.first) + success(deleted: deleted_tags.keys) + else + error('could not delete tags') + end + end + + # update the manifests of the tags with the new dummy image + def replace_tag_manifests(container_repository, dummy_manifest, tag_names) + deleted_tags = {} tag_names.each do |name| digest = container_repository.client.put_tag(container_repository.path, name, dummy_manifest) next unless digest - deleted_tags << name - tag_digests << digest + deleted_tags[name] = digest end # make sure the digests are the same (it should always be) - tag_digests.uniq! + digests = deleted_tags.values.uniq # rubocop: disable CodeReuse/ActiveRecord - Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many? + Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if digests.many? - # Deletes the dummy image - # All created tag digests are the same since they all have the same dummy image. - # a single delete is sufficient to remove all tags with it - if tag_digests.any? && container_repository.delete_tag_by_digest(tag_digests.first) - success(deleted: deleted_tags) - else - error('could not delete tags') - end + deleted_tags end end end diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml index 0aa833e49a8..c6318c9bb2f 100644 --- a/app/views/admin/application_settings/integrations.html.haml +++ b/app/views/admin/application_settings/integrations.html.haml @@ -8,5 +8,5 @@ = render_if_exists 'admin/application_settings/slack' = render 'admin/application_settings/third_party_offers' = render 'admin/application_settings/snowplow' -= render 'admin/application_settings/eks' if Feature.enabled?(:create_eks_clusters) += render 'admin/application_settings/eks' diff --git a/app/views/clusters/clusters/gcp/_new.html.haml b/app/views/clusters/clusters/gcp/_new.html.haml index 3d47f4bf2c3..6c3a230fb93 100644 --- a/app/views/clusters/clusters/gcp/_new.html.haml +++ b/app/views/clusters/clusters/gcp/_new.html.haml @@ -1,7 +1,5 @@ = render 'clusters/clusters/gcp/header' - if @valid_gcp_token = render 'clusters/clusters/gcp/form' -- elsif @authorize_url - = render 'clusters/clusters/gcp/signin_with_google_button' - else = render 'clusters/clusters/gcp/gcp_not_configured' diff --git a/app/views/clusters/clusters/gcp/_signin_with_google_button.html.haml b/app/views/clusters/clusters/gcp/_signin_with_google_button.html.haml deleted file mode 100644 index 65cfa6552b1..00000000000 --- a/app/views/clusters/clusters/gcp/_signin_with_google_button.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.signin-with-google - - create_account_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral' } - = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px', alt: _('Sign in with Google')), @authorize_url) - = s_('or %{link_start}create a new Google account%{link_end}').html_safe % { link_start: create_account_link, link_end: '</a>'.html_safe } diff --git a/app/views/clusters/clusters/new.html.haml b/app/views/clusters/clusters/new.html.haml index cb8cbe4e6f2..629585d82cd 100644 --- a/app/views/clusters/clusters/new.html.haml +++ b/app/views/clusters/clusters/new.html.haml @@ -1,6 +1,5 @@ - breadcrumb_title _('Kubernetes') - page_title _('Kubernetes Cluster') -- create_eks_enabled = Feature.enabled?(:create_eks_clusters) - active_tab = local_assigns.fetch(:active_tab, 'create') = javascript_include_tag 'https://apis.google.com/js/api.js' @@ -14,21 +13,14 @@ %li.nav-item{ role: 'presentation' } %a.nav-link{ href: '#create-cluster-pane', id: 'create-cluster-tab', class: active_when(active_tab == 'create'), data: { toggle: 'tab' }, role: 'tab' } %span - - if create_eks_enabled - = create_new_cluster_label(provider: params[:provider]) - - else - = create_new_cluster_label(provider: 'gcp') + = create_new_cluster_label(provider: params[:provider]) %li.nav-item{ role: 'presentation' } %a.nav-link{ href: '#add-cluster-pane', id: 'add-cluster-tab', class: active_when(active_tab == 'add'), data: { toggle: 'tab' }, role: 'tab' } %span Add existing cluster .tab-content.gitlab-tab-content - - if create_eks_enabled - .tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' } - = render new_cluster_partial(provider: params[:provider]) - - else - .tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' } - = render new_cluster_partial(provider: 'gcp') + .tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' } + = render new_cluster_partial(provider: params[:provider]) .tab-pane{ id: 'add-cluster-pane', class: active_when(active_tab == 'add'), role: 'tabpanel' } = render 'clusters/clusters/user/header' diff --git a/changelogs/unreleased/22392-remove-eks-cluster-feature-flag.yml b/changelogs/unreleased/22392-remove-eks-cluster-feature-flag.yml new file mode 100644 index 00000000000..07dbd072cc2 --- /dev/null +++ b/changelogs/unreleased/22392-remove-eks-cluster-feature-flag.yml @@ -0,0 +1,5 @@ +--- +title: Enable creating Amazon EKS clusters from GitLab +merge_request: 20333 +author: +type: added diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md index ad5a0c2273b..a1f437b6e54 100644 --- a/doc/user/project/clusters/add_remove_clusters.md +++ b/doc/user/project/clusters/add_remove_clusters.md @@ -211,33 +211,9 @@ GitLab supports: Before creating your first cluster on Amazon EKS with GitLab's integration, make sure the following requirements are met: -- Self-managed GitLab instances have the `create_eks_clusters` feature flag enabled. - An [Amazon Web Services](https://aws.amazon.com/) account is set up and you are able to log in. - You have permissions to manage IAM resources. -##### Enable the `create_eks_clusters` feature flag **(CORE ONLY)** - -Self-managed instances must have the feature flag `create_eks_clusters` enabled to create -EKS clusters. To enable EKS cluster creation, ask a GitLab administrator with Rails console access -to run the following command: - -```ruby -Feature.enable(:create_eks_clusters) -``` - -To have it enabled for a specific project only, ask a GitLab administrator to run the following -command using a Rails console: - -```ruby -Feature.enable(:create_eks_clusters, Project.find_by_full_path('my_group/my_project')) -``` - -To have this feature disabled, ask a GitLab administrator to run the following command: - -```ruby -Feature.disable(:create_eks_clusters) -``` - ##### Additional requirements for self-managed instances If you are using a self-managed GitLab instance, GitLab must first diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 525d441b1fc..f36318676aa 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -15948,9 +15948,6 @@ msgstr "" msgid "Sign in via 2FA code" msgstr "" -msgid "Sign in with Google" -msgstr "" - msgid "Sign in with Single Sign-On" msgstr "" @@ -21263,9 +21260,6 @@ msgstr "" msgid "opened %{timeAgoString} by %{user}" msgstr "" -msgid "or %{link_start}create a new Google account%{link_end}" -msgstr "" - msgid "out of %d total test" msgid_plural "out of %d total tests" msgstr[0] "" diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 326d0808092..4184c7e611a 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -84,29 +84,11 @@ describe Admin::ClustersController do GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key) end - before do - stub_feature_flags(create_eks_clusters: false) - allow(SecureRandom).to receive(:hex).and_return(key) - end - - it 'has authorize_url' do - get_new - - expect(assigns(:authorize_url)).to include(key) - expect(session[session_key_for_redirect_uri]).to eq(new_admin_cluster_path) - end - - context 'when create_eks_clusters feature flag is enabled' do - before do - stub_feature_flags(create_eks_clusters: true) - end - - context 'when selected provider is gke and no valid gcp token exists' do - it 'redirects to gcp authorize_url' do - get_new + context 'when selected provider is gke and no valid gcp token exists' do + it 'redirects to gcp authorize_url' do + get_new - expect(response).to redirect_to(assigns(:authorize_url)) - end + expect(response).to redirect_to(assigns(:authorize_url)) end end end diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index d1669c84e3e..d47122f051e 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -97,29 +97,15 @@ describe Groups::ClustersController do end before do - stub_feature_flags(create_eks_clusters: false) allow(SecureRandom).to receive(:hex).and_return(key) end - it 'has authorize_url' do + it 'redirects to gcp authorize_url' do go expect(assigns(:authorize_url)).to include(key) - expect(session[session_key_for_redirect_uri]).to eq(new_group_cluster_path(group)) - end - - context 'when create_eks_clusters feature flag is enabled' do - before do - stub_feature_flags(create_eks_clusters: true) - end - - context 'when selected provider is gke and no valid gcp token exists' do - it 'redirects to gcp authorize_url' do - go - - expect(response).to redirect_to(assigns(:authorize_url)) - end - end + expect(session[session_key_for_redirect_uri]).to eq(new_group_cluster_path(group, provider: :gcp)) + expect(response).to redirect_to(assigns(:authorize_url)) end end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 9c21b472c15..5efac44615f 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -95,29 +95,15 @@ describe Projects::ClustersController do end before do - stub_feature_flags(create_eks_clusters: false) allow(SecureRandom).to receive(:hex).and_return(key) end - it 'has authorize_url' do + it 'redirects to gcp authorize_url' do go expect(assigns(:authorize_url)).to include(key) - expect(session[session_key_for_redirect_uri]).to eq(new_project_cluster_path(project)) - end - - context 'when create_eks_clusters feature flag is enabled' do - before do - stub_feature_flags(create_eks_clusters: true) - end - - context 'when selected provider is gke and no valid gcp token exists' do - it 'redirects to gcp authorize_url' do - go - - expect(response).to redirect_to(assigns(:authorize_url)) - end - end + expect(session[session_key_for_redirect_uri]).to eq(new_project_cluster_path(project, provider: :gcp)) + expect(response).to redirect_to(assigns(:authorize_url)) end end diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index bdc946a9c98..9e1b4c5c45f 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -18,8 +18,6 @@ describe 'Gcp Cluster', :js do let(:project_id) { 'test-project-1234' } before do - stub_feature_flags(create_eks_clusters: false) - allow_any_instance_of(Projects::ClustersController) .to receive(:token_in_session).and_return('token') allow_any_instance_of(Projects::ClustersController) @@ -31,7 +29,8 @@ describe 'Gcp Cluster', :js do visit project_clusters_path(project) click_link 'Add Kubernetes cluster' - click_link 'Create new Cluster on GKE' + click_link 'Create new Cluster' + click_link 'Google GKE' end context 'when user filled form with valid parameters' do @@ -147,21 +146,6 @@ describe 'Gcp Cluster', :js do end end - context 'when user has not signed with Google' do - before do - stub_feature_flags(create_eks_clusters: false) - visit project_clusters_path(project) - - click_link 'Add Kubernetes cluster' - click_link 'Create new Cluster on GKE' - end - - it 'user sees a login page' do - expect(page).to have_css('.signin-with-google') - expect(page).to have_link('Google account') - end - end - context 'when a user cannot edit the environment scope' do before do visit project_clusters_path(project) @@ -177,7 +161,6 @@ describe 'Gcp Cluster', :js do context 'when user has not dismissed GCP signup offer' do before do - stub_feature_flags(create_eks_clusters: false) visit project_clusters_path(project) end @@ -190,18 +173,10 @@ describe 'Gcp Cluster', :js do expect(page).to have_css('.gcp-signup-offer') end - - it 'user sees offer on cluster GCP login page' do - click_link 'Add Kubernetes cluster' - click_link 'Create new Cluster on GKE' - - expect(page).to have_css('.gcp-signup-offer') - end end context 'when user has dismissed GCP signup offer' do before do - stub_feature_flags(create_eks_clusters: false) visit project_clusters_path(project) end diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index 67d14d0a58a..9b02991cd53 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -49,41 +49,20 @@ describe 'Clusters', :js do end end - context 'when user has not signed in Google' do + context 'user visits create cluster page' do before do - stub_feature_flags(create_eks_clusters: false) visit project_clusters_path(project) click_link 'Add Kubernetes cluster' - click_link 'Create new Cluster on GKE' + click_link 'Create new Cluster' end - it 'user sees a login page' do - expect(page).to have_css('.signin-with-google') - expect(page).to have_link('Google account') + it 'user sees a link to create a GKE cluster' do + expect(page).to have_link('Google GKE') end - end - - context 'when create_eks_clusters feature flag is enabled' do - before do - stub_feature_flags(create_eks_clusters: true) - end - - context 'when user access create cluster page' do - before do - visit project_clusters_path(project) - click_link 'Add Kubernetes cluster' - click_link 'Create new Cluster' - end - - it 'user sees a link to create a GKE cluster' do - expect(page).to have_link('Google GKE') - end - - it 'user sees a link to create an EKS cluster' do - expect(page).to have_link('Amazon EKS') - end + it 'user sees a link to create an EKS cluster' do + expect(page).to have_link('Amazon EKS') end end end diff --git a/spec/javascripts/diffs/components/settings_dropdown_spec.js b/spec/javascripts/diffs/components/settings_dropdown_spec.js index 5031846cff0..6c08474ffd2 100644 --- a/spec/javascripts/diffs/components/settings_dropdown_spec.js +++ b/spec/javascripts/diffs/components/settings_dropdown_spec.js @@ -4,15 +4,14 @@ import diffModule from '~/diffs/store/modules'; import SettingsDropdown from '~/diffs/components/settings_dropdown.vue'; import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; +const localVue = createLocalVue(); +localVue.use(Vuex); + describe('Diff settiings dropdown component', () => { let vm; let actions; function createComponent(extendStore = () => {}) { - const localVue = createLocalVue(); - - localVue.use(Vuex); - const store = new Vuex.Store({ modules: { diffs: { @@ -26,9 +25,10 @@ describe('Diff settiings dropdown component', () => { extendStore(store); - vm = mount(SettingsDropdown, { + vm = mount(localVue.extend(SettingsDropdown), { localVue, store, + sync: false, }); } diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js index dce8e3be148..27c79b9c34c 100644 --- a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js +++ b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js @@ -1,53 +1,51 @@ -import Vue from 'vue'; import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import { trimText } from 'spec/helpers/text_helper'; import { mockProject } from '../mock_data'; // can also use 'mockGroup', but not useful to test here -const createComponent = () => { - const Component = Vue.extend(frequentItemsListItemComponent); - - return shallowMount(Component, { - propsData: { - itemId: mockProject.id, - itemName: mockProject.name, - namespace: mockProject.namespace, - webUrl: mockProject.webUrl, - avatarUrl: mockProject.avatarUrl, - }, - }); -}; +const localVue = createLocalVue(); describe('FrequentItemsListItemComponent', () => { let wrapper; - let vm; - - beforeEach(() => { - wrapper = createComponent(); - ({ vm } = wrapper); - }); + const createComponent = (props = {}) => { + wrapper = shallowMount(localVue.extend(frequentItemsListItemComponent), { + propsData: { + itemId: mockProject.id, + itemName: mockProject.name, + namespace: mockProject.namespace, + webUrl: mockProject.webUrl, + avatarUrl: mockProject.avatarUrl, + ...props, + }, + sync: false, + localVue, + }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); + wrapper = null; }); describe('computed', () => { describe('hasAvatar', () => { it('should return `true` or `false` if whether avatar is present or not', () => { - wrapper.setProps({ avatarUrl: 'path/to/avatar.png' }); + createComponent({ avatarUrl: 'path/to/avatar.png' }); - expect(vm.hasAvatar).toBe(true); + expect(wrapper.vm.hasAvatar).toBe(true); + }); - wrapper.setProps({ avatarUrl: null }); + it('should return `false` if avatar is not present', () => { + createComponent({ avatarUrl: null }); - expect(vm.hasAvatar).toBe(false); + expect(wrapper.vm.hasAvatar).toBe(false); }); }); describe('highlightedItemName', () => { it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => { - wrapper.setProps({ matcher: 'lab' }); + createComponent({ matcher: 'lab' }); expect(wrapper.find('.js-frequent-items-item-title').html()).toContain( '<b>L</b><b>a</b><b>b</b>', @@ -55,7 +53,7 @@ describe('FrequentItemsListItemComponent', () => { }); it('should return project name as it is if `matcher` is not available', () => { - wrapper.setProps({ matcher: null }); + createComponent({ matcher: null }); expect(trimText(wrapper.find('.js-frequent-items-item-title').text())).toBe( mockProject.name, @@ -65,13 +63,13 @@ describe('FrequentItemsListItemComponent', () => { describe('truncatedNamespace', () => { it('should truncate project name from namespace string', () => { - wrapper.setProps({ namespace: 'platform / nokia-3310' }); + createComponent({ namespace: 'platform / nokia-3310' }); expect(trimText(wrapper.find('.js-frequent-items-item-namespace').text())).toBe('platform'); }); it('should truncate namespace string from the middle if it includes more than two groups in path', () => { - wrapper.setProps({ + createComponent({ namespace: 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310', }); @@ -84,6 +82,8 @@ describe('FrequentItemsListItemComponent', () => { describe('template', () => { it('should render component element', () => { + createComponent(); + expect(wrapper.classes()).toContain('frequent-items-list-item-container'); expect(wrapper.findAll('a').length).toBe(1); expect(wrapper.findAll('.frequent-items-item-avatar-container').length).toBe(1); diff --git a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js index ddbbc5c2d29..c548c16c022 100644 --- a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js +++ b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js @@ -1,13 +1,15 @@ -import Vue from 'vue'; import searchComponent from '~/frequent_items/components/frequent_items_search_input.vue'; import eventHub from '~/frequent_items/event_hub'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; -const createComponent = (namespace = 'projects') => { - const Component = Vue.extend(searchComponent); +const localVue = createLocalVue(); - return shallowMount(Component, { propsData: { namespace } }); -}; +const createComponent = (namespace = 'projects') => + shallowMount(localVue.extend(searchComponent), { + propsData: { namespace }, + localVue, + sync: false, + }); describe('FrequentItemsSearchInputComponent', () => { let wrapper; @@ -40,7 +42,7 @@ describe('FrequentItemsSearchInputComponent', () => { spyOn(eventHub, '$on'); const vmX = createComponent().vm; - Vue.nextTick(() => { + localVue.nextTick(() => { expect(eventHub.$on).toHaveBeenCalledWith( `${vmX.namespace}-dropdownOpen`, jasmine.any(Function), @@ -58,7 +60,7 @@ describe('FrequentItemsSearchInputComponent', () => { vmX.$mount(); vmX.$destroy(); - Vue.nextTick(() => { + localVue.nextTick(() => { expect(eventHub.$off).toHaveBeenCalledWith( `${vmX.namespace}-dropdownOpen`, jasmine.any(Function), diff --git a/spec/javascripts/jobs/components/manual_variables_form_spec.js b/spec/javascripts/jobs/components/manual_variables_form_spec.js index 093aa905185..1f2bf8674c1 100644 --- a/spec/javascripts/jobs/components/manual_variables_form_spec.js +++ b/spec/javascripts/jobs/components/manual_variables_form_spec.js @@ -1,9 +1,12 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import { GlButton } from '@gitlab/ui'; import Form from '~/jobs/components/manual_variables_form.vue'; +const localVue = createLocalVue(); + describe('Manual Variables Form', () => { let wrapper; + const requiredProps = { action: { path: '/play', @@ -14,8 +17,10 @@ describe('Manual Variables Form', () => { }; const factory = (props = {}) => { - wrapper = shallowMount(Form, { + wrapper = shallowMount(localVue.extend(Form), { propsData: props, + localVue, + sync: false, }); }; @@ -23,8 +28,15 @@ describe('Manual Variables Form', () => { factory(requiredProps); }); - afterEach(() => { - wrapper.destroy(); + afterEach(done => { + // The component has a `nextTick` callback after some events so we need + // to wait for those to finish before destroying. + setImmediate(() => { + wrapper.destroy(); + wrapper = null; + + done(); + }); }); it('renders empty form with correct placeholders', () => { @@ -71,7 +83,7 @@ describe('Manual Variables Form', () => { }); describe('when deleting a variable', () => { - it('removes the variable row', () => { + beforeEach(done => { wrapper.vm.variables = [ { key: 'new key', @@ -80,6 +92,10 @@ describe('Manual Variables Form', () => { }, ]; + wrapper.vm.$nextTick(done); + }); + + it('removes the variable row', () => { wrapper.find(GlButton).vm.$emit('click'); expect(wrapper.vm.variables.length).toBe(0); diff --git a/spec/javascripts/monitoring/charts/column_spec.js b/spec/javascripts/monitoring/charts/column_spec.js index d8ac68b9484..27b3d435f08 100644 --- a/spec/javascripts/monitoring/charts/column_spec.js +++ b/spec/javascripts/monitoring/charts/column_spec.js @@ -1,12 +1,14 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import { GlColumnChart } from '@gitlab/ui/dist/charts'; import ColumnChart from '~/monitoring/components/charts/column.vue'; +const localVue = createLocalVue(); + describe('Column component', () => { let columnChart; beforeEach(() => { - columnChart = shallowMount(ColumnChart, { + columnChart = shallowMount(localVue.extend(ColumnChart), { propsData: { graphData: { queries: [ @@ -28,6 +30,8 @@ describe('Column component', () => { }, containerWidth: 100, }, + sync: false, + localVue, }); }); diff --git a/spec/javascripts/monitoring/charts/empty_chart_spec.js b/spec/javascripts/monitoring/charts/empty_chart_spec.js index bbfca27dc5a..06822126b59 100644 --- a/spec/javascripts/monitoring/charts/empty_chart_spec.js +++ b/spec/javascripts/monitoring/charts/empty_chart_spec.js @@ -1,15 +1,19 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; +const localVue = createLocalVue(); + describe('Empty Chart component', () => { let emptyChart; const graphTitle = 'Memory Usage'; beforeEach(() => { - emptyChart = shallowMount(EmptyChart, { + emptyChart = shallowMount(localVue.extend(EmptyChart), { propsData: { graphTitle, }, + sync: false, + localVue, }); }); diff --git a/spec/javascripts/monitoring/charts/single_stat_spec.js b/spec/javascripts/monitoring/charts/single_stat_spec.js index 127a4a7955a..6adca0b0eed 100644 --- a/spec/javascripts/monitoring/charts/single_stat_spec.js +++ b/spec/javascripts/monitoring/charts/single_stat_spec.js @@ -1,15 +1,19 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import SingleStatChart from '~/monitoring/components/charts/single_stat.vue'; import { graphDataPrometheusQuery } from '../mock_data'; +const localVue = createLocalVue(); + describe('Single Stat Chart component', () => { let singleStatChart; beforeEach(() => { - singleStatChart = shallowMount(SingleStatChart, { + singleStatChart = shallowMount(localVue.extend(SingleStatChart), { propsData: { graphData: graphDataPrometheusQuery, }, + sync: false, + localVue, }); }); diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 0f20171726c..3529a3d72ba 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -72,6 +72,17 @@ describe('Dashboard', () => { let mock; let store; let component; + let wrapper; + + const createComponentWrapper = (props = {}, options = {}) => { + wrapper = shallowMount(localVue.extend(DashboardComponent), { + localVue, + sync: false, + propsData: { ...propsData, ...props }, + store, + ...options, + }); + }; beforeEach(() => { setFixtures(` @@ -81,13 +92,16 @@ describe('Dashboard', () => { store = createStore(); mock = new MockAdapter(axios); - DashboardComponent = Vue.extend(Dashboard); + DashboardComponent = localVue.extend(Dashboard); }); afterEach(() => { if (component) { component.$destroy(); } + if (wrapper) { + wrapper.destroy(); + } mock.restore(); }); @@ -123,15 +137,8 @@ describe('Dashboard', () => { }); describe('cluster health', () => { - let wrapper; - beforeEach(done => { - wrapper = shallowMount(DashboardComponent, { - localVue, - sync: false, - propsData: { ...propsData, hasMetrics: true }, - store, - }); + createComponentWrapper({ hasMetrics: true }); // all_dashboards is not defined in health dashboards wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined); @@ -383,7 +390,6 @@ describe('Dashboard', () => { }); describe('drag and drop function', () => { - let wrapper; let expectedPanelCount; // also called metrics, naming to be improved: https://gitlab.com/gitlab-org/gitlab/issues/31565 const findDraggables = () => wrapper.findAll(VueDraggable); @@ -400,13 +406,7 @@ describe('Dashboard', () => { }); beforeEach(done => { - wrapper = shallowMount(DashboardComponent, { - localVue, - sync: false, - propsData: { ...propsData, hasMetrics: true }, - store, - attachToDocument: true, - }); + createComponentWrapper({ hasMetrics: true }, { attachToDocument: true }); setupComponentStore(wrapper.vm); @@ -417,6 +417,10 @@ describe('Dashboard', () => { wrapper.destroy(); }); + afterEach(() => { + wrapper.destroy(); + }); + it('wraps vuedraggable', () => { expect(findDraggablePanels().exists()).toBe(true); expect(findDraggablePanels().length).toEqual(expectedPanelCount); @@ -502,7 +506,6 @@ describe('Dashboard', () => { // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 // eslint-disable-next-line jasmine/no-disabled-tests xdescribe('link to chart', () => { - let wrapper; const currentDashboard = 'TEST_DASHBOARD'; localVue.use(GlToast); const link = () => wrapper.find('.js-chart-link'); @@ -511,13 +514,7 @@ describe('Dashboard', () => { beforeEach(done => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - wrapper = shallowMount(DashboardComponent, { - localVue, - sync: false, - attachToDocument: true, - propsData: { ...propsData, hasMetrics: true, currentDashboard }, - store, - }); + createComponentWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true }); setTimeout(done); }); @@ -614,19 +611,12 @@ describe('Dashboard', () => { }); describe('dashboard edit link', () => { - let wrapper; const findEditLink = () => wrapper.find('.js-edit-link'); beforeEach(done => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - wrapper = shallowMount(DashboardComponent, { - localVue, - sync: false, - attachToDocument: true, - propsData: { ...propsData, hasMetrics: true }, - store, - }); + createComponentWrapper({ hasMetrics: true }, { attachToDocument: true }); wrapper.vm.$store.commit( `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, diff --git a/spec/javascripts/monitoring/components/graph_group_spec.js b/spec/javascripts/monitoring/components/graph_group_spec.js index 7bcab9116e9..04371091ca8 100644 --- a/spec/javascripts/monitoring/components/graph_group_spec.js +++ b/spec/javascripts/monitoring/components/graph_group_spec.js @@ -1,22 +1,31 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import GraphGroup from '~/monitoring/components/graph_group.vue'; +const localVue = createLocalVue(); + describe('Graph group component', () => { let graphGroup; + const findPrometheusGroup = () => graphGroup.find('.prometheus-graph-group'); const findPrometheusPanel = () => graphGroup.find('.prometheus-panel'); + const createComponent = propsData => { + graphGroup = shallowMount(localVue.extend(GraphGroup), { + propsData, + sync: false, + localVue, + }); + }; + afterEach(() => { graphGroup.destroy(); }); describe('When groups can be collapsed', () => { beforeEach(() => { - graphGroup = shallowMount(GraphGroup, { - propsData: { - name: 'panel', - collapseGroup: true, - }, + createComponent({ + name: 'panel', + collapseGroup: true, }); }); @@ -33,12 +42,10 @@ describe('Graph group component', () => { describe('When groups can not be collapsed', () => { beforeEach(() => { - graphGroup = shallowMount(GraphGroup, { - propsData: { - name: 'panel', - collapseGroup: true, - showPanels: false, - }, + createComponent({ + name: 'panel', + collapseGroup: true, + showPanels: false, }); }); @@ -49,12 +56,7 @@ describe('Graph group component', () => { describe('When collapseGroup prop is updated', () => { beforeEach(() => { - graphGroup = shallowMount(GraphGroup, { - propsData: { - name: 'panel', - collapseGroup: false, - }, - }); + createComponent({ name: 'panel', collapseGroup: false }); }); it('previously collapsed group should respond to the prop change', done => { diff --git a/spec/javascripts/notes/components/note_actions/reply_button_spec.js b/spec/javascripts/notes/components/note_actions/reply_button_spec.js index 003773d07ea..aa39ab15833 100644 --- a/spec/javascripts/notes/components/note_actions/reply_button_spec.js +++ b/spec/javascripts/notes/components/note_actions/reply_button_spec.js @@ -2,15 +2,14 @@ import Vuex from 'vuex'; import { createLocalVue, mount } from '@vue/test-utils'; import ReplyButton from '~/notes/components/note_actions/reply_button.vue'; +const localVue = createLocalVue(); +localVue.use(Vuex); + describe('ReplyButton', () => { let wrapper; beforeEach(() => { - const localVue = createLocalVue(); - - localVue.use(Vuex); - - wrapper = mount(ReplyButton, { + wrapper = mount(localVue.extend(ReplyButton), { sync: false, localVue, }); diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js index 1f2c07385a7..3439873168c 100644 --- a/spec/javascripts/notes/components/note_actions_spec.js +++ b/spec/javascripts/notes/components/note_actions_spec.js @@ -12,7 +12,7 @@ describe('noteActions', () => { const shallowMountNoteActions = propsData => { const localVue = createLocalVue(); - return shallowMount(noteActions, { + return shallowMount(localVue.extend(noteActions), { store, propsData, localVue, diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js index 96aa7824cec..35283e14dc5 100644 --- a/spec/javascripts/notes/components/note_form_spec.js +++ b/spec/javascripts/notes/components/note_form_spec.js @@ -14,7 +14,7 @@ describe('issue_note_form component', () => { const createComponentWrapper = () => { const localVue = createLocalVue(); - return shallowMount(NoteForm, { + return shallowMount(localVue.extend(NoteForm), { store, propsData: props, // see https://gitlab.com/gitlab-org/gitlab-foss/issues/56317 for the following diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index ea1ed3da112..5e359759afc 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -10,6 +10,8 @@ import mockDiffFile from '../../diffs/mock_data/diff_file'; const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json'; +const localVue = createLocalVue(); + describe('noteable_discussion component', () => { let store; let wrapper; @@ -22,8 +24,7 @@ describe('noteable_discussion component', () => { store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); - const localVue = createLocalVue(); - wrapper = mount(noteableDiscussion, { + wrapper = mount(localVue.extend(noteableDiscussion), { store, propsData: { discussion: discussionMock }, localVue, diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js index 9420713ceca..72a13afe498 100644 --- a/spec/javascripts/notes/components/noteable_note_spec.js +++ b/spec/javascripts/notes/components/noteable_note_spec.js @@ -18,7 +18,7 @@ describe('issue_note', () => { store.dispatch('setNotesData', notesDataMock); const localVue = createLocalVue(); - wrapper = shallowMount(issueNote, { + wrapper = shallowMount(localVue.extend(issueNote), { store, propsData: { note, diff --git a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js b/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js index 29760f79c3c..cf3ab4d4a68 100644 --- a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js +++ b/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js @@ -20,7 +20,7 @@ describe('RelatedMergeRequests', () => { mock = new MockAdapter(axios); mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 }); - wrapper = mount(RelatedMergeRequests, { + wrapper = mount(localVue.extend(RelatedMergeRequests), { localVue, sync: false, store: createStore(), diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js index 6cdf60f3535..703b889cd5d 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js @@ -4,18 +4,19 @@ import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pi import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue'; import { mockStore } from '../mock_data'; +const localVue = createLocalVue(); + describe('MrWidgetPipelineContainer', () => { let wrapper; const factory = (props = {}) => { - const localVue = createLocalVue(); - wrapper = mount(localVue.extend(MrWidgetPipelineContainer), { propsData: { mr: Object.assign({}, mockStore), ...props, }, localVue, + sync: false, }); }; diff --git a/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js b/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js index 7445da6cdee..204c0decfd8 100644 --- a/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js @@ -1,14 +1,18 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import GraphqlPagination from '~/vue_shared/components/pagination/graphql_pagination.vue'; +const localVue = createLocalVue(); + describe('Graphql Pagination component', () => { let wrapper; function factory({ hasNextPage = true, hasPreviousPage = true }) { - wrapper = shallowMount(GraphqlPagination, { + wrapper = shallowMount(localVue.extend(GraphqlPagination), { propsData: { hasNextPage, hasPreviousPage, }, + sync: false, + localVue, }); } |