From 913d25b23248e4f2b11f2695bcf88cdad25834bf Mon Sep 17 00:00:00 2001
From: Peter Hegman <phegman@gitlab.com>
Date: Fri, 14 Jul 2023 17:18:06 +0000
Subject: [PATCH] Add empty state to Followers and Following tabs

On Vue version of user profile. Also modify the Snippets empty state.
---
 .../javascripts/lib/utils/common_utils.js     | 10 ++++
 .../javascripts/profile/components/follow.vue | 33 ++++++++++++-
 .../profile/components/followers_tab.vue      |  4 ++
 .../profile/components/following_tab.vue      |  4 ++
 .../components/snippets/snippets_tab.vue      | 37 +++++++++++---
 app/assets/javascripts/profile/index.js       |  4 ++
 app/helpers/users_helper.rb                   |  4 +-
 locale/gitlab.pot                             | 24 +++++++--
 spec/frontend/lib/utils/common_utils_spec.js  | 34 +++++++++++++
 .../profile/components/follow_spec.js         | 49 ++++++++++++++++++-
 .../profile/components/followers_tab_spec.js  |  2 +
 .../profile/components/following_tab_spec.js  |  2 +
 .../components/snippets/snippets_tab_spec.js  | 43 ++++++++++++++--
 spec/frontend/profile/mock_data.js            |  1 +
 spec/helpers/users_helper_spec.rb             | 18 ++++++-
 15 files changed, 251 insertions(+), 18 deletions(-)

diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 7795dac18bcbb..cca4cf68f5e3c 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -721,3 +721,13 @@ export const getFirstPropertyValue = (data) => {
 
   return data[key];
 };
+
+export const isCurrentUser = (userId) => {
+  const currentUserId = window.gon?.current_user_id;
+
+  if (!currentUserId) {
+    return false;
+  }
+
+  return Number(userId) === currentUserId;
+};
diff --git a/app/assets/javascripts/profile/components/follow.vue b/app/assets/javascripts/profile/components/follow.vue
index 7bab8a1c30dd7..2673ab6fbf423 100644
--- a/app/assets/javascripts/profile/components/follow.vue
+++ b/app/assets/javascripts/profile/components/follow.vue
@@ -1,7 +1,14 @@
 <script>
-import { GlAvatarLabeled, GlAvatarLink, GlLoadingIcon, GlPagination } from '@gitlab/ui';
+import {
+  GlAvatarLabeled,
+  GlAvatarLink,
+  GlLoadingIcon,
+  GlPagination,
+  GlEmptyState,
+} from '@gitlab/ui';
 import { DEFAULT_PER_PAGE } from '~/api';
 import { NEXT, PREV } from '~/vue_shared/components/pagination/constants';
