Commit 92c3078b authored by Phil Hughes's avatar Phil Hughes

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

Resolve "Add new "Overview" tab on user profile page"

Closes #49801

See merge request gitlab-org/gitlab-ce!21663
parents 14ed916c 337b2c80
...@@ -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 = {
activity: `
<div class="clearfix calendar"> <div class="clearfix calendar">
<div class="js-contrib-calendar"></div> <div class="js-contrib-calendar"></div>
<div class="calendar-hint"> <div class="calendar-hint bottom-right"></div>
Summary of issues, merge requests, push events, and comments
</div> </div>
`,
overview: `
<div class="clearfix calendar">
<div class="calendar-hint"></div>
<div class="js-contrib-calendar prepend-top-20"></div>
</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;
&.bottom-right {
direction: ltr; direction: ltr;
margin-top: -23px;
float: right;
}
} }
.pika-single.gitlab-theme { .pika-single.gitlab-theme {
......
...@@ -29,11 +29,17 @@ class UsersController < ApplicationController ...@@ -29,11 +29,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
...@@ -53,9 +59,7 @@ class UsersController < ApplicationController ...@@ -53,9 +59,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
...@@ -125,6 +129,7 @@ class UsersController < ApplicationController ...@@ -125,6 +129,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
...@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d ...@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :contributed, as: :contributed_projects get :contributed, as: :contributed_projects
get :snippets get :snippets
get :exists get :exists
get :activity
get '/', to: redirect('%{username}'), as: nil get '/', to: redirect('%{username}'), as: nil
end end
......
...@@ -3345,6 +3345,9 @@ msgstr "" ...@@ -3345,6 +3345,9 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable." msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr "" msgstr ""
msgid "Issues, merge requests, pushes and comments."
msgstr ""
msgid "Jan" msgid "Jan"
msgstr "" msgstr ""
...@@ -3709,6 +3712,9 @@ msgstr "" ...@@ -3709,6 +3712,9 @@ msgstr ""
msgid "Median" msgid "Median"
msgstr "" msgstr ""
msgid "Member since %{date}"
msgstr ""
msgid "Members" msgid "Members"
msgstr "" msgstr ""
...@@ -5753,6 +5759,9 @@ msgstr "" ...@@ -5753,6 +5759,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 ""
...@@ -6581,6 +6590,51 @@ msgstr "" ...@@ -6581,6 +6590,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