summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js26
-rw-r--r--app/assets/javascripts/pages/users/user_overview_block.js42
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js84
-rw-r--r--app/assets/stylesheets/framework/calendar.scss12
-rw-r--r--app/controllers/users_controller.rb13
-rw-r--r--app/finders/user_recent_events_finder.rb2
-rw-r--r--app/helpers/users_helper.rb2
-rw-r--r--app/views/users/_overview.html.haml32
-rw-r--r--app/views/users/show.html.haml38
-rw-r--r--changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml5
-rw-r--r--config/routes/user.rb1
-rw-r--r--locale/gitlab.pot54
-rw-r--r--spec/features/calendar_spec.rb21
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/users/overview_spec.rb123
-rw-r--r--spec/features/users/show_spec.rb2
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')