+import { isCurrentUser } from '~/lib/utils/common_utils';
 
 export default {
   i18n: {
@@ -13,7 +20,9 @@ export default {
     GlAvatarLink,
     GlLoadingIcon,
     GlPagination,
+    GlEmptyState,
   },
+  inject: ['followEmptyState', 'userId'],
   props: {
     /**
      * Expected format:
@@ -48,12 +57,34 @@ export default {
       required: false,
       default: DEFAULT_PER_PAGE,
     },
+    currentUserEmptyStateTitle: {
+      type: String,
+      required: true,
+    },
+    visitorEmptyStateTitle: {
+      type: String,
+      required: true,
+    },
+  },
+  computed: {
+    emptyStateTitle() {
+      return isCurrentUser(this.userId)
+        ? this.currentUserEmptyStateTitle
+        : this.visitorEmptyStateTitle;
+    },
   },
 };
 </script>
 
 <template>
   <gl-loading-icon v-if="loading" class="gl-mt-5" size="md" />
+  <gl-empty-state
+    v-else-if="!users.length"
+    class="gl-mt-5"
+    :svg-path="followEmptyState"
+    :svg-height="144"
+    :title="emptyStateTitle"
+  />
   <div v-else>
     <div class="gl-my-n3 gl-mx-n3 gl-display-flex gl-flex-wrap">
       <div v-for="user in users" :key="user.id" class="gl-p-3 gl-w-full gl-md-w-half gl-lg-w-25p">
diff --git a/app/assets/javascripts/profile/components/followers_tab.vue b/app/assets/javascripts/profile/components/followers_tab.vue
index 1fa579bc6118c..927424d6c3fef 100644
--- a/app/assets/javascripts/profile/components/followers_tab.vue
+++ b/app/assets/javascripts/profile/components/followers_tab.vue
@@ -12,6 +12,8 @@ export default {
     errorMessage: s__(
       'UserProfile|An error occurred loading the followers. Please refresh the page to try again.',
     ),
+    currentUserEmptyStateTitle: s__('UserProfile|You do not have any followers'),
+    visitorEmptyStateTitle: s__("UserProfile|This user doesn't have any followers"),
   },
   components: {
     GlBadge,
@@ -68,6 +70,8 @@ export default {
       :loading="loading"
       :page="page"
       :total-items="totalItems"
+      :current-user-empty-state-title="$options.i18n.currentUserEmptyStateTitle"
+      :visitor-empty-state-title="$options.i18n.visitorEmptyStateTitle"
       @pagination-input="onPaginationInput"
     />
   </gl-tab>
diff --git a/app/assets/javascripts/profile/components/following_tab.vue b/app/assets/javascripts/profile/components/following_tab.vue
index 27c16ee5b4652..66c7ee42a3f64 100644
--- a/app/assets/javascripts/profile/components/following_tab.vue
+++ b/app/assets/javascripts/profile/components/following_tab.vue
@@ -12,6 +12,8 @@ export default {
     errorMessage: s__(
       'UserProfile|An error occurred loading the following. Please refresh the page to try again.',
     ),
+    currentUserEmptyStateTitle: s__('UserProfile|You are not following other users'),
+    visitorEmptyStateTitle: s__("UserProfile|This user isn't following other users"),
   },
   components: {
     GlBadge,
@@ -69,6 +71,8 @@ export default {
       :loading="loading"
       :page="page"
       :total-items="totalItems"
+      :current-user-empty-state-title="$options.i18n.currentUserEmptyStateTitle"
+      :visitor-empty-state-title="$options.i18n.visitorEmptyStateTitle"
       @pagination-input="onPaginationInput"
     />
   </gl-tab>
diff --git a/app/assets/javascripts/profile/components/snippets/snippets_tab.vue b/app/assets/javascripts/profile/components/snippets/snippets_tab.vue
index fce5e2f5e78bd..95649f9645ba8 100644
--- a/app/assets/javascripts/profile/components/snippets/snippets_tab.vue
+++ b/app/assets/javascripts/profile/components/snippets/snippets_tab.vue
@@ -1,9 +1,11 @@
 <script>
 import { GlTab, GlKeysetPagination, GlEmptyState } from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
 import { convertToGraphQLId } from '~/graphql_shared/utils';
 import { TYPENAME_USER } from '~/graphql_shared/constants';
 import { SNIPPET_MAX_LIST_COUNT } from '~/profile/constants';
+import { isCurrentUser } from '~/lib/utils/common_utils';
+import { helpPagePath } from '~/helpers/help_page_helper';
 import getUserSnippets from '../graphql/get_user_snippets.query.graphql';
 import SnippetRow from './snippet_row.vue';
 
@@ -11,7 +13,11 @@ export default {
   name: 'SnippetsTab',
   i18n: {
     title: s__('UserProfile|Snippets'),
-    noSnippets: s__('UserProfiles|No snippets found.'),
+    currentUserEmptyStateTitle: s__('UserProfile|Get started with snippets'),
+    visitorEmptyStateTitle: s__("UserProfile|This user doesn't have any snippets"),
+    emptyStateDescription: s__('UserProfile|Store, share, and embed bits of code and text.'),
+    newSnippet: __('New snippet'),
+    learnMore: __('Learn more'),
   },
   components: {
     GlTab,
@@ -19,7 +25,7 @@ export default {
     GlEmptyState,
     SnippetRow,
   },
-  inject: ['userId', 'snippetsEmptyState'],
+  inject: ['userId', 'snippetsEmptyState', 'newSnippetPath'],
   data() {
     return {
       userInfo: {},
@@ -57,6 +63,14 @@ export default {
     hasSnippets() {
       return this.userSnippets?.length;
     },
+    emptyStateTitle() {
+      return isCurrentUser(this.userId)
+        ? this.$options.i18n.currentUserEmptyStateTitle
+        : this.$options.i18n.visitorEmptyStateTitle;
+    },
+    emptyStateDescription() {
+      return isCurrentUser(this.userId) ? this.$options.i18n.emptyStateDescription : null;
+    },
   },
   methods: {
     isLastSnippet(index) {
@@ -76,6 +90,7 @@ export default {
         beforeToken: this.pageInfo.startCursor,
       };
     },
+    helpPagePath,
   },
 };
 </script>
@@ -100,11 +115,17 @@ export default {
       </div>
     </template>
     <template v-if="!hasSnippets">
-      <gl-empty-state class="gl-mt-5" :svg-height="75" :svg-path="snippetsEmptyState">
-        <template #title>
-          <p class="gl-font-weight-bold gl-mt-n5">{{ $options.i18n.noSnippets }}</p>
-        </template>
-      </gl-empty-state>
+      <gl-empty-state
+        class="gl-mt-5"
+        :svg-path="snippetsEmptyState"
+        :svg-height="144"
+        :title="emptyStateTitle"
+        :description="emptyStateDescription"
+        :primary-button-link="newSnippetPath"
+        :primary-button-text="$options.i18n.newSnippet"
+        :secondary-button-text="$options.i18n.learnMore"
+        :secondary-button-link="helpPagePath('user/snippets')"
+      />
     </template>
   </gl-tab>
 </template>
diff --git a/app/assets/javascripts/profile/index.js b/app/assets/javascripts/profile/index.js
index 198ffdb434bbd..76430d7b34df2 100644
--- a/app/assets/javascripts/profile/index.js
+++ b/app/assets/javascripts/profile/index.js
@@ -21,6 +21,8 @@ export const initProfileTabs = () => {
     utcOffset,
     userId,
     snippetsEmptyState,
+    newSnippetPath,
+    followEmptyState,
   } = el.dataset;
 
   const apolloProvider = new VueApollo({
@@ -39,6 +41,8 @@ export const initProfileTabs = () => {
       utcOffset,
       userId,
       snippetsEmptyState,
+      newSnippetPath,
+      followEmptyState,
     },
     render(createElement) {
       return createElement(ProfileTabs);
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 383f482687c19..29998a996e2d7 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -190,7 +190,9 @@ def user_profile_tabs_app_data(user)
       user_activity_path: user_activity_path(user, :json),
       utc_offset: local_timezone_instance(user.timezone).now.utc_offset,
       user_id: user.id,
-      snippets_empty_state: image_path('illustrations/empty-state/empty-snippets-md.svg')
+      snippets_empty_state: image_path('illustrations/empty-state/empty-snippets-md.svg'),
+      new_snippet_path: (new_snippet_path if can?(current_user, :create_snippet)),
+      follow_empty_state: image_path('illustrations/empty-state/empty-friends-md.svg')
     }
   end
 
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d7ab526bf1bb0..a0d9ede9a6ca8 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -49898,9 +49898,6 @@ msgstr ""
 msgid "UserList|created %{timeago}"
 msgstr ""
 
-msgid "UserProfiles|No snippets found."
-msgstr ""
-
 msgid "UserProfile|%{count} %{file}"
 msgstr ""
 
@@ -49955,6 +49952,9 @@ msgstr ""
 msgid "UserProfile|Following"
 msgstr ""
 
+msgid "UserProfile|Get started with snippets"
+msgstr ""
+
 msgid "UserProfile|Groups"
 msgstr ""
 
@@ -49994,15 +49994,24 @@ msgstr ""
 msgid "UserProfile|Starred projects"
 msgstr ""
 
+msgid "UserProfile|Store, share, and embed bits of code and text."
+msgstr ""
+
 msgid "UserProfile|Subscribe"
 msgstr ""
 
 msgid "UserProfile|There are no projects available to be displayed here."
 msgstr ""
 
+msgid "UserProfile|This user doesn't have any followers"
+msgstr ""
+
 msgid "UserProfile|This user doesn't have any followers."
 msgstr ""
 
+msgid "UserProfile|This user doesn't have any snippets"
+msgstr ""
+
 msgid "UserProfile|This user has a private profile"
 msgstr ""
 
@@ -50015,6 +50024,9 @@ msgstr ""
 msgid "UserProfile|This user is blocked"
 msgstr ""
 
+msgid "UserProfile|This user isn't following other users"
+msgstr ""
+
 msgid "UserProfile|This user isn't following other users."
 msgstr ""
 
@@ -50036,12 +50048,18 @@ msgstr ""
 msgid "UserProfile|View user in admin area"
 msgstr ""
 
+msgid "UserProfile|You are not following other users"
+msgstr ""
+
 msgid "UserProfile|You are not following other users."
 msgstr ""
 
 msgid "UserProfile|You can create a group for several dependent projects."
 msgstr ""
 
+msgid "UserProfile|You do not have any followers"
+msgstr ""
+
 msgid "UserProfile|You do not have any followers."
 msgstr ""
 
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index b4ec00ab76603..444d4a96f9c10 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -1140,4 +1140,38 @@ describe('common_utils', () => {
       expect(result).toEqual([{ hello: '' }, { helloWorld: '' }]);
     });
   });
+
+  describe('isCurrentUser', () => {
+    describe('when user is not signed in', () => {
+      it('returns `false`', () => {
+        window.gon.current_user_id = null;
+
+        expect(commonUtils.isCurrentUser(1)).toBe(false);
+      });
+    });
+
+    describe('when current user id does not match the provided user id', () => {
+      it('returns `false`', () => {
+        window.gon.current_user_id = 2;
+
+        expect(commonUtils.isCurrentUser(1)).toBe(false);
+      });
+    });
+
+    describe('when current user id matches the provided user id', () => {
+      it('returns `true`', () => {
+        window.gon.current_user_id = 1;
+
+        expect(commonUtils.isCurrentUser(1)).toBe(true);
+      });
+    });
+
+    describe('when provided user id is a string and it matches current user id', () => {
+      it('returns `true`', () => {
+        window.gon.current_user_id = 1;
+
+        expect(commonUtils.isCurrentUser('1')).toBe(true);
+      });
+    });
+  });
 });
diff --git a/spec/frontend/profile/components/follow_spec.js b/spec/frontend/profile/components/follow_spec.js
index 2555e41257fd8..a2e8d065a462b 100644
--- a/spec/frontend/profile/components/follow_spec.js
+++ b/spec/frontend/profile/components/follow_spec.js
@@ -1,11 +1,19 @@
-import { GlAvatarLabeled, GlAvatarLink, GlLoadingIcon, GlPagination } from '@gitlab/ui';
+import {
+  GlAvatarLabeled,
+  GlAvatarLink,
+  GlEmptyState,
+  GlLoadingIcon,
+  GlPagination,
+} from '@gitlab/ui';
 import { shallowMount } from '@vue/test-utils';
 
 import users from 'test_fixtures/api/users/followers/get.json';
 import Follow from '~/profile/components/follow.vue';
 import { DEFAULT_PER_PAGE } from '~/api';
+import { isCurrentUser } from '~/lib/utils/common_utils';
 
 jest.mock('~/rest_api');
+jest.mock('~/lib/utils/common_utils');
 
 describe('FollowersTab', () => {
   let wrapper;
@@ -15,6 +23,13 @@ describe('FollowersTab', () => {
     loading: false,
     page: 1,
     totalItems: 50,
+    currentUserEmptyStateTitle: 'UserProfile|You do not have any followers.',
+    visitorEmptyStateTitle: "UserProfile|This user doesn't have any followers.",
+  };
+
+  const defaultProvide = {
+    followEmptyState: '/illustrations/empty-state/empty-friends-md.svg',
+    userId: '1',
   };
 
   const createComponent = ({ propsData = {} } = {}) => {
@@ -23,11 +38,13 @@ describe('FollowersTab', () => {
         ...defaultPropsData,
         ...propsData,
       },
+      provide: defaultProvide,
     });
   };
 
   const findPagination = () => wrapper.findComponent(GlPagination);
   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+  const findEmptyState = () => wrapper.findComponent(GlEmptyState);
 
   describe('when `loading` prop is `true`', () => {
     it('renders loading icon', () => {
@@ -95,5 +112,35 @@ describe('FollowersTab', () => {
         expect(wrapper.emitted('pagination-input')).toEqual([[nextPage]]);
       });
     });
+
+    describe('when the users prop is empty', () => {
+      describe('when user is the current user', () => {
+        beforeEach(() => {
+          isCurrentUser.mockImplementation(() => true);
+          createComponent({ propsData: { users: [] } });
+        });
+
+        it('displays empty state with correct message', () => {
+          expect(findEmptyState().props()).toMatchObject({
+            svgPath: defaultProvide.followEmptyState,
+            title: defaultPropsData.currentUserEmptyStateTitle,
+          });
+        });
+      });
+
+      describe('when user is a visitor', () => {
+        beforeEach(() => {
+          isCurrentUser.mockImplementation(() => false);
+          createComponent({ propsData: { users: [] } });
+        });
+
+        it('displays empty state with correct message', () => {
+          expect(findEmptyState().props()).toMatchObject({
+            svgPath: defaultProvide.followEmptyState,
+            title: defaultPropsData.visitorEmptyStateTitle,
+          });
+        });
+      });
+    });
   });
 });
diff --git a/spec/frontend/profile/components/followers_tab_spec.js b/spec/frontend/profile/components/followers_tab_spec.js
index 0370005d0a4e2..75586a2c9eaa4 100644
--- a/spec/frontend/profile/components/followers_tab_spec.js
+++ b/spec/frontend/profile/components/followers_tab_spec.js
@@ -75,6 +75,8 @@ describe('FollowersTab', () => {
         loading: false,
         page: 1,
         totalItems: 6,
+        currentUserEmptyStateTitle: FollowersTab.i18n.currentUserEmptyStateTitle,
+        visitorEmptyStateTitle: FollowersTab.i18n.visitorEmptyStateTitle,
       });
     });
 
diff --git a/spec/frontend/profile/components/following_tab_spec.js b/spec/frontend/profile/components/following_tab_spec.js
index 1eadb2c738886..48d84187739e4 100644
--- a/spec/frontend/profile/components/following_tab_spec.js
+++ b/spec/frontend/profile/components/following_tab_spec.js
@@ -68,6 +68,8 @@ describe('FollowingTab', () => {
         loading: false,
         page: MOCK_PAGE,
         totalItems: MOCK_TOTAL_FOLLOWING,
+        currentUserEmptyStateTitle: FollowingTab.i18n.currentUserEmptyStateTitle,
+        visitorEmptyStateTitle: FollowingTab.i18n.visitorEmptyStateTitle,
       });
     });
 
diff --git a/spec/frontend/profile/components/snippets/snippets_tab_spec.js b/spec/frontend/profile/components/snippets/snippets_tab_spec.js
index 47e2fbcf2c09e..5992bb03e4d2c 100644
--- a/spec/frontend/profile/components/snippets/snippets_tab_spec.js
+++ b/spec/frontend/profile/components/snippets/snippets_tab_spec.js
@@ -7,6 +7,7 @@ import { SNIPPET_MAX_LIST_COUNT } from '~/profile/constants';
 import SnippetsTab from '~/profile/components/snippets/snippets_tab.vue';
 import SnippetRow from '~/profile/components/snippets/snippet_row.vue';
 import getUserSnippets from '~/profile/components/graphql/get_user_snippets.query.graphql';
+import { isCurrentUser } from '~/lib/utils/common_utils';
 import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import {
@@ -15,8 +16,14 @@ import {
   MOCK_USER_SNIPPETS_RES,
   MOCK_USER_SNIPPETS_PAGINATION_RES,
   MOCK_USER_SNIPPETS_EMPTY_RES,
+  MOCK_NEW_SNIPPET_PATH,
 } from 'jest/profile/mock_data';
 
+jest.mock('~/lib/utils/common_utils');
+jest.mock('~/helpers/help_page_helper', () => ({
+  helpPagePath: jest.fn().mockImplementation(() => 'http://127.0.0.1:3000/help/user/snippets'),
+}));
+
 Vue.use(VueApollo);
 
 describe('UserProfileSnippetsTab', () => {
@@ -32,6 +39,7 @@ describe('UserProfileSnippetsTab', () => {
       provide: {
         userId: MOCK_USER.id,
         snippetsEmptyState: MOCK_SNIPPETS_EMPTY_STATE,
+        newSnippetPath: MOCK_NEW_SNIPPET_PATH,
       },
     });
   };
@@ -52,9 +60,38 @@ describe('UserProfileSnippetsTab', () => {
       expect(findSnippetRows().exists()).toBe(false);
     });
 
-    it('does render empty state with correct svg', () => {
-      expect(findGlEmptyState().exists()).toBe(true);
-      expect(findGlEmptyState().attributes('svgpath')).toBe(MOCK_SNIPPETS_EMPTY_STATE);
+    describe('when user is the current user', () => {
+      beforeEach(() => {
+        isCurrentUser.mockImplementation(() => true);
+        createComponent();
+      });
+
+      it('displays empty state with correct message', () => {
+        expect(findGlEmptyState().props()).toMatchObject({
+          svgPath: MOCK_SNIPPETS_EMPTY_STATE,
+          title: SnippetsTab.i18n.currentUserEmptyStateTitle,
+          description: SnippetsTab.i18n.emptyStateDescription,
+          primaryButtonLink: MOCK_NEW_SNIPPET_PATH,
+          primaryButtonText: SnippetsTab.i18n.newSnippet,
+          secondaryButtonLink: 'http://127.0.0.1:3000/help/user/snippets',
+          secondaryButtonText: SnippetsTab.i18n.learnMore,
+        });
+      });
+    });
+
+    describe('when user is a visitor', () => {
+      beforeEach(() => {
+        isCurrentUser.mockImplementation(() => false);
+        createComponent();
+      });
+
+      it('displays empty state with correct message', () => {
+        expect(findGlEmptyState().props()).toMatchObject({
+          svgPath: MOCK_SNIPPETS_EMPTY_STATE,
+          title: SnippetsTab.i18n.visitorEmptyStateTitle,
+          description: null,
+        });
+      });
     });
   });
 
diff --git a/spec/frontend/profile/mock_data.js b/spec/frontend/profile/mock_data.js
index 856534aebd375..6c4ff0a84f95a 100644
--- a/spec/frontend/profile/mock_data.js
+++ b/spec/frontend/profile/mock_data.js
@@ -22,6 +22,7 @@ export const userCalendarResponse = {
 };
 
 export const MOCK_SNIPPETS_EMPTY_STATE = 'illustrations/empty-state/empty-snippets-md.svg';
+export const MOCK_NEW_SNIPPET_PATH = '/-/snippets/new';
 
 export const MOCK_USER = {
   id: '1',
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 6ee208dfd15cc..c0d3c31a36d44 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -496,13 +496,17 @@ def stub_profile_permission_allowed(allowed, current_user = nil)
 
   describe '#user_profile_tabs_app_data' do
     before do
+      allow(helper).to receive(:current_user).and_return(user)
       allow(helper).to receive(:user_calendar_path).with(user, :json).and_return('/users/root/calendar.json')
       allow(helper).to receive(:user_activity_path).with(user, :json).and_return('/users/root/activity.json')
+      allow(helper).to receive(:new_snippet_path).and_return('/-/snippets/new')
       allow(user).to receive_message_chain(:followers, :count).and_return(2)
       allow(user).to receive_message_chain(:followees, :count).and_return(3)
     end
 
     it 'returns expected hash' do
+      allow(helper).to receive(:can?).with(user, :create_snippet).and_return(true)
+
       expect(helper.user_profile_tabs_app_data(user)).to match({
         followees_count: 3,
         followers_count: 2,
@@ -510,9 +514,21 @@ def stub_profile_permission_allowed(allowed, current_user = nil)
         user_activity_path: '/users/root/activity.json',
         utc_offset: 0,
         user_id: user.id,
-        snippets_empty_state: match_asset_path('illustrations/empty-state/empty-snippets-md.svg')
+        new_snippet_path: '/-/snippets/new',
+        snippets_empty_state: match_asset_path('illustrations/empty-state/empty-snippets-md.svg'),
+        follow_empty_state: match_asset_path('illustrations/empty-state/empty-friends-md.svg')
       })
     end
+
+    context 'when user does not have create_snippet permissions' do
+      before do
+        allow(helper).to receive(:can?).with(user, :create_snippet).and_return(false)
+      end
+
+      it 'returns nil for new_snippet_path property' do
+        expect(helper.user_profile_tabs_app_data(user)[:new_snippet_path]).to be_nil
+      end
+    end
   end
 
   describe '#load_max_project_member_accesses' do
-- 
GitLab