From ed7ab572d7ab4edd51afc743d24d0bb2df59e4ce Mon Sep 17 00:00:00 2001 From: Van Anderson <van.m.anderson@gmail.com> Date: Fri, 26 Jul 2024 14:13:42 +0000 Subject: [PATCH] Refactor users/show.haml and user_tabs.js to remove legacy tabs Remove hidden legacy tabs from users/show.haml and refactor user_tabs.js to render lazy content into a single centralized content container. This allows a significant amount of code to be removed and simplified. Light refactors of these files also remove naming references to removed legacy concepts and enhance readability. --- .../javascripts/pages/users/user_tabs.js | 195 ++++-------------- app/controllers/users_controller.rb | 7 + app/helpers/users_helper.rb | 20 +- app/views/users/_legacy_tabs.html.haml | 11 + app/views/users/show.html.haml | 102 +-------- spec/features/calendar_spec.rb | 18 +- .../profiles/user_visits_profile_spec.rb | 14 +- spec/features/users/overview_spec.rb | 26 +-- spec/features/users/show_spec.rb | 109 +--------- ...user_browses_projects_on_user_page_spec.rb | 4 +- spec/helpers/users_helper_spec.rb | 25 ++- 11 files changed, 125 insertions(+), 406 deletions(-) create mode 100644 app/views/users/_legacy_tabs.html.haml diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index c857fd2fa2a65..670aabd633700 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -11,59 +11,6 @@ import { __ } from '~/locale'; import ActivityCalendar from './activity_calendar'; import UserOverviewBlock from './user_overview_block'; -/** - * UserTabs - * - * Handles persisting and restoring the current tab selection and lazily-loading - * content on the Users#show page. - * - * ### Example Markup - * - * <ul class="nav-links"> - * <li class="activity-tab active"> - * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/username"> - * Activity - * </a> - * </li> - * <li class="groups-tab"> - * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/users/username/groups"> - * Groups - * </a> - * </li> - * <li class="contributed-tab"> - * ... - * </li> - * <li class="projects-tab"> - * ... - * </li> - * <li class="snippets-tab"> - * ... - * </li> - * </ul> - * - * <div class="tab-content"> - * <div class="tab-pane" id="activity"> - * Activity Content - * </div> - * <div class="tab-pane" id="groups"> - * Groups Content - * </div> - * <div class="tab-pane" id="contributed"> - * Contributed projects content - * </div> - * <div class="tab-pane" id="projects"> - * Projects content - * </div> - * <div class="tab-pane" id="snippets"> - * Snippets content - * </div> - * </div> - * - * <div class="loading"> - * Loading Animation - * </div> - */ - const CALENDAR_TEMPLATE = ` <div class="calendar"> <div class="js-contrib-calendar gl-overflow-x-auto"></div> @@ -96,80 +43,54 @@ const CALENDAR_TEMPLATE = ` const CALENDAR_PERIOD_12_MONTHS = 12; +const DEFAULT_LOADER_ACTIONS = [ + 'groups', + 'contributed', + 'projects', + 'starred', + 'snippets', + 'followers', + 'following', +]; + export default class UserTabs { - constructor({ defaultAction, action, parentEl }) { - this.loaded = {}; - this.defaultAction = defaultAction || 'overview'; - this.action = action || this.defaultAction; - this.$parentEl = $(parentEl) || $(document); + constructor({ parentEl }) { + this.$legacyTabsContainer = $('#js-legacy-tabs-container'); + this.$parentEl = $(parentEl || document); this.windowLocation = window.location; - this.$parentEl.find('.nav-links a').each((i, navLink) => { - this.loaded[$(navLink).attr('data-action')] = false; - }); - this.actions = Object.keys(this.loaded); - this.bindEvents(); - // TODO: refactor to make this configurable via constructor params with a default value of 'show' - if (this.action === 'show') { - this.action = this.defaultAction; - } + const action = this.$legacyTabsContainer.data('action'); + const endpoint = this.$legacyTabsContainer.data('endpoint'); - this.activateTab(this.action); + this.bindPaginationEvent(); + this.loadPage(action, endpoint); } - bindEvents() { - this.$parentEl - .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') - .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', (event) => this.tabShown(event)) - .on('click', '.gl-pagination a', (event) => this.changeProjectsPage(event)); + bindPaginationEvent() { + this.$parentEl.on('click', '.gl-pagination a', (event) => this.changePage(event)); } - changeProjectsPage(e) { + changePage(e) { e.preventDefault(); - $('.tab-pane.active').empty(); + $('#js-legacy-tabs-container').empty(); const endpoint = $(e.target).attr('href'); - this.loadTab(this.getCurrentAction(), endpoint); - } - - tabShown(event) { - const $target = $(event.target); - const action = $target.data('action'); - const source = $target.attr('href'); - const endpoint = $target.data('endpoint'); - this.setTab(action, endpoint); - return this.setCurrentAction(source); - } - - activateTab(action) { - return this.$parentEl.find(`.nav-links .js-${action}-tab a`).tab('show'); + const action = this.$legacyTabsContainer.data('action'); + this.loadPage(action, endpoint); } - setTab(action, endpoint) { - if (this.loaded[action]) { - return; - } + loadPage(action, endpoint) { if (action === 'activity') { - this.loadActivities(); + // eslint-disable-next-line no-new + new Activities('#js-legacy-tabs-container'); } else if (action === 'overview') { - this.loadOverviewTab(); - } - - const loadableActions = [ - 'groups', - 'contributed', - 'projects', - 'starred', - 'snippets', - 'followers', - 'following', - ]; - if (loadableActions.indexOf(action) > -1) { - this.loadTab(action, endpoint); + this.loadOverviewPage(); + } else if (DEFAULT_LOADER_ACTIONS.includes(action)) { + this.defaultPageLoader(action, endpoint); } } - loadTab(action, endpoint) { + defaultPageLoader(action, endpoint) { this.toggleLoading(true); const params = action === 'projects' ? { skip_namespace: true } : {}; @@ -177,10 +98,9 @@ export default class UserTabs { return axios .get(endpoint, { params }) .then(({ data }) => { - const tabSelector = `div#${action}`; - this.$parentEl.find(tabSelector).html(data.html); - this.loaded[action] = true; - localTimeAgo(document.querySelectorAll(`${tabSelector} .js-timeago`)); + const containerSelector = `div#js-legacy-tabs-container`; + this.$parentEl.find(containerSelector).html(data.html); + localTimeAgo(document.querySelectorAll(`${containerSelector} .js-timeago`)); this.toggleLoading(false); }) @@ -189,35 +109,18 @@ export default class UserTabs { }); } - loadActivities() { - if (this.loaded.activity) { - return; - } - - // eslint-disable-next-line no-new - new Activities('#activity'); - - this.loaded.activity = true; - } - - loadOverviewTab() { - if (this.loaded.overview) { - return; - } - + loadOverviewPage() { initReadMore(); this.loadActivityCalendar(); - UserTabs.renderMostRecentBlocks('#js-overview .activities-block', { + UserTabs.renderMostRecentBlocks('#js-legacy-tabs-container .activities-block', { requestParams: { limit: 15 }, }); - UserTabs.renderMostRecentBlocks('#js-overview .projects-block', { + UserTabs.renderMostRecentBlocks('#js-legacy-tabs-container .projects-block', { requestParams: { limit: 3, skip_pagination: true, skip_namespace: true, card_mode: true }, }); - - this.loaded.overview = true; } static renderMostRecentBlocks(container, options) { @@ -234,7 +137,7 @@ export default class UserTabs { } loadActivityCalendar() { - const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar'); + const $calendarWrap = this.$parentEl.find('.user-calendar'); const calendarPath = $calendarWrap.data('calendarPath'); AjaxCache.retrieve(calendarPath) @@ -265,9 +168,9 @@ export default class UserTabs { // eslint-disable-next-line no-new new ActivityCalendar({ - container: '.tab-pane.active .js-contrib-calendar', - activitiesContainer: '.tab-pane.active .user-calendar-activities', - recentActivitiesContainer: '.tab-pane.active .overview-content-list', + container: '#js-legacy-tabs-container .js-contrib-calendar', + activitiesContainer: '#js-legacy-tabs-container .user-calendar-activities', + recentActivitiesContainer: '#js-legacy-tabs-container .overview-content-list', timestamps: data, calendarActivitiesPath, utcOffset, @@ -283,22 +186,4 @@ export default class UserTabs { toggleLoading(status) { return this.$parentEl.find('.loading').toggleClass('hide', !status); } - - setCurrentAction(source) { - let newState = source; - newState = newState.replace(/\/+$/, ''); - newState += this.windowLocation.search + this.windowLocation.hash; - window.history.replaceState( - { - url: newState, - }, - document.title, - newState, - ); - return newState; - } - - getCurrentAction() { - return this.$parentEl.find('.nav-links a.active').data('action'); - } } diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 55a50f5b3f55d..15a705fbf9503 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -21,6 +21,7 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } before_action :user, except: [:exists] + before_action :set_legacy_data before_action :authorize_read_user_profile!, only: [ :calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following ] @@ -307,6 +308,12 @@ def finder_params not_aimed_for_deletion: true } end + + def set_legacy_data + controller_action = params[:action] + @action = controller_action.gsub('show', 'overview') + @endpoint = request.path + end end UsersController.prepend_mod_with('UsersController') diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 7dcc514ab42b7..d9ae9368513f5 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -30,12 +30,12 @@ def user_email_help_text(user) }) + content_tag(:p) { confirmation_link } end - def profile_tabs - @profile_tabs ||= get_profile_tabs - end + def profile_actions(user) + return [] unless can?(current_user, :read_user_profile, user) + + return [:overview, :activity] if user.bot? - def profile_tab?(tab) - profile_tabs.include?(tab) + [:overview, :activity, :groups, :contributed, :projects, :starred, :snippets, :followers, :following] end def user_internal_regex_data @@ -278,16 +278,6 @@ def blocked_user_badge(user) { text: s_('AdminUsers|Blocked'), variant: 'danger' } end - def get_profile_tabs - tabs = [] - - if can?(current_user, :read_user_profile, @user) - tabs += [:overview, :activity, :groups, :contributed, :projects, :starred, :snippets, :followers, :following] - end - - tabs - end - def get_current_user_menu_items items = [] diff --git a/app/views/users/_legacy_tabs.html.haml b/app/views/users/_legacy_tabs.html.haml new file mode 100644 index 0000000000000..c7aa1b86bbd77 --- /dev/null +++ b/app/views/users/_legacy_tabs.html.haml @@ -0,0 +1,11 @@ +- if allowable_actions.include?(action.to_sym) + #js-legacy-tabs-container{ data: { action: action, endpoint: endpoint } } + - case action + - when 'overview' + = render "users/overview" + - when 'activity' + .flash-container + - if can?(current_user, :read_cross_project) + .content_list.user-activity-content{ data: { href: user_activity_path } } + .loading + = render Pajamas::SpinnerComponent.new(size: :md) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 41166503ed798..7d139d3b26ad7 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -8,6 +8,7 @@ - add_page_specific_style 'page_bundles/projects' - @force_desktop_expanded_sidebar = true - nav "user_profile" +- allowable_actions = profile_actions(@user) = content_for :meta_tags do = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") @@ -43,108 +44,19 @@ = markdown_field(@user.status, :message) .user-profile{ class: @user.blocked? ? '' : 'user-profile-with-sidebar' } .user-profile-content - - if !profile_tabs.empty? && Feature.enabled?(:profile_tabs_vue, current_user) - #js-profile-tabs{ data: user_profile_tabs_app_data(@user) } - - unless Feature.enabled?(:profile_tabs_vue, current_user) - .tab-content - - if profile_tab?(:overview) - #js-overview.tab-pane.user-overview-page - = render "users/overview" - - - if profile_tab?(:activity) - #activity.tab-pane - .flash-container - - if can?(current_user, :read_cross_project) - .content_list.user-activity-content{ data: { href: user_activity_path } } - .loading - = gl_loading_icon(size: 'md') - - unless @user.bot? - - if profile_tab?(:groups) - #groups.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:contributed) - #contributed.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:projects) - #projects.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:starred) - #starred.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:snippets) - #snippets.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:followers) - #followers.tab-pane - -# This tab is always loaded via AJAX - - - if profile_tab?(:following) - #following.tab-pane - -# This tab is always loaded via AJAX - + - if allowable_actions.any? + - if Feature.enabled?(:profile_tabs_vue, current_user) + #js-profile-tabs{ data: user_profile_tabs_app_data(@user) } + - else + = render 'users/legacy_tabs', action: @action, endpoint: @endpoint, allowable_actions: allowable_actions .loading.hide = render Pajamas::SpinnerComponent.new(size: :md) - - - if profile_tabs.empty? + - else - if @user.blocked? = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-access-md.svg', title: s_('UserProfile|This user is blocked')) - else = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-private-md.svg', title: s_('UserProfile|This user has a private profile')) - - unless @user.blocked? = render 'users/profile_sidebar' - - -# TODO: Remove this with the removal of the old navigatifon. - -# See https://gitlab.com/gitlab-org/gitlab/-/issues/435899. - - if !profile_tabs.empty? && !Feature.enabled?(:profile_tabs_vue, current_user) - .scrolling-tabs-container.gl-display-none - %button.fade-left{ type: 'button', title: _('Scroll left'), 'aria-label': _('Scroll left') } - = sprite_icon('chevron-lg-left', size: 12) - %button.fade-right{ type: 'button', title: _('Scroll right'), 'aria-label': _('Scroll right') } - = sprite_icon('chevron-lg-right', size: 12) - %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs.gl-border-b-0 - - if profile_tab?(:overview) - %li.js-overview-tab - = link_to user_path, data: { target: 'div#js-overview', action: 'overview', toggle: 'tab' } do - = s_('UserProfile|Overview') - - if profile_tab?(:activity) - %li.js-activity-tab - = link_to user_activity_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do - = s_('UserProfile|Activity') - - if profile_tab?(:groups) - %li.js-groups-tab - = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do - = s_('UserProfile|Groups') - - if profile_tab?(:contributed) - %li.js-contributed-tab - = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do - = s_('UserProfile|Contributed projects') - - if profile_tab?(:projects) - %li.js-projects-tab - = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do - = s_('UserProfile|Personal projects') - - if profile_tab?(:starred) - %li.js-starred-tab - = link_to user_starred_projects_path, data: { target: 'div#starred', action: 'starred', toggle: 'tab', endpoint: user_starred_projects_path(format: :json), card_mode: true } do - = s_('UserProfile|Starred projects') - - if profile_tab?(:snippets) - %li.js-snippets-tab - = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do - = s_('UserProfile|Snippets') - - if profile_tab?(:followers) - %li.js-followers-tab - = link_to user_followers_path, data: { target: 'div#followers', action: 'followers', toggle: 'tab', endpoint: user_followers_path(format: :json) } do - = s_('UserProfile|Followers') - = gl_badge_tag @user.followers.count - - if profile_tab?(:following) - %li.js-following-tab - = link_to user_following_path, data: { target: 'div#following', action: 'following', toggle: 'tab', endpoint: user_following_path(format: :json), testid: 'following_tab' } do - = s_('UserProfile|Following') - = gl_badge_tag @user.followees.count diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 501af5ade1a99..48396eddbde03 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -77,7 +77,7 @@ def note_comment_contribution end def selected_day_activities(visible: true) - find('#js-overview .user-calendar-activities', visible: visible).text + find('#js-legacy-tabs-container .user-calendar-activities', visible: visible).text end def recent_activities(visible: true) @@ -101,11 +101,11 @@ def recent_activities(visible: true) include_context 'when user page is visited' it 'displays calendar' do - expect(find('#js-overview')).to have_css('.js-contrib-calendar') + expect(find('#js-legacy-tabs-container')).to have_css('.js-contrib-calendar') end describe 'select calendar day' do - let(:cells) { page.all('#js-overview .user-contrib-cell') } + let(:cells) { page.all('#js-legacy-tabs-container .user-contrib-cell') } before do cells[0].click @@ -152,10 +152,10 @@ def recent_activities(visible: true) include_context 'when user page is visited' it 'displays calendar activity square for 1 contribution', :sidekiq_inline do - expect(find('#js-overview')).to have_selector(get_cell_level_selector(contribution_count), count: 1) + expect(find('#js-legacy-tabs-container')).to have_selector(get_cell_level_selector(contribution_count), count: 1) today = Date.today.strftime(date_format) - expect(find('#js-overview')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) + expect(find('#js-legacy-tabs-container')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) end end @@ -179,7 +179,7 @@ def recent_activities(visible: true) include_context 'when user page is visited' it 'displays calendar activity log', :sidekiq_inline do - expect(all('#js-overview .overview-content-list .event-target-title').map(&:text)).to contain_exactly( + expect(all('#js-legacy-tabs-container .overview-content-list .event-target-title').map(&:text)).to contain_exactly( match(/#{issue_title}/), match(/new task/) ) @@ -219,17 +219,17 @@ def recent_activities(visible: true) include_context 'when user page is visited' it 'displays calendar activity squares for both days', :sidekiq_inline do - expect(find('#js-overview')).to have_selector(get_cell_level_selector(1), count: 2) + expect(find('#js-legacy-tabs-container')).to have_selector(get_cell_level_selector(1), count: 2) end it 'displays calendar activity square for yesterday', :sidekiq_inline do yesterday = Date.yesterday.strftime(date_format) - expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, yesterday), count: 1) + expect(find('#js-legacy-tabs-container')).to have_selector(get_cell_date_selector(1, yesterday), count: 1) end it 'displays calendar activity square for today' do today = Date.today.strftime(date_format) - expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, today), count: 1) + expect(find('#js-legacy-tabs-container')).to have_selector(get_cell_date_selector(1, today), count: 1) end end end diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb index b8d8d4b429786..7ea813af19da3 100644 --- a/spec/features/profiles/user_visits_profile_spec.rb +++ b/spec/features/profiles/user_visits_profile_spec.rb @@ -79,7 +79,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Groups' } - let(:div) { '#groups' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { group.name } end end @@ -98,7 +98,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Contributed projects' } - let(:div) { '#contributed' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { project.name } end end @@ -110,7 +110,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Personal projects' } - let(:div) { '#projects' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { project.name } end end @@ -124,7 +124,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Starred projects' } - let(:div) { '#starred' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { project.name } end end @@ -134,7 +134,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Snippets' } - let(:div) { '#snippets' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { snippet.title } end end @@ -148,7 +148,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Followers' } - let(:div) { '#followers' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { fan.name } end end @@ -162,7 +162,7 @@ it_behaves_like 'shows expected content' do let(:link) { 'Following' } - let(:div) { '#following' } + let(:div) { '#js-legacy-tabs-container' } let(:expected_content) { star.name } end end diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb index 4ef22f7f7b73e..b819e26f6652e 100644 --- a/spec/features/users/overview_spec.rb +++ b/spec/features/users/overview_spec.rb @@ -44,7 +44,7 @@ def push_code_contribution end it 'does not show a link to the activity list' do - expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: false) + expect(find('#js-legacy-tabs-container .activities-block')).to have_selector('.js-view-all', visible: false) end end @@ -56,7 +56,7 @@ def push_code_contribution include_context 'visit overview tab' it 'display 3 entries in the list of activities' do - expect(find('#js-overview')).to have_selector('.event-item', count: 3) + expect(find('#js-legacy-tabs-container')).to have_selector('.event-item', count: 3) end end @@ -68,11 +68,11 @@ def push_code_contribution include_context 'visit overview tab' it 'displays 15 entries in the list of activities' do - expect(find('#js-overview')).to have_selector('.event-item', count: 15) + expect(find('#js-legacy-tabs-container')).to have_selector('.event-item', count: 15) end it 'shows a link to the activity list' do - expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: true) + expect(find('#js-legacy-tabs-container .activities-block')).to have_selector('.js-view-all', visible: true) end it 'links to the activity tab' do @@ -100,11 +100,11 @@ def push_code_contribution end it 'shows a link to the project list' do - expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true) + expect(find('#js-legacy-tabs-container .projects-block')).to have_selector('.js-view-all', visible: true) end it 'shows projects in "card mode"' do - page.within('#js-overview .projects-block') do + page.within('#js-legacy-tabs-container .projects-block') do expect(find('.js-projects-list-holder')).to have_css('.gl-card') end end @@ -126,7 +126,7 @@ def push_code_contribution end it 'shows a link to the project list' do - expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true) + expect(find('#js-legacy-tabs-container .projects-block')).to have_selector('.js-view-all', visible: true) end it 'does not show pagination' do @@ -145,7 +145,7 @@ def push_code_contribution end it 'shows an empty followers list with an info message' do - page.within('#followers') do + page.within('#js-legacy-tabs-container') do expect(page).to have_content('You do not have any followers') expect(page).not_to have_selector('.gl-card.gl-mb-5') expect(page).not_to have_selector('.gl-pagination') @@ -163,7 +163,7 @@ def push_code_contribution end it 'shows followers' do - page.within('#followers') do + page.within('#js-legacy-tabs-container') do expect(page).to have_content(follower.name) expect(page).to have_selector('.gl-card.gl-mb-5') expect(page).not_to have_selector('.gl-pagination') @@ -184,7 +184,7 @@ def push_code_contribution end it 'shows paginated followers' do - page.within('#followers') do + page.within('#js-legacy-tabs-container') do other_users.each_with_index do |follower, i| break if i == 20 @@ -206,7 +206,7 @@ def push_code_contribution end it 'shows an empty following list with an info message' do - page.within('#following') do + page.within('#js-legacy-tabs-container') do expect(page).to have_content('You are not following other users') expect(page).not_to have_selector('.gl-card.gl-mb-5') expect(page).not_to have_selector('.gl-pagination') @@ -224,7 +224,7 @@ def push_code_contribution end it 'shows following user' do - page.within('#following') do + page.within('#js-legacy-tabs-container') do expect(page).to have_content(followee.name) expect(page).to have_selector('.gl-card.gl-mb-5') expect(page).not_to have_selector('.gl-pagination') @@ -245,7 +245,7 @@ def push_code_contribution end it 'shows paginated following' do - page.within('#following') do + page.within('#js-legacy-tabs-container') do other_users.each_with_index do |followee, i| break if i == 20 diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index f62a1dd67ff4e..5253034d8ca24 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -28,42 +28,6 @@ end context 'with public profile' do - context 'with `profile_tabs_vue` feature flag disabled' do - before do - stub_feature_flags(profile_tabs_vue: false) - end - - it 'shows all the tabs' do - subject - - page.within '.nav-links' do - expect(page).to have_link('Overview') - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') - expect(page).to have_link('Followers') - expect(page).to have_link('Following') - end - end - end - - it 'shows all the tabs', :js do - subject - - page.within '[role="tablist"]' do - expect(page).to have_link('Overview') - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') - expect(page).to have_link('Followers') - expect(page).to have_link('Following') - end - end - it 'does not show private profile message' do subject @@ -225,11 +189,11 @@ context 'with private profile' do let_it_be(:user) { create(:user, private_profile: true) } - it 'shows no tab' do + it 'shows no page content container' do subject expect(page).to have_css("div.profile-header") - expect(page).not_to have_css("ul.nav-links") + expect(page).not_to have_css("#js-legacy-tabs-container") end it 'shows private profile message' do @@ -237,44 +201,6 @@ expect(page).to have_content("This user has a private profile") end - - context 'with `profile_tabs_vue` feature flag disabled' do - before do - stub_feature_flags(profile_tabs_vue: false) - end - - it 'shows own tabs' do - sign_in(user) - subject - - page.within '.nav-links' do - expect(page).to have_link('Overview') - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') - expect(page).to have_link('Followers') - expect(page).to have_link('Following') - end - end - end - - it 'shows own tabs', :js do - sign_in(user) - subject - - page.within '[role="tablist"]' do - expect(page).to have_link('Overview') - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') - expect(page).to have_link('Followers') - expect(page).to have_link('Following') - end - end end context 'with blocked profile' do @@ -295,9 +221,9 @@ visit_profile end - it 'shows no tab' do + it 'shows no content container' do expect(page).not_to have_css("div.profile-header") - expect(page).not_to have_css("ul.nav-links") + expect(page).not_to have_css("#js-legacy-tabs-container") end it 'shows no sidebar' do @@ -350,9 +276,9 @@ expect(page).to have_css('[data-testid="user-profile-header"]', text: 'Unconfirmed user') end - it 'shows no tab' do + it 'shows no content container' do expect(page).to have_css('[data-testid="user-profile-header"]') - expect(page).not_to have_css("ul.nav-links") + expect(page).not_to have_css("#js-legacy-tabs-container") end it 'shows no additional fields' do @@ -460,29 +386,6 @@ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet' end - context 'with a bot user' do - let_it_be(:user) { create(:user, user_type: :security_bot) } - - before do - stub_feature_flags(profile_tabs_vue: false) - end - - it 'only shows Overview and Activity tabs' do - subject - - page.within '.nav-links' do - expect(page).to have_link('Overview') - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') - expect(page).to have_link('Followers') - expect(page).to have_link('Following') - end - end - end - context 'structured markup' do let_it_be(:user) { create(:user, website_url: 'https://gitlab.com', organization: 'GitLab', job_title: 'Frontend Engineer', email: 'public@example.com', public_email: 'public@example.com', location: 'Country', created_at: Time.zone.now, updated_at: Time.zone.now) } diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb index addf9bbdcb1dd..46dee648f9ce8 100644 --- a/spec/features/users/user_browses_projects_on_user_page_spec.rb +++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb @@ -77,7 +77,7 @@ def click_nav_link(name) visit user_path(user) click_nav_link('Personal projects') - expect(page).to have_css('.tab-content #projects.active') + expect(page).to have_css('.user-profile-content #js-legacy-tabs-container') expect(title).to start_with(user.name) expect(page).to have_content(public_project.name) @@ -142,7 +142,7 @@ def click_nav_link(name) click_nav_link('Contributed projects') - page.within '#contributed' do + page.within '#js-legacy-tabs-container' do expect(page).to have_content(contributed_project.name) end end diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index 36c140464fb45..fc5a912f4334e 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -66,27 +66,38 @@ def filter_ee_badges(badges) end end - describe '#profile_tabs' do - subject(:tabs) { helper.profile_tabs } + describe '#profile_actions' do + subject(:profile_actions) { helper.profile_actions(other_user) } + + let_it_be(:other_user) { create(:user) } before do allow(helper).to receive(:current_user).and_return(user) - allow(helper).to receive(:can?).and_return(true) + allow(user).to receive(:bot?).and_return(false) + allow(helper).to receive(:can?).with(user, :read_user_profile, other_user).and_return(true) end context 'with public profile' do - it 'includes all the expected tabs' do - expect(tabs).to include(:activity, :groups, :contributed, :projects, :starred, :snippets) + it 'contains all profile actions' do + expect(profile_actions).to match_array [:overview, :activity, :groups, :contributed, :projects, :starred, :snippets, :followers, :following] end end context 'with private profile' do before do - allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false) + allow(helper).to receive(:can?).with(user, :read_user_profile, other_user).and_return(false) end it 'is empty' do - expect(tabs).to be_empty + expect(profile_actions).to match_array [] + end + end + + context 'with a public bot user' do + let_it_be(:other_user) { create(:user, :bot) } + + it 'contains bot profile actions' do + expect(profile_actions).to match_array [:overview, :activity] end end end -- GitLab