From c2872033329e90566e4c1b91c11c36e8746d59b1 Mon Sep 17 00:00:00 2001 From: Andrew Smith <gitlab@espadav8.co.uk> Date: Thu, 22 Feb 2024 09:48:05 +0000 Subject: [PATCH] Move all Vue app init functions into a single location We are going to need to combine these apps into a single `new Vue` call, so having them in the same file to start with will make things much easier later on. Signed-off-by: Andrew Smith <espadav8@gmail.com> --- .../javascripts/forks/init_forks_button.js | 41 ------ .../components/more_actions_dropdown.vue | 10 +- .../init_more_actions_dropdown.js | 2 +- .../components/notifications_dropdown.vue | 6 +- app/assets/javascripts/notifications/index.js | 2 +- .../home_panel/components/home_panel.vue | 61 +++++++++ .../pages/projects/home_panel/index.js | 106 ++++++++++++++++ .../javascripts/pages/projects/show/index.js | 12 +- app/assets/javascripts/stars/index.js | 40 ------ app/controllers/projects_controller.rb | 1 - app/helpers/projects_helper.rb | 59 +++++++-- app/views/groups/_home_panel.html.haml | 2 +- .../_more_actions_dropdown.html.haml} | 6 - app/views/projects/_home_panel.html.haml | 7 +- app/views/projects/buttons/_fork.html.haml | 3 - app/views/projects/buttons/_star.html.haml | 1 - spec/controllers/projects_controller_spec.rb | 29 ----- .../components/more_actions_dropdown_spec.js | 6 +- .../home_panel/components/home_panel_spec.js | 65 ++++++++++ spec/helpers/projects_helper_spec.rb | 118 ++++++++++++++++++ .../projects/_home_panel.html.haml_spec.rb | 35 +----- 21 files changed, 419 insertions(+), 193 deletions(-) delete mode 100644 app/assets/javascripts/forks/init_forks_button.js create mode 100644 app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue create mode 100644 app/assets/javascripts/pages/projects/home_panel/index.js delete mode 100644 app/assets/javascripts/stars/index.js rename app/views/{shared/_groups_projects_more_actions_dropdown.html.haml => groups/_more_actions_dropdown.html.haml} (64%) delete mode 100644 app/views/projects/buttons/_fork.html.haml delete mode 100644 app/views/projects/buttons/_star.html.haml create mode 100644 spec/frontend/home_panel/components/home_panel_spec.js diff --git a/app/assets/javascripts/forks/init_forks_button.js b/app/assets/javascripts/forks/init_forks_button.js deleted file mode 100644 index b899d1c51dbf7..0000000000000 --- a/app/assets/javascripts/forks/init_forks_button.js +++ /dev/null @@ -1,41 +0,0 @@ -import Vue from 'vue'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import ForksButton from './components/forks_button.vue'; - -const initForksButton = () => { - const el = document.getElementById('js-forks-button'); - - if (!el) { - return false; - } - - const { - forksCount, - projectFullPath, - projectForksUrl, - userForkUrl, - newForkUrl, - canReadCode, - canCreateFork, - canForkProject, - } = el.dataset; - - return new Vue({ - el, - provide: { - forksCount, - projectFullPath, - projectForksUrl, - userForkUrl, - newForkUrl, - canReadCode: parseBoolean(canReadCode), - canCreateFork: parseBoolean(canCreateFork), - canForkProject: parseBoolean(canForkProject), - }, - render(createElement) { - return createElement(ForksButton); - }, - }); -}; - -export default initForksButton; diff --git a/app/assets/javascripts/groups_projects/components/more_actions_dropdown.vue b/app/assets/javascripts/groups_projects/components/more_actions_dropdown.vue index 238e382fabee8..8863481617712 100644 --- a/app/assets/javascripts/groups_projects/components/more_actions_dropdown.vue +++ b/app/assets/javascripts/groups_projects/components/more_actions_dropdown.vue @@ -23,7 +23,7 @@ export default { }, inject: [ 'isGroup', - 'id', + 'groupOrProjectId', 'leavePath', 'leaveConfirmMessage', 'withdrawPath', @@ -90,7 +90,7 @@ export default { }, copyIdItem() { return { - text: sprintf(this.copyTitle, { id: this.id }), + text: sprintf(this.copyTitle, { id: this.groupOrProjectId }), action: () => { this.$toast.show(this.copiedToClipboard); }, @@ -148,7 +148,11 @@ export default { </div> </template> - <gl-disclosure-dropdown-item v-if="id" :item="copyIdItem" :data-clipboard-text="id" /> + <gl-disclosure-dropdown-item + v-if="groupOrProjectId" + :item="copyIdItem" + :data-clipboard-text="groupOrProjectId" + /> <gl-disclosure-dropdown-group v-if="hasPath" bordered> <gl-disclosure-dropdown-item v-if="leavePath" ref="leaveItem" :item="leaveItem" /> diff --git a/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js b/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js index 5d83f9ed3b2e7..c5f6d29d08079 100644 --- a/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js +++ b/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js @@ -24,7 +24,7 @@ export default function InitMoreActionsDropdown() { name: 'MoreActionsDropdownRoot', provide: { isGroup: parseBoolean(isGroup), - id, + groupOrProjectId: id, leavePath, leaveConfirmMessage, withdrawPath, diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown.vue b/app/assets/javascripts/notifications/components/notifications_dropdown.vue index 6b450c2b5fd7c..eeca39fa5227a 100644 --- a/app/assets/javascripts/notifications/components/notifications_dropdown.vue +++ b/app/assets/javascripts/notifications/components/notifications_dropdown.vue @@ -21,7 +21,7 @@ export default { containerClass: { default: '', }, - disabled: { + emailsDisabled: { default: false, }, dropdownItems: { @@ -81,7 +81,7 @@ export default { this.$options.i18n.notificationTitles[this.selectedNotificationLevel] || this.selectedNotificationLevel; - return this.disabled + return this.emailsDisabled ? this.$options.i18n.notificationDescriptions.owner_disabled : sprintf(this.$options.i18n.notificationTooltipTitle, { notification_title: notificationTitle, @@ -127,7 +127,7 @@ export default { :size="buttonSize" :icon="buttonIcon" :loading="isLoading" - :disabled="disabled" + :disabled="emailsDisabled" :split="isCustomNotification" :text="buttonText" :no-flip="noFlip" diff --git a/app/assets/javascripts/notifications/index.js b/app/assets/javascripts/notifications/index.js index d41b1d9585449..2bdd6717577e1 100644 --- a/app/assets/javascripts/notifications/index.js +++ b/app/assets/javascripts/notifications/index.js @@ -60,7 +60,7 @@ export default () => { provide: { containerClass, buttonSize, - disabled: parseBoolean(disabled), + emailsDisabled: parseBoolean(disabled), dropdownItems: JSON.parse(dropdownItems), initialNotificationLevel: notificationLevel, helpPagePath, diff --git a/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue b/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue new file mode 100644 index 0000000000000..72f7be4243527 --- /dev/null +++ b/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue @@ -0,0 +1,61 @@ +<script> +import { s__, sprintf } from '~/locale'; +import { isLoggedIn } from '~/lib/utils/common_utils'; +import ForksButton from '~/forks/components/forks_button.vue'; +import MoreActionsDropdown from '~/groups_projects/components/more_actions_dropdown.vue'; +import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue'; +import StarCount from '~/stars/components/star_count.vue'; + +export default { + components: { + ForksButton, + MoreActionsDropdown, + NotificationsDropdown, + StarCount, + }, + inject: { + canReadProject: { + default: false, + }, + isProjectEmpty: { + default: false, + }, + projectId: { + default: '', + }, + }, + data() { + return { + isLoggedIn: isLoggedIn(), + }; + }, + computed: { + canForkProject() { + return !this.isProjectEmpty && isLoggedIn() && this.canReadProject; + }, + copyProjectId() { + return sprintf(s__('ProjectPage|Project ID: %{id}'), { id: this.projectId }); + }, + }, +}; +</script> + +<template> + <div class="gl-display-flex gl-gap-3"> + <template v-if="isLoggedIn && canReadProject"> + <notifications-dropdown /> + </template> + + <star-count /> + + <forks-button v-if="canForkProject" /> + + <template v-if="canReadProject"> + <span class="gl-sr-only" itemprop="identifier" data-testid="project-id-content"> + {{ copyProjectId }} + </span> + </template> + + <more-actions-dropdown /> + </div> +</template> diff --git a/app/assets/javascripts/pages/projects/home_panel/index.js b/app/assets/javascripts/pages/projects/home_panel/index.js new file mode 100644 index 0000000000000..071d3d81b7eca --- /dev/null +++ b/app/assets/javascripts/pages/projects/home_panel/index.js @@ -0,0 +1,106 @@ +import { GlToast } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; + +import { parseBoolean } from '~/lib/utils/common_utils'; +import HomePanel from './components/home_panel.vue'; + +Vue.use(GlToast); +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +const initHomePanel = () => { + const container = document.getElementById('js-home-panel'); + + if (container === null) { + return null; + } + + const { + // HomePanel component + canReadProject, + isProjectEmpty, + projectId, + + // Dropdown component + isGroup, + leaveConfirmMessage, + leavePath, + requestAccessPath, + withdrawConfirmMessage, + withdrawPath, + + // Fork component + canCreateFork, + canForkProject, + canReadCode, + forksCount, + newForkUrl, + projectForksUrl, + projectFullPath, + userForkUrl, + + // Notification component + emailsDisabled, + notificationDropdownItems, + notificationHelpPagePath, + notificationLevel, + + // Star component + signInPath, + starCount, + starred, + starrersPath, + } = container.dataset; + + return new Vue({ + apolloProvider, + el: container, + name: 'HomePanelRoot', + provide: { + // HomePanel component + canReadProject: parseBoolean(canReadProject), + isProjectEmpty: parseBoolean(isProjectEmpty), + projectId, + + // Dropdown component + groupOrProjectId: projectId, + isGroup: parseBoolean(isGroup), + leaveConfirmMessage, + leavePath, + requestAccessPath, + withdrawConfirmMessage, + withdrawPath, + + // Fork component + canCreateFork: parseBoolean(canCreateFork), + canForkProject: parseBoolean(canForkProject), + canReadCode: parseBoolean(canReadCode), + forksCount: parseInt(forksCount, 10) || 0, + newForkUrl, + projectForksUrl, + projectFullPath, + userForkUrl, + + // Notification component + dropdownItems: JSON.parse(notificationDropdownItems || null), + emailsDisabled: parseBoolean(emailsDisabled), + helpPagePath: notificationHelpPagePath, + initialNotificationLevel: notificationLevel, + noFlip: true, + + // Star component + signInPath, + starCount: parseInt(starCount, 10) || 0, + starred: parseBoolean(starred), + starrersPath, + }, + render: (createElement) => createElement(HomePanel), + }); +}; + +export { initHomePanel }; diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index f7833c038cbdf..b840df8708fcd 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -3,15 +3,12 @@ import { addShortcutsExtension } from '~/behaviors/shortcuts'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initClustersDeprecationAlert from '~/projects/clusters_deprecation_alert'; import leaveByUrl from '~/namespaces/leave_by_url'; -import initVueNotificationsDropdown from '~/notifications'; -import initVueStarCount from '~/stars'; import initTerraformNotification from '~/projects/terraform_notification'; import { initUploadFileTrigger } from '~/projects/upload_file'; import initReadMore from '~/read_more'; -import initForksButton from '~/forks/init_forks_button'; import initAmbiguousRefModal from '~/ref/init_ambiguous_ref_modal'; -import InitMoreActionsDropdown from '~/groups_projects/init_more_actions_dropdown'; import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue'; +import { initHomePanel } from '../home_panel'; // Project show page loads different overview content based on user preferences if (document.getElementById('js-tree-list')) { @@ -38,17 +35,14 @@ if (document.querySelector('.project-show-activity')) { .catch(() => {}); } -initVueNotificationsDropdown(); -initVueStarCount(); - addShortcutsExtension(ShortcutsNavigation); initUploadFileTrigger(); initClustersDeprecationAlert(); initTerraformNotification(); - initReadMore(); initAmbiguousRefModal(); +initHomePanel(); if (document.querySelector('.js-autodevops-banner')) { import(/* webpackChunkName: 'userCallOut' */ '~/user_callout') @@ -62,8 +56,6 @@ if (document.querySelector('.js-autodevops-banner')) { .catch(() => {}); } -initForksButton(); -InitMoreActionsDropdown(); leaveByUrl('project'); const initCodeDropdown = () => { diff --git a/app/assets/javascripts/stars/index.js b/app/assets/javascripts/stars/index.js deleted file mode 100644 index e19b3a540210a..0000000000000 --- a/app/assets/javascripts/stars/index.js +++ /dev/null @@ -1,40 +0,0 @@ -import { GlToast } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createDefaultClient from '~/lib/graphql'; -import { parseBoolean } from '../lib/utils/common_utils'; -import StarCount from './components/star_count.vue'; - -Vue.use(GlToast); -Vue.use(VueApollo); - -const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient(), -}); - -export default () => { - const containers = document.querySelectorAll('.js-vue-star-count'); - - if (containers.length === 0) { - return false; - } - - return containers.forEach((el) => { - const { projectId, starCount, starred, starrersPath, signInPath } = el.dataset; - - return new Vue({ - el, - provide: { - starred: parseBoolean(starred), - starCount, - projectId, - starrersPath, - signInPath, - }, - render(h) { - return h(StarCount); - }, - apolloProvider, - }); - }); -}; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 1206b3f43a92a..c2adab7d6dc0c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -196,7 +196,6 @@ def show respond_to do |format| format.html do - @notification_setting = current_user.notification_settings_for(@project) if current_user @project = @project.present(current_user: current_user) render_landing_page end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 52d582755ea9d..2e60fad4f775b 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -427,20 +427,21 @@ def able_to_see_forks_count?(project, user) def fork_button_data_attributes(project) return unless current_user + return if project.empty_repo? if current_user.already_forked?(project) && current_user.forkable_namespaces.size < 2 user_fork_url = namespace_project_path(current_user, current_user.fork_of(project)) end { + can_create_fork: can?(current_user, :create_fork).to_s, + can_fork_project: can?(current_user, :fork_project, project).to_s, + can_read_code: can?(current_user, :read_code, project).to_s, forks_count: project.forks_count, - project_full_path: project.full_path, - project_forks_url: project_forks_path(project), - user_fork_url: user_fork_url, new_fork_url: new_project_fork_path(project), - can_read_code: can?(current_user, :read_code, project).to_s, - can_fork_project: can?(current_user, :fork_project, project).to_s, - can_create_fork: can?(current_user, :create_fork).to_s + project_forks_url: project_forks_path(project), + project_full_path: project.full_path, + user_fork_url: user_fork_url } end @@ -448,16 +449,48 @@ def star_count_data_attributes(project) starred = current_user ? current_user.starred?(project) : false { - data: { - project_id: project.id, - sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'), - star_count: project.star_count, - starred: starred.to_s, - starrers_path: project_starrers_path(project) - } + project_id: project.id, + sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'), + star_count: project.star_count, + starred: starred.to_s, + starrers_path: project_starrers_path(project) } end + def notification_data_attributes(project) + return unless current_user + + notification_setting = current_user.notification_settings_for(project) + dropdown_items = notification_dropdown_items(notification_setting).to_json if notification_setting + notification_level = notification_setting.level if notification_setting + + { + emails_disabled: project.emails_disabled?.to_s, + notification_dropdown_items: dropdown_items, + notification_help_page_path: help_page_path('user/profile/notifications'), + notification_level: notification_level + } + end + + def home_panel_data_attributes + project = @project.is_a?(ProjectPresenter) ? @project.project : @project + dropdown_attributes = groups_projects_more_actions_dropdown_data(project) || {} + fork_button_attributes = fork_button_data_attributes(project) || {} + notification_attributes = notification_data_attributes(project) || {} + star_count_attributes = star_count_data_attributes(project) + + { + can_read_project: can?(current_user, :read_project, project).to_s, + is_project_empty: project.empty_repo?.to_s, + project_id: project.id + }.merge( + dropdown_attributes, + fork_button_attributes, + notification_attributes, + star_count_attributes + ) + end + def import_from_bitbucket_message configure_oauth_import_message('Bitbucket', help_page_path("integration/bitbucket")) end diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml index 265e2c582f8e7..5984a21dd0cfd 100644 --- a/app/views/groups/_home_panel.html.haml +++ b/app/views/groups/_home_panel.html.haml @@ -28,7 +28,7 @@ = render Pajamas::ButtonComponent.new(href: new_project_path(namespace_id: @group.id), variant: :confirm, button_options: { data: { testid: 'new-project-button' }, class: 'gl-sm-w-auto gl-w-full' }) do = _('New project') - = render 'shared/groups_projects_more_actions_dropdown', source: @group + = render 'groups/more_actions_dropdown', source: @group - if @group.description.present? .group-home-desc.mt-1 diff --git a/app/views/shared/_groups_projects_more_actions_dropdown.html.haml b/app/views/groups/_more_actions_dropdown.html.haml similarity index 64% rename from app/views/shared/_groups_projects_more_actions_dropdown.html.haml rename to app/views/groups/_more_actions_dropdown.html.haml index 5189dc54f0c07..21f523fe9446a 100644 --- a/app/views/shared/_groups_projects_more_actions_dropdown.html.haml +++ b/app/views/groups/_more_actions_dropdown.html.haml @@ -6,11 +6,5 @@ %span.gl-sr-only{ itemprop: 'identifier', data: { testid: 'group-id-content' } } = s_('GroupPage|Group ID: %{id}') % { id: id } -- elsif can?(current_user, :read_project, @project) - - id = @project.id - - %span.gl-sr-only{ itemprop: 'identifier', data: { testid: 'project-id-content' } } - = s_('ProjectPage|Project ID: %{id}') % { id: id } - - if id || current_user .js-groups-projects-more-actions-dropdown{ data: dropdown_data } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index aea735e4690a3..2542720c3e922 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,5 +1,4 @@ - empty_repo = @project.empty_repo? -- emails_disabled = @project.emails_disabled? - ff_reorg_disabled = Feature.disabled?(:project_overview_reorg) %header.project-home-panel.js-show-on-project-root.gl-mt-5{ class: [("empty-project" if empty_repo)] } @@ -19,12 +18,8 @@ - if current_user - if current_user.admin? = link_button_to nil, [:admin, @project], icon: 'admin', title: _('View project in admin area'), data: {toggle: 'tooltip', placement: 'top', container: 'body'} - - if @notification_setting - .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, no_flip: 'true' } } - = render 'projects/buttons/star', project: @project - = render 'projects/buttons/fork' - = render 'shared/groups_projects_more_actions_dropdown', source: @project + #js-home-panel{ data: home_panel_data_attributes } - if ff_reorg_disabled - if can?(current_user, :read_code, @project) diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml deleted file mode 100644 index 963c416ed426a..0000000000000 --- a/app/views/projects/buttons/_fork.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- unless @project.empty_repo? - - if current_user - #js-forks-button{ data: fork_button_data_attributes(@project) } diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml deleted file mode 100644 index c081ab6105c58..0000000000000 --- a/app/views/projects/buttons/_star.html.haml +++ /dev/null @@ -1 +0,0 @@ -.js-vue-star-count{ star_count_data_attributes(@project) } diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 94058fbc21a32..f7cb9c8814229 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -173,40 +173,11 @@ def get_activity(project) sign_in(user) end - context "user does not have access to project" do - let(:private_project) { create(:project, :private) } - - it "does not initialize notification setting" do - get :show, params: { namespace_id: private_project.namespace, id: private_project } - expect(assigns(:notification_setting)).to be_nil - end - end - context "user has access to project" do before do expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original end - context "and does not have notification setting" do - it "initializes notification as disabled" do - get :show, params: { namespace_id: public_project.namespace, id: public_project } - expect(assigns(:notification_setting).level).to eq("global") - end - end - - context "and has notification setting" do - before do - setting = user.notification_settings_for(public_project) - setting.level = :watch - setting.save! - end - - it "shows current notification setting" do - get :show, params: { namespace_id: public_project.namespace, id: public_project } - expect(assigns(:notification_setting).level).to eq("watch") - end - end - context 'when ambiguous_ref_modal is disabled' do before do stub_feature_flags(ambiguous_ref_modal: false) diff --git a/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js b/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js index 777190149d105..3595038c03dc5 100644 --- a/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js +++ b/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js @@ -13,7 +13,7 @@ describe('moreActionsDropdown', () => { wrapper = shallowMountExtended(moreActionsDropdown, { provide: { isGroup: false, - id: 1, + groupOrProjectId: 1, leavePath: '', leaveConfirmMessage: '', withdrawPath: '', @@ -39,7 +39,7 @@ describe('moreActionsDropdown', () => { beforeEach(async () => { createComponent({ provideData: { - id: 22, + groupOrProjectId: 22, }, }); await showDropdown(); @@ -60,7 +60,7 @@ describe('moreActionsDropdown', () => { createComponent({ provideData: { isGroup: true, - id: 11, + groupOrProjectId: 11, }, }); await showDropdown(); diff --git a/spec/frontend/home_panel/components/home_panel_spec.js b/spec/frontend/home_panel/components/home_panel_spec.js new file mode 100644 index 0000000000000..ce2b14088cf3d --- /dev/null +++ b/spec/frontend/home_panel/components/home_panel_spec.js @@ -0,0 +1,65 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import HomePanel from '~/pages/projects/home_panel/components/home_panel.vue'; +import ForksButton from '~/forks/components/forks_button.vue'; +import MoreActionsDropdown from '~/groups_projects/components/more_actions_dropdown.vue'; +import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue'; +import StarCount from '~/stars/components/star_count.vue'; + +describe('HomePanel', () => { + let wrapper; + + const createComponent = ({ isLoggedIn = false, provide = {} } = {}) => { + if (isLoggedIn) { + window.gon.current_user_id = 1; + } + + wrapper = shallowMountExtended(HomePanel, { + provide: { + ...provide, + }, + }); + }; + + const findForksButton = () => wrapper.findComponent(ForksButton); + const findMoreActionsDropdown = () => wrapper.findComponent(MoreActionsDropdown); + const findNotificationsDropdown = () => wrapper.findComponent(NotificationsDropdown); + const findStarCount = () => wrapper.findComponent(StarCount); + + describe.each` + isLoggedIn | canReadProject | isProjectEmpty | isForkButtonVisible | isMoreActionsDropdownVisible | isNotificationDropdownVisible | isStarCountVisible + ${true} | ${true} | ${true} | ${false} | ${true} | ${true} | ${true} + ${true} | ${true} | ${false} | ${true} | ${true} | ${true} | ${true} + ${true} | ${false} | ${true} | ${false} | ${true} | ${false} | ${true} + ${true} | ${false} | ${false} | ${false} | ${true} | ${false} | ${true} + ${false} | ${true} | ${true} | ${false} | ${true} | ${false} | ${true} + ${false} | ${true} | ${false} | ${false} | ${true} | ${false} | ${true} + ${false} | ${false} | ${true} | ${false} | ${true} | ${false} | ${true} + ${false} | ${false} | ${false} | ${false} | ${true} | ${false} | ${true} + `( + 'renders components', + ({ + isLoggedIn, + canReadProject, + isProjectEmpty, + isForkButtonVisible, + isMoreActionsDropdownVisible, + isNotificationDropdownVisible, + isStarCountVisible, + }) => { + it('as expected', () => { + createComponent({ + isLoggedIn, + provide: { + canReadProject, + isProjectEmpty, + }, + }); + + expect(findForksButton().exists()).toBe(isForkButtonVisible); + expect(findMoreActionsDropdown().exists()).toBe(isMoreActionsDropdownVisible); + expect(findNotificationsDropdown().exists()).toBe(isNotificationDropdownVisible); + expect(findStarCount().exists()).toBe(isStarCountVisible); + }); + }, + ); +}); diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 31793dddd1fa9..27e7aab988ec7 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -1078,6 +1078,124 @@ def license_name end end + describe '#star_count_data_attributes' do + before do + allow(user).to receive(:starred?).with(project).and_return(starred) + allow(helper).to receive(:new_session_path).and_return(sign_in_path) + allow(project).to receive(:star_count).and_return(5) + end + + let(:sign_in_path) { 'sign/in/path' } + let(:common_data_attributes) do + { + project_id: project.id, + sign_in_path: sign_in_path, + star_count: 5, + starrers_path: "/#{project.full_path}/-/starrers" + } + end + + subject { helper.star_count_data_attributes(project) } + + context 'when user has already starred the project' do + let(:starred) { true } + let(:expected) { common_data_attributes.merge({ starred: "true" }) } + + it { is_expected.to eq(expected) } + end + + context 'when user has not starred the project' do + let(:starred) { false } + let(:expected) { common_data_attributes.merge({ starred: "false" }) } + + it { is_expected.to eq(expected) } + end + end + + describe '#notification_data_attributes' do + before do + allow(helper).to receive(:help_page_path).and_return(notification_help_path) + allow(project).to receive(:emails_disabled?).and_return(false) + end + + let(:notification_help_path) { 'notification/help/path' } + let(:notification_dropdown_items) { '["global","watch","participating","mention","disabled"]' } + + context "returns default user notification settings" do + let(:expected) do + { + emails_disabled: "false", + notification_dropdown_items: notification_dropdown_items, + notification_help_page_path: notification_help_path, + notification_level: "global" + } + end + + subject { helper.notification_data_attributes(project) } + + it { is_expected.to eq(expected) } + end + + context "returns configured users notification settings" do + before do + allow(project).to receive(:emails_disabled?).and_return(true) + setting = user.notification_settings_for(project) + setting.level = :watch + setting.save! + end + + let(:expected) do + { + emails_disabled: "true", + notification_dropdown_items: notification_dropdown_items, + notification_help_page_path: notification_help_path, + notification_level: "watch" + } + end + + subject { helper.notification_data_attributes(project) } + + it { is_expected.to eq(expected) } + end + end + + describe '#home_panel_data_attributes' do + using RSpec::Parameterized::TableSyntax + + before do + allow(helper).to receive(:groups_projects_more_actions_dropdown_data).and_return(nil) + allow(helper).to receive(:fork_button_data_attributes).and_return(nil) + allow(helper).to receive(:notification_data_attributes).and_return(nil) + allow(helper).to receive(:star_count_data_attributes).and_return({}) + end + + where(:can_read_project, :is_empty_repo) do + true | true + false | false + end + + with_them do + context "returns default user project details" do + before do + allow(helper).to receive(:can?).with(user, :read_project, project).and_return(can_read_project) + allow(project).to receive(:empty_repo?).and_return(is_empty_repo) + end + + let(:expected) do + { + can_read_project: can_read_project.to_s, + is_project_empty: is_empty_repo.to_s, + project_id: project.id + } + end + + subject { helper.home_panel_data_attributes } + + it { is_expected.to eq(expected) } + end + end + end + shared_examples 'configure import method modal' do context 'as a user' do it 'returns a link to contact an administrator' do diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index 0282e149b2552..2f80eab613e3e 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -37,44 +37,17 @@ end end - context 'notifications' do + context 'home panel' do let(:project) { create(:project) } before do assign(:project, project) - - allow(view).to receive(:current_user).and_return(user) - allow(view).to receive(:can?).with(user, :read_project, project).and_return(false) - allow(project).to receive(:license_anchor_data).and_return(false) - end - - context 'when user is signed in' do - let(:user) { create(:user) } - - before do - notification_settings = user.notification_settings_for(project) - assign(:notification_setting, notification_settings) - end - - it 'renders Vue app root' do - render - - expect(rendered).to have_selector('.js-vue-notification-dropdown') - end end - context 'when user is signed out' do - let(:user) { nil } - - before do - assign(:notification_setting, nil) - end - - it 'does not render Vue app root' do - render + it 'renders Vue app root' do + render - expect(rendered).not_to have_selector('.js-vue-notification-dropdown') - end + expect(rendered).to have_selector('#js-home-panel') end end -- GitLab