From 066ca76baf17e3adaca89966a49923547867b9a1 Mon Sep 17 00:00:00 2001 From: Wu Jeremy <jeremyw@jihulab.com> Date: Thu, 20 Jul 2023 16:54:29 +0000 Subject: [PATCH] Feat: implement rss functionality for profile edit overflow menu * Add RSS subscription link to the newly introduced overflow menu. * Add test cases against the addition. --- .../actions/components/user_actions_app.vue | 26 ++++++++- .../users/profile/actions/index.js | 3 +- app/views/users/show.html.haml | 2 +- spec/features/users/rss_spec.rb | 57 +++++++++++++------ .../components/user_actions_app_spec.js | 31 +++++++++- 5 files changed, 96 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/users/profile/actions/components/user_actions_app.vue b/app/assets/javascripts/users/profile/actions/components/user_actions_app.vue index bf983d911ea6..a8ecc014a959 100644 --- a/app/assets/javascripts/users/profile/actions/components/user_actions_app.vue +++ b/app/assets/javascripts/users/profile/actions/components/user_actions_app.vue @@ -11,13 +11,17 @@ export default { type: String, required: true, }, + rssSubscriptionPath: { + type: String, + required: true, + }, }, data() { return { - // Only implement the copy function in MR for now + // Only implement the copy function and RSS subscription in MR for now // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122971 // The rest will be implemented in the upcoming MR. - dropdownItems: [ + defaultDropdownItems: [ { action: this.onUserIdCopy, text: sprintf(this.$options.i18n.userId, { id: this.userId }), @@ -28,6 +32,23 @@ export default { ], }; }, + computed: { + dropdownItems() { + if (this.rssSubscriptionPath) { + return [ + ...this.defaultDropdownItems, + { + href: this.rssSubscriptionPath, + text: this.$options.i18n.rssSubscribe, + extraAttrs: { + 'data-testid': 'user-profile-rss-subscription-link', + }, + }, + ]; + } + return this.defaultDropdownItems; + }, + }, methods: { onUserIdCopy() { this.$toast.show(this.$options.i18n.userIdCopied); @@ -36,6 +57,7 @@ export default { i18n: { userId: s__('UserProfile|Copy user ID: %{id}'), userIdCopied: s__('UserProfile|User ID copied to clipboard'), + rssSubscribe: s__('UserProfile|Subscribe'), }, }; </script> diff --git a/app/assets/javascripts/users/profile/actions/index.js b/app/assets/javascripts/users/profile/actions/index.js index 37a3faf82a50..22507165eced 100644 --- a/app/assets/javascripts/users/profile/actions/index.js +++ b/app/assets/javascripts/users/profile/actions/index.js @@ -7,7 +7,7 @@ export const initUserActionsApp = () => { if (!mountingEl) return false; - const { userId } = mountingEl.dataset; + const { userId, rssSubscriptionPath } = mountingEl.dataset; Vue.use(GlToast); @@ -18,6 +18,7 @@ export const initUserActionsApp = () => { return createElement(UserActionsApp, { props: { userId, + rssSubscriptionPath, }, }); }, diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 380d6aacb848..810a2ca36070 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -26,7 +26,7 @@ = s_("UserProfile|Edit profile") = render 'users/view_gpg_keys' = render 'users/view_user_in_admin_area' - .js-user-profile-actions{ data: { user_id: @user.id } } + .js-user-profile-actions{ data: { user_id: @user.id, rss_subscription_path: can?(current_user, :read_user_profile, @user) ? user_path(@user, rss_url_options) : '' } } - else = render layout: 'users/cover_controls' do - if @user == current_user diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb index 39b6d049e435..2db58ce04a1e 100644 --- a/spec/features/users/rss_spec.rb +++ b/spec/features/users/rss_spec.rb @@ -6,28 +6,53 @@ let(:user) { create(:user) } let(:path) { user_path(create(:user)) } - before do - stub_feature_flags(user_profile_overflow_menu_vue: false) - end - - context 'when signed in' do + describe 'with "user_profile_overflow_menu_vue" feature flag off' do before do - sign_in(user) - visit path + stub_feature_flags(user_profile_overflow_menu_vue: false) end - it_behaves_like "it has an RSS button with current_user's feed token" - end + context 'when signed in' do + before do + sign_in(user) + visit path + end - context 'when signed out' do - before do - visit path + it_behaves_like "it has an RSS button with current_user's feed token" end - it_behaves_like "it has an RSS button without a feed token" + context 'when signed out' do + before do + visit path + end + + it_behaves_like "it has an RSS button without a feed token" + end end - # TODO: implement tests before the FF "user_profile_overflow_menu_vue" is turned on - # See: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122971 - # Related Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/416974 + describe 'with "user_profile_overflow_menu_vue" feature flag on', :js do + context 'when signed in' do + before do + sign_in(user) + visit path + end + + it 'shows the RSS link with overflow menu' do + find('[data-testid="base-dropdown-toggle"').click + + expect(page).to have_link 'Subscribe', href: /feed_token=glft-.*-#{user.id}/ + end + end + + context 'when signed out' do + before do + visit path + end + + it 'has an RSS without a feed token' do + find('[data-testid="base-dropdown-toggle"').click + + expect(page).not_to have_link 'Subscribe', href: /feed_token=glft-.*-#{user.id}/ + end + end + end end diff --git a/spec/frontend/users/profile/actions/components/user_actions_app_spec.js b/spec/frontend/users/profile/actions/components/user_actions_app_spec.js index d27962440ee1..c2ae3d8364f2 100644 --- a/spec/frontend/users/profile/actions/components/user_actions_app_spec.js +++ b/spec/frontend/users/profile/actions/components/user_actions_app_spec.js @@ -6,11 +6,13 @@ describe('User Actions App', () => { let wrapper; const USER_ID = 'test-id'; + const DEFAULT_SUBSCRIPTION_PATH = ''; const createWrapper = (propsData = {}) => { wrapper = mountExtended(UserActionsApp, { propsData: { userId: USER_ID, + rssSubscriptionPath: DEFAULT_SUBSCRIPTION_PATH, ...propsData, }, }); @@ -19,15 +21,25 @@ describe('User Actions App', () => { const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); const findActions = () => wrapper.findAllByTestId('disclosure-dropdown-item'); const findAction = (position = 0) => findActions().at(position); + const findSubscriptionLink = () => wrapper.findByTestId('user-profile-rss-subscription-link'); it('shows dropdown', () => { createWrapper(); expect(findDropdown().exists()).toBe(true); }); - it('shows actions correctly', () => { - createWrapper(); - expect(findActions()).toHaveLength(1); + describe('shows user action items', () => { + it('should show items without RSS subscriptions', () => { + createWrapper(); + expect(findActions()).toHaveLength(1); + }); + + it('should show items with RSS subscriptions', () => { + createWrapper({ + rssSubscriptionPath: '/test/path', + }); + expect(findActions()).toHaveLength(2); + }); }); it('shows copy user id action', () => { @@ -35,4 +47,17 @@ describe('User Actions App', () => { expect(findAction().text()).toBe(`Copy user ID: ${USER_ID}`); expect(findAction().findComponent('button').attributes('data-clipboard-text')).toBe(USER_ID); }); + + it('shows subscription link when subscription url was presented', () => { + const testSubscriptionPath = '/test/path'; + + createWrapper({ + rssSubscriptionPath: testSubscriptionPath, + }); + + const rssLink = findSubscriptionLink(); + expect(rssLink.exists()).toBe(true); + expect(rssLink.attributes('href')).toBe(testSubscriptionPath); + expect(rssLink.text()).toBe('Subscribe'); + }); }); -- GitLab