diff options
-rw-r--r-- | app/assets/javascripts/pages/users/activity_calendar.js | 26 | ||||
-rw-r--r-- | app/assets/javascripts/pages/users/user_overview_block.js | 42 | ||||
-rw-r--r-- | app/assets/javascripts/pages/users/user_tabs.js | 84 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/calendar.scss | 12 | ||||
-rw-r--r-- | app/controllers/users_controller.rb | 13 | ||||
-rw-r--r-- | app/finders/user_recent_events_finder.rb | 2 | ||||
-rw-r--r-- | app/helpers/users_helper.rb | 2 | ||||
-rw-r--r-- | app/views/users/_overview.html.haml | 32 | ||||
-rw-r--r-- | app/views/users/show.html.haml | 38 | ||||
-rw-r--r-- | changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml | 5 | ||||
-rw-r--r-- | config/routes/user.rb | 1 | ||||
-rw-r--r-- | locale/gitlab.pot | 54 | ||||
-rw-r--r-- | spec/features/calendar_spec.rb | 21 | ||||
-rw-r--r-- | spec/features/dashboard/datetime_on_tooltips_spec.rb | 2 | ||||
-rw-r--r-- | spec/features/users/overview_spec.rb | 123 | ||||
-rw-r--r-- | spec/features/users/show_spec.rb | 2 |
16 files changed, 398 insertions, 61 deletions
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index 9892a039941..bf592ba7a3c 100644 --- a/app/assets/javascripts/pages/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -43,7 +43,15 @@ const initColorKey = () => .domain([0, 3]); export default class ActivityCalendar { - constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0, firstDayOfWeek = 0) { + constructor( + container, + activitiesContainer, + timestamps, + calendarActivitiesPath, + utcOffset = 0, + firstDayOfWeek = 0, + monthsAgo = 12, + ) { this.calendarActivitiesPath = calendarActivitiesPath; this.clickDay = this.clickDay.bind(this); this.currentSelectedDate = ''; @@ -66,6 +74,8 @@ export default class ActivityCalendar { ]; this.months = []; this.firstDayOfWeek = firstDayOfWeek; + this.activitiesContainer = activitiesContainer; + this.container = container; // Loop through the timestamps to create a group of objects // The group of objects will be grouped based on the day of the week they are @@ -75,13 +85,13 @@ export default class ActivityCalendar { const today = getSystemDate(utcOffset); today.setHours(0, 0, 0, 0, 0); - const oneYearAgo = new Date(today); - oneYearAgo.setFullYear(today.getFullYear() - 1); + const timeAgo = new Date(today); + timeAgo.setMonth(today.getMonth() - monthsAgo); - const days = getDayDifference(oneYearAgo, today); + const days = getDayDifference(timeAgo, today); for (let i = 0; i <= days; i += 1) { - const date = new Date(oneYearAgo); + const date = new Date(timeAgo); date.setDate(date.getDate() + i); const day = date.getDay(); @@ -280,7 +290,7 @@ export default class ActivityCalendar { this.currentSelectedDate.getDate(), ].join('-'); - $('.user-calendar-activities').html(LOADING_HTML); + $(this.activitiesContainer).html(LOADING_HTML); axios .get(this.calendarActivitiesPath, { @@ -289,11 +299,11 @@ export default class ActivityCalendar { }, responseType: 'text', }) - .then(({ data }) => $('.user-calendar-activities').html(data)) + .then(({ data }) => $(this.activitiesContainer).html(data)) .catch(() => flash(__('An error occurred while retrieving calendar activity'))); } else { this.currentSelectedDate = ''; - $('.user-calendar-activities').html(''); + $(this.activitiesContainer).html(''); } } } diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js new file mode 100644 index 00000000000..0009419cd0c --- /dev/null +++ b/app/assets/javascripts/pages/users/user_overview_block.js @@ -0,0 +1,42 @@ +import axios from '~/lib/utils/axios_utils'; + +export default class UserOverviewBlock { + constructor(options = {}) { + this.container = options.container; + this.url = options.url; + this.limit = options.limit || 20; + this.loadData(); + } + + loadData() { + const loadingEl = document.querySelector(`${this.container} .loading`); + + loadingEl.classList.remove('hide'); + + axios + .get(this.url, { + params: { + limit: this.limit, + }, + }) + .then(({ data }) => this.render(data)) + .catch(() => loadingEl.classList.add('hide')); + } + + render(data) { + const { html, count } = data; + const contentList = document.querySelector(`${this.container} .overview-content-list`); + + contentList.innerHTML += html; + + const loadingEl = document.querySelector(`${this.container} .loading`); + + if (count && count > 0) { + document.querySelector(`${this.container} .js-view-all`).classList.remove('hide'); + } else { + document.querySelector(`${this.container} .nothing-here-block`).classList.add('text-left', 'p-0'); + } + + loadingEl.classList.add('hide'); + } +} diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index a2ca03536f2..23b0348a99f 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -2,9 +2,10 @@ import $ from 'jquery'; import axios from '~/lib/utils/axios_utils'; import Activities from '~/activities'; import { localTimeAgo } from '~/lib/utils/datetime_utility'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import flash from '~/flash'; import ActivityCalendar from './activity_calendar'; +import UserOverviewBlock from './user_overview_block'; /** * UserTabs @@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar'; * </div> */ -const CALENDAR_TEMPLATE = ` - <div class="clearfix calendar"> - <div class="js-contrib-calendar"></div> - <div class="calendar-hint"> - Summary of issues, merge requests, push events, and comments +const CALENDAR_TEMPLATES = { + activity: ` + <div class="clearfix calendar"> + <div class="js-contrib-calendar"></div> + <div class="calendar-hint bottom-right"></div> </div> - </div> -`; + `, + overview: ` + <div class="clearfix calendar"> + <div class="calendar-hint"></div> + <div class="js-contrib-calendar prepend-top-20"></div> + </div> + `, +}; + +const CALENDAR_PERIOD_6_MONTHS = 6; +const CALENDAR_PERIOD_12_MONTHS = 12; export default class UserTabs { constructor({ defaultAction, action, parentEl }) { this.loaded = {}; - this.defaultAction = defaultAction || 'activity'; + this.defaultAction = defaultAction || 'overview'; this.action = action || this.defaultAction; this.$parentEl = $(parentEl) || $(document); this.windowLocation = window.location; @@ -124,6 +134,8 @@ export default class UserTabs { } if (action === 'activity') { this.loadActivities(); + } else if (action === 'overview') { + this.loadOverviewTab(); } const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; @@ -154,7 +166,40 @@ export default class UserTabs { if (this.loaded.activity) { return; } - const $calendarWrap = this.$parentEl.find('.user-calendar'); + + this.loadActivityCalendar('activity'); + + // eslint-disable-next-line no-new + new Activities(); + + this.loaded.activity = true; + } + + loadOverviewTab() { + if (this.loaded.overview) { + return; + } + + this.loadActivityCalendar('overview'); + + UserTabs.renderMostRecentBlocks('#js-overview .activities-block', 5); + UserTabs.renderMostRecentBlocks('#js-overview .projects-block', 10); + + this.loaded.overview = true; + } + + static renderMostRecentBlocks(container, limit) { + // eslint-disable-next-line no-new + new UserOverviewBlock({ + container, + url: $(`${container} .overview-content-list`).data('href'), + limit, + }); + } + + loadActivityCalendar(action) { + const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS; + const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar'); const calendarPath = $calendarWrap.data('calendarPath'); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const utcOffset = $calendarWrap.data('utcOffset'); @@ -166,17 +211,22 @@ export default class UserTabs { axios .get(calendarPath) .then(({ data }) => { - $calendarWrap.html(CALENDAR_TEMPLATE); - $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`); + $calendarWrap.html(CALENDAR_TEMPLATES[action]); + + let calendarHint = ''; + + if (action === 'activity') { + calendarHint = sprintf(__('Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})'), { utcFormatted }); + } else if (action === 'overview') { + calendarHint = __('Issues, merge requests, pushes and comments.'); + } + + $calendarWrap.find('.calendar-hint').text(calendarHint); // eslint-disable-next-line no-new - new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset); + new ActivityCalendar('.tab-pane.active .js-contrib-calendar', '.tab-pane.active .user-calendar-activities', data, calendarActivitiesPath, utcOffset, 0, monthsAgo); }) .catch(() => flash(__('There was an error loading users activity calendar.'))); - - // eslint-disable-next-line no-new - new Activities(); - this.loaded.activity = true; } toggleLoading(status) { diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 0b9dff64b0b..9638fee6078 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -1,8 +1,7 @@ -.calender-block { +.calendar-block { padding-left: 0; padding-right: 0; border-top: 0; - direction: rtl; @media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) { overflow-x: auto; @@ -42,10 +41,13 @@ } .calendar-hint { - margin-top: -23px; - float: right; font-size: 12px; - direction: ltr; + + &.bottom-right { + direction: ltr; + margin-top: -23px; + float: right; + } } .pika-single.gitlab-theme { diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e509098d778..d16240af404 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -29,11 +29,17 @@ class UsersController < ApplicationController format.json do load_events - pager_json("events/_events", @events.count) + pager_json("events/_events", @events.count, events: @events) end end end + def activity + respond_to do |format| + format.html { render 'show' } + end + end + def groups load_groups @@ -53,9 +59,7 @@ class UsersController < ApplicationController respond_to do |format| format.html { render 'show' } format.json do - render json: { - html: view_to_html_string("shared/projects/_list", projects: @projects) - } + pager_json("shared/projects/_list", @projects.count, projects: @projects) end end end @@ -125,6 +129,7 @@ class UsersController < ApplicationController @projects = PersonalProjectsFinder.new(user).execute(current_user) .page(params[:page]) + .per(params[:limit]) prepare_projects_for_rendering(@projects) end diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb index eeca5026da1..3f2e813d381 100644 --- a/app/finders/user_recent_events_finder.rb +++ b/app/finders/user_recent_events_finder.rb @@ -31,7 +31,7 @@ class UserRecentEventsFinder recent_events(params[:offset] || 0) .joins(:project) .with_associations - .limit_recent(LIMIT, params[:offset]) + .limit_recent(params[:limit].presence || LIMIT, params[:offset]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index bcd91f619c8..42b533ad772 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -76,7 +76,7 @@ module UsersHelper tabs = [] if can?(current_user, :read_user_profile, @user) - tabs += [:activity, :groups, :contributed, :projects, :snippets] + tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets] end tabs diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml new file mode 100644 index 00000000000..f8b3754840d --- /dev/null +++ b/app/views/users/_overview.html.haml @@ -0,0 +1,32 @@ +.row + .col-md-12.col-lg-6 + .calendar-block + .content-block.hide-bottom-border + %h4 + = s_('UserProfile|Activity') + .user-calendar.d-none.d-sm-block.text-left{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } } + %h4.center.light + %i.fa.fa-spinner.fa-spin + .user-calendar-activities.d-none.d-sm-block + + - if can?(current_user, :read_cross_project) + .activities-block + .content-block + %h5.prepend-top-10 + = s_('UserProfile|Recent contributions') + .overview-content-list{ data: { href: user_path } } + .center.light.loading + %i.fa.fa-spinner.fa-spin + .prepend-top-10 + = link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all" + + .col-md-12.col-lg-6 + .projects-block + .content-block + %h4 + = s_('UserProfile|Personal projects') + .overview-content-list{ data: { href: user_projects_path } } + .center.light.loading + %i.fa.fa-spinner.fa-spin + .prepend-top-10 + = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all" diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 7a38d290915..d6c8420b744 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -12,22 +12,22 @@ .cover-block.user-cover-block.top-area .cover-controls - if @user == current_user - = link_to profile_path, class: 'btn btn-default has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do + = link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do = icon('pencil') - elsif current_user - if @user.abuse_report - %button.btn.btn-danger{ title: 'Already reported for abuse', + %button.btn.btn-danger{ title: s_('UserProfile|Already reported for abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } } = icon('exclamation-circle') - else = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn', - title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do + title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('exclamation-circle') - if can?(current_user, :read_user_profile, @user) - = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do + = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: s_('UserProfile|Subscribe'), 'aria-label': 'Subscribe' do = icon('rss') - if current_user && current_user.admin? - = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area', + = link_to [:admin, @user], class: 'btn btn-default', title: s_('UserProfile|View user in admin area'), data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('users') @@ -51,7 +51,7 @@ @#{@user.username} - if can?(current_user, :read_user_profile, @user) %span.middle-dot-divider - Member since #{@user.created_at.to_date.to_s(:long)} + = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) } .cover-desc - unless @user.public_email.blank? @@ -91,32 +91,40 @@ .fade-left= icon('angle-left') .fade-right= icon('angle-right') %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs + - 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_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do - Activity + = 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 - Groups + = 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 - Contributed projects + = 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 - Personal projects + = s_('UserProfile|Personal 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 - Snippets + = s_('UserProfile|Snippets') %div{ class: container_class } .tab-content + - if profile_tab?(:overview) + #js-overview.tab-pane + = render "users/overview" + - if profile_tab?(:activity) #activity.tab-pane - .row-content-block.calender-block.white.second-block.d-none.d-sm-block + .row-content-block.calendar-block.white.second-block.d-none.d-sm-block .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } } %h4.center.light %i.fa.fa-spinner.fa-spin @@ -124,7 +132,7 @@ - if can?(current_user, :read_cross_project) %h4.prepend-top-20 - Most Recent Activity + = s_('UserProfile|Most Recent Activity') .content_list{ data: { href: user_path } } = spinner @@ -155,4 +163,4 @@ .col-12.text-center .text-content %h4 - This user has a private profile + = s_('UserProfile|This user has a private profile') diff --git a/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml b/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml new file mode 100644 index 00000000000..5e2be42c8b7 --- /dev/null +++ b/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml @@ -0,0 +1,5 @@ +--- +title: Adds new 'Overview' tab on user profile page +merge_request: 21663 +author: +type: other diff --git a/config/routes/user.rb b/config/routes/user.rb index bc7df5e7584..e0ae264e2c0 100644 --- a/config/routes/user.rb +++ b/config/routes/user.rb @@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d get :contributed, as: :contributed_projects get :snippets get :exists + get :activity get '/', to: redirect('%{username}'), as: nil end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5591b0b8bb2..be002d84f0d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3345,6 +3345,9 @@ msgstr "" msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable." msgstr "" +msgid "Issues, merge requests, pushes and comments." +msgstr "" + msgid "Jan" msgstr "" @@ -3709,6 +3712,9 @@ msgstr "" msgid "Median" msgstr "" +msgid "Member since %{date}" +msgstr "" + msgid "Members" msgstr "" @@ -5753,6 +5759,9 @@ msgstr "" msgid "Subscribe at project level" msgstr "" +msgid "Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})" +msgstr "" + msgid "Switch branch/tag" msgstr "" @@ -6581,6 +6590,51 @@ msgstr "" msgid "User map" msgstr "" +msgid "UserProfile|Activity" +msgstr "" + +msgid "UserProfile|Already reported for abuse" +msgstr "" + +msgid "UserProfile|Contributed projects" +msgstr "" + +msgid "UserProfile|Edit profile" +msgstr "" + +msgid "UserProfile|Groups" +msgstr "" + +msgid "UserProfile|Most Recent Activity" +msgstr "" + +msgid "UserProfile|Overview" +msgstr "" + +msgid "UserProfile|Personal projects" +msgstr "" + +msgid "UserProfile|Recent contributions" +msgstr "" + +msgid "UserProfile|Report abuse" +msgstr "" + +msgid "UserProfile|Snippets" +msgstr "" + +msgid "UserProfile|Subscribe" +msgstr "" + +msgid "UserProfile|This user has a private profile" +msgstr "" + +msgid "UserProfile|View all" +msgstr "" + +msgid "UserProfile|View user in admin area" +msgstr "" + msgid "Users" msgstr "" diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index f08946b0593..aa3ca8923ff 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do end def selected_day_activities(visible: true) - find('.user-calendar-activities', visible: visible).text + find('.tab-pane#activity .user-calendar-activities', visible: visible).text end before do @@ -74,15 +74,16 @@ describe 'Contributions Calendar', :js do describe 'calendar day selection' do before do visit user.username + page.find('.js-activity-tab a').click wait_for_requests end it 'displays calendar' do - expect(page).to have_css('.js-contrib-calendar') + expect(find('.tab-pane#activity')).to have_css('.js-contrib-calendar') end describe 'select calendar day' do - let(:cells) { page.all('.user-contrib-cell') } + let(:cells) { page.all('.tab-pane#activity .user-contrib-cell') } before do cells[0].click @@ -108,6 +109,7 @@ describe 'Contributions Calendar', :js do describe 'deselect calendar day' do before do cells[0].click + page.find('.js-activity-tab a').click wait_for_requests end @@ -122,6 +124,7 @@ describe 'Contributions Calendar', :js do shared_context 'visit user page' do before do visit user.username + page.find('.js-activity-tab a').click wait_for_requests end end @@ -130,12 +133,12 @@ describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity square color for 1 contribution' do - expect(page).to have_selector(get_cell_color_selector(contribution_count), count: 1) + expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(contribution_count), count: 1) end it 'displays calendar activity square on the correct date' do today = Date.today.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) + expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) end end @@ -150,7 +153,7 @@ describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity log' do - expect(find('.content_list .event-note')).to have_content issue_title + expect(find('.tab-pane#activity .content_list .event-note')).to have_content issue_title end end end @@ -182,17 +185,17 @@ describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity squares for both days' do - expect(page).to have_selector(get_cell_color_selector(1), count: 2) + expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(1), count: 2) end it 'displays calendar activity square for yesterday' do yesterday = Date.yesterday.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(1, yesterday), count: 1) + expect(find('.tab-pane#activity')).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(page).to have_selector(get_cell_date_selector(1, today), count: 1) + expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(1, today), count: 1) end end end diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb index d7234158fa1..0db8093411b 100644 --- a/spec/features/dashboard/datetime_on_tooltips_spec.rb +++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb @@ -14,7 +14,7 @@ describe 'Tooltips on .timeago dates', :js do updated_at: created_date, created_at: created_date) sign_in user - visit user_path(user) + visit user_activity_path(user) wait_for_requests() page.find('.js-timeago').hover diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb new file mode 100644 index 00000000000..11f357cbaa5 --- /dev/null +++ b/spec/features/users/overview_spec.rb @@ -0,0 +1,123 @@ +require 'spec_helper' + +describe 'Overview tab on a user profile', :js do + let(:user) { create(:user) } + let(:contributed_project) { create(:project, :public, :repository) } + + def push_code_contribution + event = create(:push_event, project: contributed_project, author: user) + + create(:push_event_payload, + event: event, + commit_from: '11f9ac0a48b62cef25eedede4c1819964f08d5ce', + commit_to: '1cf19a015df3523caf0a1f9d40c98a267d6a2fc2', + commit_count: 3, + ref: 'master') + end + + before do + sign_in user + end + + describe 'activities section' do + shared_context 'visit overview tab' do + before do + visit user.username + page.find('.js-overview-tab a').click + wait_for_requests + end + end + + describe 'user has no activities' do + include_context 'visit overview tab' + + it 'does not show any entries in the list of activities' do + page.within('.activities-block') do + expect(page).not_to have_selector('.event-item') + end + 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) + end + end + + describe 'user has 3 activities' do + before do + 3.times { push_code_contribution } + end + + 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) + end + end + + describe 'user has 10 activities' do + before do + 10.times { push_code_contribution } + end + + include_context 'visit overview tab' + + it 'displays 5 entries in the list of activities' do + expect(find('#js-overview')).to have_selector('.event-item', count: 5) + end + + it 'shows a link to the activity list' do + expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: true) + end + + it 'links to the activity tab' do + page.within('.activities-block') do + find('.js-view-all').click + wait_for_requests + expect(URI.parse(current_url).path).to eq("/users/#{user.username}/activity") + end + end + end + end + + describe 'projects section' do + shared_context 'visit overview tab' do + before do + visit user.username + page.find('.js-overview-tab a').click + wait_for_requests + end + end + + describe 'user has no personal projects' do + include_context 'visit overview tab' + + it 'it shows an empty project list with an info message' do + page.within('.projects-block') do + expect(page).to have_content('No projects found') + expect(page).not_to have_selector('.project-row') + end + end + + it 'does not show a link to the project list' do + expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: false) + end + end + + describe 'user has a personal project' do + let(:private_project) { create(:project, :private, namespace: user.namespace, creator: user) { |p| p.add_maintainer(user) } } + let!(:private_event) { create(:event, project: private_project, author: user) } + + include_context 'visit overview tab' + + it 'it shows one entry in the list of projects' do + page.within('.projects-block') do + expect(page).to have_selector('.project-row', count: 1) + end + end + + it 'shows a link to the project list' do + expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true) + end + end + end +end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index bc07ab48c39..86379164cf0 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -8,6 +8,7 @@ describe 'User page' do visit(user_path(user)) 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') @@ -44,6 +45,7 @@ describe 'User page' do visit(user_path(user)) 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') |