Commit 8614b436 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'ee-49801-add-new-overview-tab-on-user-profile-page' into 'master'

(EE Port) Adds overview tab to user profile page

See merge request gitlab-org/gitlab-ee!7783
parents ae3fe99b fa0ebee2
...@@ -43,7 +43,15 @@ const initColorKey = () => ...@@ -43,7 +43,15 @@ const initColorKey = () =>
.domain([0, 3]); .domain([0, 3]);
export default class ActivityCalendar { 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.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this); this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = ''; this.currentSelectedDate = '';
...@@ -66,6 +74,8 @@ export default class ActivityCalendar { ...@@ -66,6 +74,8 @@ export default class ActivityCalendar {
]; ];
this.months = []; this.months = [];
this.firstDayOfWeek = firstDayOfWeek; this.firstDayOfWeek = firstDayOfWeek;
this.activitiesContainer = activitiesContainer;
this.container = container;
// Loop through the timestamps to create a group of objects // 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 // The group of objects will be grouped based on the day of the week they are
...@@ -75,13 +85,13 @@ export default class ActivityCalendar { ...@@ -75,13 +85,13 @@ export default class ActivityCalendar {
const today = getSystemDate(utcOffset); const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0); today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today); const timeAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1); timeAgo.setMonth(today.getMonth() - monthsAgo);
const days = getDayDifference(oneYearAgo, today); const days = getDayDifference(timeAgo, today);
for (let i = 0; i <= days; i += 1) { for (let i = 0; i <= days; i += 1) {
const date = new Date(oneYearAgo); const date = new Date(timeAgo);
date.setDate(date.getDate() + i); date.setDate(date.getDate() + i);
const day = date.getDay(); const day = date.getDay();
...@@ -280,7 +290,7 @@ export default class ActivityCalendar { ...@@ -280,7 +290,7 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(), this.currentSelectedDate.getDate(),
].join('-'); ].join('-');
$('.user-calendar-activities').html(LOADING_HTML); $(this.activitiesContainer).html(LOADING_HTML);
axios axios
.get(this.calendarActivitiesPath, { .get(this.calendarActivitiesPath, {
...@@ -289,11 +299,11 @@ export default class ActivityCalendar { ...@@ -289,11 +299,11 @@ export default class ActivityCalendar {
}, },
responseType: 'text', responseType: 'text',
}) })
.then(({ data }) => $('.user-calendar-activities').html(data)) .then(({ data }) => $(this.activitiesContainer).html(data))
.catch(() => flash(__('An error occurred while retrieving calendar activity'))); .catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else { } else {
this.currentSelectedDate = ''; this.currentSelectedDate = '';
$('.user-calendar-activities').html(''); $(this.activitiesContainer).html('');
} }
} }
} }
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');
}
}
...@@ -2,9 +2,10 @@ import $ from 'jquery'; ...@@ -2,9 +2,10 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Activities from '~/activities'; import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility'; import { localTimeAgo } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale'; import { __, sprintf } from '~/locale';
import flash from '~/flash'; import flash from '~/flash';
import ActivityCalendar from './activity_calendar'; import ActivityCalendar from './activity_calendar';
import UserOverviewBlock from './user_overview_block';
/** /**
* UserTabs * UserTabs
...@@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar'; ...@@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar';
* </div> * </div>
*/ */
const CALENDAR_TEMPLATE = ` const CALENDAR_TEMPLATES = {
<div class="clearfix calendar"> activity: `
<div class="js-contrib-calendar"></div> <div class="clearfix calendar">
<div class="calendar-hint"> <div class="js-contrib-calendar"></div>
Summary of issues, merge requests, push events, and comments <div class="calendar-hint bottom-right"></div>
</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 { export default class UserTabs {
constructor({ defaultAction, action, parentEl }) { constructor({ defaultAction, action, parentEl }) {
this.loaded = {}; this.loaded = {};
this.defaultAction = defaultAction || 'activity'; this.defaultAction = defaultAction || 'overview';
this.action = action || this.defaultAction; this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document); this.$parentEl = $(parentEl) || $(document);
this.windowLocation = window.location; this.windowLocation = window.location;
...@@ -124,6 +134,8 @@ export default class UserTabs { ...@@ -124,6 +134,8 @@ export default class UserTabs {
} }
if (action === 'activity') { if (action === 'activity') {
this.loadActivities(); this.loadActivities();
} else if (action === 'overview') {
this.loadOverviewTab();
} }
const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
...@@ -154,7 +166,40 @@ export default class UserTabs { ...@@ -154,7 +166,40 @@ export default class UserTabs {
if (this.loaded.activity) { if (this.loaded.activity) {
return; 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 calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset'); const utcOffset = $calendarWrap.data('utcOffset');
...@@ -166,17 +211,22 @@ export default class UserTabs { ...@@ -166,17 +211,22 @@ export default class UserTabs {
axios axios
.get(calendarPath) .get(calendarPath)
.then(({ data }) => { .then(({ data }) => {
$calendarWrap.html(CALENDAR_TEMPLATE); $calendarWrap.html(CALENDAR_TEMPLATES[action]);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
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 // 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.'))); .catch(() => flash(__('There was an error loading users activity calendar.')));
// eslint-disable-next-line no-new
new Activities();
this.loaded.activity = true;
} }
toggleLoading(status) { toggleLoading(status) {
......
.calender-block { .calendar-block {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
border-top: 0; border-top: 0;
direction: rtl;
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) { @media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
overflow-x: auto; overflow-x: auto;
...@@ -42,10 +41,13 @@ ...@@ -42,10 +41,13 @@
} }
.calendar-hint { .calendar-hint {
margin-top: -23px;
float: right;
font-size: 12px; font-size: 12px;
direction: ltr;
&.bottom-right {
direction: ltr;
margin-top: -23px;
float: right;
}
} }
.pika-single.gitlab-theme { .pika-single.gitlab-theme {
......
...@@ -30,11 +30,17 @@ class UsersController < ApplicationController ...@@ -30,11 +30,17 @@ class UsersController < ApplicationController
format.json do format.json do
load_events load_events
pager_json("events/_events", @events.count) pager_json("events/_events", @events.count, events: @events)
end end
end end
end end
def activity
respond_to do |format|
format.html { render 'show' }
end
end
def groups def groups
load_groups load_groups
...@@ -54,9 +60,7 @@ class UsersController < ApplicationController ...@@ -54,9 +60,7 @@ class UsersController < ApplicationController
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html { render 'show' }
format.json do format.json do
render json: { pager_json("shared/projects/_list", @projects.count, projects: @projects)
html: view_to_html_string("shared/projects/_list", projects: @projects)
}
end end
end end
end end
...@@ -126,6 +130,7 @@ class UsersController < ApplicationController ...@@ -126,6 +130,7 @@ class UsersController < ApplicationController
@projects = @projects =
PersonalProjectsFinder.new(user).execute(current_user) PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page]) .page(params[:page])
.per(params[:limit])
prepare_projects_for_rendering(@projects) prepare_projects_for_rendering(@projects)
end end
......
...@@ -31,7 +31,7 @@ class UserRecentEventsFinder ...@@ -31,7 +31,7 @@ class UserRecentEventsFinder
recent_events(params[:offset] || 0) recent_events(params[:offset] || 0)
.joins(:project) .joins(:project)
.with_associations .with_associations
.limit_recent(LIMIT, params[:offset]) .limit_recent(params[:limit].presence || LIMIT, params[:offset])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -76,7 +76,7 @@ module UsersHelper ...@@ -76,7 +76,7 @@ module UsersHelper
tabs = [] tabs = []
if can?(current_user, :read_user_profile, @user) if can?(current_user, :read_user_profile, @user)
tabs += [:activity, :groups, :contributed, :projects, :snippets] tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets]
end end
tabs tabs
......
.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"
...@@ -12,22 +12,22 @@ ...@@ -12,22 +12,22 @@
.cover-block.user-cover-block.top-area .cover-block.user-cover-block.top-area
.cover-controls .cover-controls
- if @user == current_user - 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') = icon('pencil')
- elsif current_user - elsif current_user
- if @user.abuse_report - 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' } } data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }
= icon('exclamation-circle') = icon('exclamation-circle')
- else - else
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn', = 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') = icon('exclamation-circle')
- if can?(current_user, :read_user_profile, @user) - 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') = icon('rss')
- if current_user && current_user.admin? - 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 data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('users') = icon('users')
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
@#{@user.username} @#{@user.username}
- if can?(current_user, :read_user_profile, @user) - if can?(current_user, :read_user_profile, @user)
%span.middle-dot-divider %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 .cover-desc
- unless @user.public_email.blank? - unless @user.public_email.blank?
...@@ -91,32 +91,40 @@ ...@@ -91,32 +91,40 @@
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
%ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs %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) - if profile_tab?(:activity)
%li.js-activity-tab %li.js-activity-tab
= link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do = link_to user_activity_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
Activity = s_('UserProfile|Activity')
- if profile_tab?(:groups) - if profile_tab?(:groups)
%li.js-groups-tab %li.js-groups-tab
= link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do = 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) - if profile_tab?(:contributed)
%li.js-contributed-tab %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 = 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) - if profile_tab?(:projects)
%li.js-projects-tab %li.js-projects-tab
= link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do = 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) - if profile_tab?(:snippets)
%li.js-snippets-tab %li.js-snippets-tab
= link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do = 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 } %div{ class: container_class }
.tab-content .tab-content
- if profile_tab?(:overview)
#js-overview.tab-pane
= render "users/overview"
- if profile_tab?(:activity) - if profile_tab?(:activity)
#activity.tab-pane #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 } } .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 %h4.center.light
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
...@@ -124,7 +132,7 @@ ...@@ -124,7 +132,7 @@
- if can?(current_user, :read_cross_project) - if can?(current_user, :read_cross_project)
%h4.prepend-top-20 %h4.prepend-top-20
Most Recent Activity = s_('UserProfile|Most Recent Activity')
.content_list{ data: { href: user_path } } .content_list{ data: { href: user_path } }
= spinner = spinner
...@@ -155,4 +163,4 @@ ...@@ -155,4 +163,4 @@
.col-12.text-center .col-12.text-center
.text-content .text-content
%h4 %h4
This user has a private profile = s_('UserProfile|This user has a private profile')
---
title: Adds new 'Overview' tab on user profile page
merge_request: 21663
author:
type: other
...@@ -62,6 +62,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d ...@@ -62,6 +62,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :snippets get :snippets
get :exists get :exists
get :pipelines_quota get :pipelines_quota
get :activity
get '/', to: redirect('%{username}'), as: nil get '/', to: redirect('%{username}'), as: nil
## EE-specific ## EE-specific
......
...@@ -4204,6 +4204,9 @@ msgstr "" ...@@ -4204,6 +4204,9 @@ msgstr ""
msgid "Issues closed" msgid "Issues closed"
msgstr "" msgstr ""
msgid "Issues, merge requests, pushes and comments."
msgstr ""
msgid "Jan" msgid "Jan"
msgstr "" msgstr ""
...@@ -4649,6 +4652,9 @@ msgstr "" ...@@ -4649,6 +4652,9 @@ msgstr ""
msgid "Median" msgid "Median"
msgstr "" msgstr ""
msgid "Member since %{date}"
msgstr ""
msgid "Members" msgid "Members"
msgstr "" msgstr ""
...@@ -7206,6 +7212,9 @@ msgstr "" ...@@ -7206,6 +7212,9 @@ msgstr ""
msgid "Subscribe at project level" msgid "Subscribe at project level"
msgstr "" msgstr ""
msgid "Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})"
msgstr ""
msgid "Switch branch/tag" msgid "Switch branch/tag"
msgstr "" msgstr ""
...@@ -8160,6 +8169,51 @@ msgstr "" ...@@ -8160,6 +8169,51 @@ msgstr ""
msgid "User map" msgid "User map"
msgstr "" 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" msgid "Users"
msgstr "" msgstr ""
......
...@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do ...@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do
end end
def selected_day_activities(visible: true) def selected_day_activities(visible: true)
find('.user-calendar-activities', visible: visible).text find('.tab-pane#activity .user-calendar-activities', visible: visible).text
end end
before do before do
...@@ -74,15 +74,16 @@ describe 'Contributions Calendar', :js do ...@@ -74,15 +74,16 @@ describe 'Contributions Calendar', :js do
describe 'calendar day selection' do describe 'calendar day selection' do
before do before do
visit user.username visit user.username
page.find('.js-activity-tab a').click
wait_for_requests wait_for_requests
end end
it 'displays calendar' do it 'displays calendar' do
expect(page).to have_css('.js-contrib-calendar') expect(find('.tab-pane#activity')).to have_css('.js-contrib-calendar')
end end
describe 'select calendar day' do describe 'select calendar day' do
let(:cells) { page.all('.user-contrib-cell') } let(:cells) { page.all('.tab-pane#activity .user-contrib-cell') }
before do before do
cells[0].click cells[0].click
...@@ -108,6 +109,7 @@ describe 'Contributions Calendar', :js do ...@@ -108,6 +109,7 @@ describe 'Contributions Calendar', :js do
describe 'deselect calendar day' do describe 'deselect calendar day' do
before do before do
cells[0].click cells[0].click
page.find('.js-activity-tab a').click
wait_for_requests wait_for_requests
end end
...@@ -122,6 +124,7 @@ describe 'Contributions Calendar', :js do ...@@ -122,6 +124,7 @@ describe 'Contributions Calendar', :js do
shared_context 'visit user page' do shared_context 'visit user page' do
before do before do
visit user.username visit user.username
page.find('.js-activity-tab a').click
wait_for_requests wait_for_requests
end end
end end
...@@ -130,12 +133,12 @@ describe 'Contributions Calendar', :js do ...@@ -130,12 +133,12 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page' include_context 'visit user page'
it 'displays calendar activity square color for 1 contribution' do 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 end
it 'displays calendar activity square on the correct date' do it 'displays calendar activity square on the correct date' do
today = Date.today.strftime(date_format) 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
end end
...@@ -150,7 +153,7 @@ describe 'Contributions Calendar', :js do ...@@ -150,7 +153,7 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page' include_context 'visit user page'
it 'displays calendar activity log' do 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 end
end end
...@@ -182,17 +185,17 @@ describe 'Contributions Calendar', :js do ...@@ -182,17 +185,17 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page' include_context 'visit user page'
it 'displays calendar activity squares for both days' do 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 end
it 'displays calendar activity square for yesterday' do it 'displays calendar activity square for yesterday' do
yesterday = Date.yesterday.strftime(date_format) 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 end
it 'displays calendar activity square for today' do it 'displays calendar activity square for today' do
today = Date.today.strftime(date_format) 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 end
end end
......
...@@ -14,7 +14,7 @@ describe 'Tooltips on .timeago dates', :js do ...@@ -14,7 +14,7 @@ describe 'Tooltips on .timeago dates', :js do
updated_at: created_date, created_at: created_date) updated_at: created_date, created_at: created_date)
sign_in user sign_in user
visit user_path(user) visit user_activity_path(user)
wait_for_requests() wait_for_requests()
page.find('.js-timeago').hover page.find('.js-timeago').hover
......
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
...@@ -8,6 +8,7 @@ describe 'User page' do ...@@ -8,6 +8,7 @@ describe 'User page' do
visit(user_path(user)) visit(user_path(user))
page.within '.nav-links' do page.within '.nav-links' do
expect(page).to have_link('Overview')
expect(page).to have_link('Activity') expect(page).to have_link('Activity')
expect(page).to have_link('Groups') expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects') expect(page).to have_link('Contributed projects')
...@@ -44,6 +45,7 @@ describe 'User page' do ...@@ -44,6 +45,7 @@ describe 'User page' do
visit(user_path(user)) visit(user_path(user))
page.within '.nav-links' do page.within '.nav-links' do
expect(page).to have_link('Overview')
expect(page).to have_link('Activity') expect(page).to have_link('Activity')
expect(page).to have_link('Groups') expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects') expect(page).to have_link('Contributed projects')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment