mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-19 09:36:47 +10:00
Merge branch 'rs-custom-dashboard-backport' into '7-12-stable'
Backport custom dashboard This is a backport of !778 without the `Gitlab::Themes` refactor for the 7.12 branch. See merge request !828
This commit is contained in:
@@ -55,7 +55,7 @@ class Dispatcher
|
||||
when 'projects:merge_requests:index'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
MergeRequests.init()
|
||||
when 'dashboard:show'
|
||||
when 'dashboard:show', 'root:show'
|
||||
new Dashboard()
|
||||
new Activities()
|
||||
when 'dashboard:projects:starred'
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
class @Profile
|
||||
constructor: ->
|
||||
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click ->
|
||||
# Submit the form
|
||||
$('.edit_user').submit()
|
||||
|
||||
new Flash("Appearance settings saved", "notice")
|
||||
# Automatically submit the Preferences form when any of its radio buttons change
|
||||
$('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
|
||||
$(this).parents('form').submit()
|
||||
|
||||
$('.update-username form').on 'ajax:before', ->
|
||||
$('.loading-gif').show()
|
||||
@@ -18,7 +16,6 @@ class @Profile
|
||||
$('.update-notifications').on 'ajax:complete', ->
|
||||
$(this).find('.btn-save').enable()
|
||||
|
||||
|
||||
$('.js-choose-user-avatar-button').bind "click", ->
|
||||
form = $(this).closest("form")
|
||||
form.find(".js-user-avatar-input").click()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
class Profiles::PreferencesController < Profiles::ApplicationController
|
||||
before_action :user
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
begin
|
||||
if @user.update_attributes(preferences_params)
|
||||
flash[:notice] = 'Preferences saved.'
|
||||
else
|
||||
flash[:alert] = 'Failed to save preferences.'
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
# Raised when `dashboard` is given an invalid value.
|
||||
flash[:alert] = "Failed to save preferences (#{e.message})."
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to profile_preferences_path }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
def preferences_params
|
||||
params.require(:user).permit(
|
||||
:color_scheme_id,
|
||||
:dashboard,
|
||||
:theme_id
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -8,9 +8,6 @@ class ProfilesController < Profiles::ApplicationController
|
||||
def show
|
||||
end
|
||||
|
||||
def design
|
||||
end
|
||||
|
||||
def applications
|
||||
@applications = current_user.oauth_applications
|
||||
@authorized_tokens = current_user.oauth_authorized_tokens
|
||||
@@ -29,7 +26,6 @@ class ProfilesController < Profiles::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :back }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,10 +61,21 @@ class ProfilesController < Profiles::ApplicationController
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(
|
||||
:email, :password, :password_confirmation, :bio, :name,
|
||||
:username, :skype, :linkedin, :twitter, :website_url,
|
||||
:color_scheme_id, :theme_id, :avatar, :hide_no_ssh_key,
|
||||
:hide_no_password, :location, :public_email
|
||||
:avatar,
|
||||
:bio,
|
||||
:email,
|
||||
:hide_no_password,
|
||||
:hide_no_ssh_key,
|
||||
:linkedin,
|
||||
:location,
|
||||
:name,
|
||||
:password,
|
||||
:password_confirmation,
|
||||
:public_email,
|
||||
:skype,
|
||||
:twitter,
|
||||
:username,
|
||||
:website_url
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# RootController
|
||||
#
|
||||
# This controller exists solely to handle requests to `root_url`. When a user is
|
||||
# logged in and has customized their `dashboard` setting, they will be
|
||||
# redirected to their preferred location.
|
||||
#
|
||||
# For users who haven't customized the setting, we simply delegate to
|
||||
# `DashboardController#show`, which is the default.
|
||||
class RootController < DashboardController
|
||||
before_action :redirect_to_custom_dashboard, only: [:show]
|
||||
|
||||
def show
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redirect_to_custom_dashboard
|
||||
return unless current_user
|
||||
|
||||
case current_user.dashboard
|
||||
when 'stars'
|
||||
redirect_to starred_dashboard_projects_path
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# Helper methods for per-User preferences
|
||||
module PreferencesHelper
|
||||
# Maps `dashboard` values to more user-friendly option text
|
||||
DASHBOARD_CHOICES = {
|
||||
projects: 'Your Projects (default)',
|
||||
stars: 'Starred Projects'
|
||||
}.with_indifferent_access.freeze
|
||||
|
||||
# Returns an Array usable by a select field for more user-friendly option text
|
||||
def dashboard_choices
|
||||
defined = User.dashboards
|
||||
|
||||
if defined.size != DASHBOARD_CHOICES.size
|
||||
# Ensure that anyone adding new options updates this method too
|
||||
raise RuntimeError, "`User` defines #{defined.size} dashboard choices," +
|
||||
" but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
|
||||
else
|
||||
defined.map do |key, _|
|
||||
# Use `fetch` so `KeyError` gets raised when a key is missing
|
||||
[DASHBOARD_CHOICES.fetch(key), key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+6
-1
@@ -50,12 +50,13 @@
|
||||
# bitbucket_access_token :string(255)
|
||||
# bitbucket_access_token_secret :string(255)
|
||||
# location :string(255)
|
||||
# public_email :string(255) default(""), not null
|
||||
# encrypted_otp_secret :string(255)
|
||||
# encrypted_otp_secret_iv :string(255)
|
||||
# encrypted_otp_secret_salt :string(255)
|
||||
# otp_required_for_login :boolean
|
||||
# otp_backup_codes :text
|
||||
# public_email :string(255) default(""), not null
|
||||
# dashboard :integer default(0)
|
||||
#
|
||||
|
||||
require 'carrierwave/orm/activerecord'
|
||||
@@ -701,4 +702,8 @@ class User < ActiveRecord::Base
|
||||
def can_be_removed?
|
||||
!solo_owned_groups.present?
|
||||
end
|
||||
|
||||
# User's Dashboard preference
|
||||
# Note: When adding an option, it MUST go on the end of the array.
|
||||
enum dashboard: [:projects, :stars]
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%ul.nav.nav-sidebar
|
||||
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
|
||||
= link_to root_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
|
||||
= nav_link(path: ['dashboard#show', 'root#show'], html_options: {class: 'home'}) do
|
||||
= link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
|
||||
= icon('dashboard fw')
|
||||
%span
|
||||
Your Projects
|
||||
|
||||
@@ -38,11 +38,12 @@
|
||||
%span
|
||||
SSH Keys
|
||||
%span.count= current_user.keys.count
|
||||
= nav_link(path: 'profiles#design') do
|
||||
= link_to design_profile_path, title: 'Design', data: {placement: 'right'} do
|
||||
= nav_link(controller: :preferences) do
|
||||
= link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do
|
||||
-# TODO (rspeicher): Better icon?
|
||||
= icon('image fw')
|
||||
%span
|
||||
Design
|
||||
Preferences
|
||||
= nav_link(path: 'profiles#history') do
|
||||
= link_to history_profile_path, title: 'History', data: {placement: 'right'} do
|
||||
= icon('history fw')
|
||||
|
||||
+20
-6
@@ -1,11 +1,13 @@
|
||||
- page_title "Design"
|
||||
- page_title 'Preferences'
|
||||
%h3.page-title
|
||||
= page_title
|
||||
%p.light
|
||||
Appearance settings will be saved to your profile and made available across all devices.
|
||||
These settings allow you to customize the appearance and behavior of the site.
|
||||
They are saved with your account and will persist to any device you use to
|
||||
access the site.
|
||||
%hr
|
||||
|
||||
= form_for @user, url: profile_path, remote: true, method: :put do |f|
|
||||
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f|
|
||||
.panel.panel-default.application-theme
|
||||
.panel-heading
|
||||
Application theme
|
||||
@@ -50,7 +52,19 @@
|
||||
.code_highlight_opts
|
||||
- color_schemes.each do |color_scheme_id, color_scheme|
|
||||
= label_tag do
|
||||
.prev
|
||||
= image_tag "#{color_scheme}-scheme-preview.png"
|
||||
.prev= image_tag "#{color_scheme}-scheme-preview.png"
|
||||
= f.radio_button :color_scheme_id, color_scheme_id
|
||||
= color_scheme.gsub(/[-_]+/, ' ').humanize
|
||||
= color_scheme.tr('-_', ' ').titleize
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
Behavior
|
||||
.panel-body
|
||||
.form-group
|
||||
= f.label :dashboard, class: 'control-label' do
|
||||
Default Dashboard
|
||||
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
|
||||
.col-sm-10
|
||||
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
|
||||
.panel-footer
|
||||
= f.submit 'Save', class: 'btn btn-save'
|
||||
@@ -1,3 +1,9 @@
|
||||
// Remove body class for any previous theme, re-add current one
|
||||
$('body').removeClass('<%= Gitlab::Theme.body_classes %>')
|
||||
$('body').addClass('<%= app_theme %> <%= theme_type %>')
|
||||
|
||||
// Re-enable the "Save" button
|
||||
$('input[type=submit]').enable()
|
||||
|
||||
// Show the notice flash message
|
||||
new Flash('<%= flash.discard(:notice) %>', 'notice')
|
||||
+2
-2
@@ -203,7 +203,6 @@ Gitlab::Application.routes.draw do
|
||||
resource :profile, only: [:show, :update] do
|
||||
member do
|
||||
get :history
|
||||
get :design
|
||||
get :applications
|
||||
|
||||
put :reset_private_token
|
||||
@@ -222,6 +221,7 @@ Gitlab::Application.routes.draw do
|
||||
put :reset
|
||||
end
|
||||
end
|
||||
resource :preferences, only: [:show, :update]
|
||||
resources :keys
|
||||
resources :emails, only: [:index, :create, :destroy]
|
||||
resource :avatar, only: [:destroy]
|
||||
@@ -293,7 +293,7 @@ Gitlab::Application.routes.draw do
|
||||
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
|
||||
end
|
||||
|
||||
root to: "dashboard#show"
|
||||
root to: "root#show"
|
||||
|
||||
#
|
||||
# Project Area
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class AddDashboardToUsers < ActiveRecord::Migration
|
||||
def up
|
||||
add_column :users, :dashboard, :integer, default: 0
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :users, :dashboard
|
||||
end
|
||||
end
|
||||
+5
-3
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150529150354) do
|
||||
ActiveRecord::Schema.define(version: 20150610065936) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -29,12 +29,13 @@ ActiveRecord::Schema.define(version: 20150529150354) do
|
||||
t.boolean "twitter_sharing_enabled", default: true
|
||||
t.text "restricted_visibility_levels"
|
||||
t.boolean "version_check_enabled", default: true
|
||||
t.integer "max_attachment_size", default: 10, null: false
|
||||
t.integer "max_attachment_size", default: 10, null: false
|
||||
t.integer "default_project_visibility"
|
||||
t.integer "default_snippet_visibility"
|
||||
t.text "restricted_signup_domains"
|
||||
t.boolean "user_oauth_applications", default: true
|
||||
t.string "after_sign_out_path"
|
||||
t.integer "session_expire_delay", default: 10080, null: false
|
||||
end
|
||||
|
||||
create_table "broadcast_messages", force: true do |t|
|
||||
@@ -495,12 +496,13 @@ ActiveRecord::Schema.define(version: 20150529150354) do
|
||||
t.string "bitbucket_access_token"
|
||||
t.string "bitbucket_access_token_secret"
|
||||
t.string "location"
|
||||
t.string "public_email", default: "", null: false
|
||||
t.string "encrypted_otp_secret"
|
||||
t.string "encrypted_otp_secret_iv"
|
||||
t.string "encrypted_otp_secret_salt"
|
||||
t.boolean "otp_required_for_login"
|
||||
t.text "otp_backup_codes"
|
||||
t.string "public_email", default: "", null: false
|
||||
t.integer "dashboard", default: 0
|
||||
end
|
||||
|
||||
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
|
||||
|
||||
+36
-35
@@ -1,35 +1,36 @@
|
||||
# Documentation
|
||||
|
||||
## User documentation
|
||||
|
||||
- [API](api/README.md) Automate GitLab via a simple and powerful API.
|
||||
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
||||
- [Importing to GitLab](workflow/importing/README.md).
|
||||
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
|
||||
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
|
||||
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
|
||||
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
|
||||
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
|
||||
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
|
||||
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
|
||||
|
||||
## Administrator documentation
|
||||
|
||||
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
|
||||
- [Install](install/README.md) Requirements, directory structures and installation from source.
|
||||
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
|
||||
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
|
||||
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
|
||||
- [Log system](logs/logs.md) Log system.
|
||||
- [Operations](operations/README.md) Keeping GitLab up and running
|
||||
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
|
||||
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
|
||||
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
|
||||
- [Update](update/README.md) Update guides to upgrade your installation.
|
||||
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
|
||||
|
||||
## Contributor documentation
|
||||
|
||||
- [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
|
||||
- [Legal](legal/README.md) Contributor license agreements.
|
||||
- [Release](release/README.md) How to make the monthly and security releases.
|
||||
# Documentation
|
||||
|
||||
## User documentation
|
||||
|
||||
- [API](api/README.md) Automate GitLab via a simple and powerful API.
|
||||
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
||||
- [Importing to GitLab](workflow/importing/README.md).
|
||||
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
|
||||
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
|
||||
- [Profile Settings](profile/README.md)
|
||||
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
|
||||
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
|
||||
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
|
||||
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
|
||||
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
|
||||
|
||||
## Administrator documentation
|
||||
|
||||
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
|
||||
- [Install](install/README.md) Requirements, directory structures and installation from source.
|
||||
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
|
||||
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
|
||||
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
|
||||
- [Log system](logs/logs.md) Log system.
|
||||
- [Operations](operations/README.md) Keeping GitLab up and running
|
||||
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
|
||||
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
|
||||
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
|
||||
- [Update](update/README.md) Update guides to upgrade your installation.
|
||||
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
|
||||
|
||||
## Contributor documentation
|
||||
|
||||
- [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
|
||||
- [Legal](legal/README.md) Contributor license agreements.
|
||||
- [Release](release/README.md) How to make the monthly and security releases.
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
# Profile Settings
|
||||
|
||||
- [Preferences](preferences.md)
|
||||
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Profile Preferences
|
||||
|
||||
Settings in the **Profile > Preferences** page allow the user to customize
|
||||
various aspects of the site to their liking.
|
||||
|
||||
## Application theme
|
||||
|
||||
Changing this setting allows the user to customize the color scheme used for the
|
||||
navigation bar on the left side of the screen.
|
||||
|
||||
The default is **Charcoal**.
|
||||
|
||||
## Syntax highlighting theme
|
||||
|
||||
Changing this setting allows the user to customize the theme used when viewing
|
||||
syntax highlighted code on the site.
|
||||
|
||||
The default is **White**.
|
||||
|
||||
## Behavior
|
||||
|
||||
### Default Dashboard
|
||||
|
||||
For users who have access to a large number of projects but only keep up with a
|
||||
select few, the amount of activity on the default Dashboard page can be
|
||||
overwhelming.
|
||||
|
||||
Changing this setting allows the user to redefine what their default dashboard
|
||||
will be. Setting it to **Starred Projects** will make that Dashboard view the
|
||||
default when signing in or clicking the application logo in the upper left.
|
||||
|
||||
The default is **Your Projects**.
|
||||
+15
-16
@@ -1,16 +1,15 @@
|
||||
# Workflow
|
||||
|
||||
- [Authorization for merge requests](authorization_for_merge_requests.md)
|
||||
- [Change your time zone](timezone.md)
|
||||
- [Feature branch workflow](workflow.md)
|
||||
- [GitLab Flow](gitlab_flow.md)
|
||||
- [Groups](groups.md)
|
||||
- [Keyboard shortcuts](shortcuts.md)
|
||||
- [Labels](labels.md)
|
||||
- [Notifications](notifications.md)
|
||||
- [Project Features](project_features.md)
|
||||
- [Project forking workflow](forking_workflow.md)
|
||||
- [Protected branches](protected_branches.md)
|
||||
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
|
||||
- [Web Editor](web_editor.md)
|
||||
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
|
||||
# Workflow
|
||||
|
||||
- [Authorization for merge requests](authorization_for_merge_requests.md)
|
||||
- [Change your time zone](timezone.md)
|
||||
- [Feature branch workflow](workflow.md)
|
||||
- [GitLab Flow](gitlab_flow.md)
|
||||
- [Groups](groups.md)
|
||||
- [Keyboard shortcuts](shortcuts.md)
|
||||
- [Labels](labels.md)
|
||||
- [Notifications](notifications.md)
|
||||
- [Project Features](project_features.md)
|
||||
- [Project forking workflow](forking_workflow.md)
|
||||
- [Protected branches](protected_branches.md)
|
||||
- [Web Editor](web_editor.md)
|
||||
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
|
||||
|
||||
@@ -18,9 +18,9 @@ Feature: Profile Active Tab
|
||||
Then the active main tab should be SSH Keys
|
||||
And no other main tabs should be active
|
||||
|
||||
Scenario: On Profile Design
|
||||
Given I visit profile design page
|
||||
Then the active main tab should be Design
|
||||
Scenario: On Profile Preferences
|
||||
Given I visit profile preferences page
|
||||
Then the active main tab should be Preferences
|
||||
And no other main tabs should be active
|
||||
|
||||
Scenario: On Profile History
|
||||
|
||||
@@ -84,16 +84,3 @@ Feature: Profile
|
||||
Then I visit profile applications page
|
||||
And I click to remove application
|
||||
Then I see that application is removed
|
||||
|
||||
@javascript
|
||||
Scenario: I change my application theme
|
||||
Given I visit profile design page
|
||||
When I change my application theme
|
||||
Then I should see the theme change immediately
|
||||
And I should receive feedback that the changes were saved
|
||||
|
||||
@javascript
|
||||
Scenario: I change my code preview theme
|
||||
Given I visit profile design page
|
||||
When I change my code preview theme
|
||||
Then I should receive feedback that the changes were saved
|
||||
|
||||
@@ -15,8 +15,8 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
|
||||
ensure_active_main_tab('SSH Keys')
|
||||
end
|
||||
|
||||
step 'the active main tab should be Design' do
|
||||
ensure_active_main_tab('Design')
|
||||
step 'the active main tab should be Preferences' do
|
||||
ensure_active_main_tab('Preferences')
|
||||
end
|
||||
|
||||
step 'the active main tab should be History' do
|
||||
|
||||
@@ -114,27 +114,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
|
||||
expect(page).to have_content "#{current_user.name} closed issue"
|
||||
end
|
||||
|
||||
step "I change my application theme" do
|
||||
page.within '.application-theme' do
|
||||
choose "Violet"
|
||||
end
|
||||
end
|
||||
|
||||
step "I change my code preview theme" do
|
||||
page.within '.code-preview-theme' do
|
||||
choose "Solarized dark"
|
||||
end
|
||||
end
|
||||
|
||||
step "I should see the theme change immediately" do
|
||||
expect(page).to have_selector('body.ui_color')
|
||||
expect(page).not_to have_selector('body.ui_basic')
|
||||
end
|
||||
|
||||
step "I should receive feedback that the changes were saved" do
|
||||
expect(page).to have_content("saved")
|
||||
end
|
||||
|
||||
step 'my password is expired' do
|
||||
current_user.update_attributes(password_expires_at: Time.now - 1.hour)
|
||||
end
|
||||
|
||||
@@ -123,8 +123,8 @@ module SharedPaths
|
||||
visit profile_keys_path
|
||||
end
|
||||
|
||||
step 'I visit profile design page' do
|
||||
visit design_profile_path
|
||||
step 'I visit profile preferences page' do
|
||||
visit profile_preferences_path
|
||||
end
|
||||
|
||||
step 'I visit profile history page' do
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Profiles::PreferencesController do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
allow(subject).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
describe 'GET show' do
|
||||
it 'renders' do
|
||||
get :show
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
|
||||
it 'assigns user' do
|
||||
get :show
|
||||
expect(assigns[:user]).to eq user
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH update' do
|
||||
def go(params: {}, format: :js)
|
||||
params.reverse_merge!(
|
||||
color_scheme_id: '1',
|
||||
dashboard: 'stars',
|
||||
theme_id: '1'
|
||||
)
|
||||
|
||||
patch :update, user: params, format: format
|
||||
end
|
||||
|
||||
context 'on successful update' do
|
||||
it 'sets the flash' do
|
||||
go
|
||||
expect(flash[:notice]).to eq 'Preferences saved.'
|
||||
end
|
||||
|
||||
it "changes the user's preferences" do
|
||||
prefs = {
|
||||
color_scheme_id: '1',
|
||||
dashboard: 'stars',
|
||||
theme_id: '2'
|
||||
}.with_indifferent_access
|
||||
|
||||
expect(user).to receive(:update_attributes).with(prefs)
|
||||
|
||||
go params: prefs
|
||||
end
|
||||
end
|
||||
|
||||
context 'on failed update' do
|
||||
it 'sets the flash' do
|
||||
expect(user).to receive(:update_attributes).and_return(false)
|
||||
|
||||
go
|
||||
|
||||
expect(flash[:alert]).to eq('Failed to save preferences.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid dashboard setting' do
|
||||
it 'sets the flash' do
|
||||
prefs = {dashboard: 'invalid'}
|
||||
|
||||
go params: prefs
|
||||
|
||||
expect(flash[:alert]).to match(/\AFailed to save preferences \(.+\)\.\z/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'as js' do
|
||||
it 'renders' do
|
||||
go
|
||||
expect(response).to render_template :update
|
||||
end
|
||||
end
|
||||
|
||||
context 'as html' do
|
||||
it 'redirects' do
|
||||
go format: :html
|
||||
expect(response).to redirect_to(profile_preferences_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe RootController do
|
||||
describe 'GET show' do
|
||||
context 'with a user' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
allow(subject).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
context 'who has customized their dashboard setting' do
|
||||
before do
|
||||
user.update_attribute(:dashboard, 'stars')
|
||||
end
|
||||
|
||||
it 'redirects to their specified dashboard' do
|
||||
get :show
|
||||
expect(response).to redirect_to starred_dashboard_projects_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'who uses the default dashboard setting' do
|
||||
it 'renders the default dashboard' do
|
||||
get :show
|
||||
expect(response).to render_template 'dashboard/show'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Profile > Preferences' do
|
||||
before do
|
||||
login_as(:user)
|
||||
visit profile_preferences_path
|
||||
end
|
||||
|
||||
describe 'User changes their application theme', js: true do
|
||||
let(:default_class) { Gitlab::Theme.css_class_by_id(nil) }
|
||||
let(:theme_5_class) { Gitlab::Theme.css_class_by_id(5) }
|
||||
|
||||
it 'creates a flash message' do
|
||||
choose 'user_theme_id_5'
|
||||
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
|
||||
it 'updates their preference' do
|
||||
choose 'user_theme_id_5'
|
||||
|
||||
allowing_for_delay do
|
||||
visit page.current_path
|
||||
expect(page).to have_checked_field("user_theme_id_5")
|
||||
end
|
||||
end
|
||||
|
||||
it 'reflects the changes immediately' do
|
||||
expect(page).to have_selector("body.#{default_class}")
|
||||
|
||||
choose 'user_theme_id_5'
|
||||
|
||||
expect(page).not_to have_selector("body.#{default_class}")
|
||||
expect(page).to have_selector("body.#{theme_5_class}")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'User changes their syntax highlighting theme', js: true do
|
||||
it 'creates a flash message' do
|
||||
choose 'user_color_scheme_id_5'
|
||||
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
|
||||
it 'updates their preference' do
|
||||
choose 'user_color_scheme_id_5'
|
||||
|
||||
allowing_for_delay do
|
||||
visit page.current_path
|
||||
expect(page).to have_checked_field('user_color_scheme_id_5')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'User changes their default dashboard' do
|
||||
it 'creates a flash message' do
|
||||
select 'Starred Projects', from: 'user_dashboard'
|
||||
click_button 'Save'
|
||||
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
|
||||
it 'updates their preference' do
|
||||
select 'Starred Projects', from: 'user_dashboard'
|
||||
click_button 'Save'
|
||||
|
||||
click_link 'Dashboard'
|
||||
expect(page.current_path).to eq starred_dashboard_projects_path
|
||||
|
||||
click_link 'Your Projects'
|
||||
expect(page.current_path).to eq dashboard_path
|
||||
end
|
||||
end
|
||||
|
||||
def expect_preferences_saved_message
|
||||
within('.flash-container') do
|
||||
expect(page).to have_content('Preferences saved.')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -36,8 +36,8 @@ describe "Profile access", feature: true do
|
||||
it { is_expected.to be_denied_for :visitor }
|
||||
end
|
||||
|
||||
describe "GET /profile/design" do
|
||||
subject { design_profile_path }
|
||||
describe "GET /profile/preferences" do
|
||||
subject { profile_preferences_path }
|
||||
|
||||
it { is_expected.to be_allowed_for @u1 }
|
||||
it { is_expected.to be_allowed_for :admin }
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe PreferencesHelper do
|
||||
describe 'dashboard_choices' do
|
||||
it 'raises an exception when defined choices may be missing' do
|
||||
expect(User).to receive(:dashboards).and_return(foo: 'foo')
|
||||
expect { dashboard_choices }.to raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it 'raises an exception when defined choices may be using the wrong key' do
|
||||
expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
|
||||
expect { dashboard_choices }.to raise_error(KeyError)
|
||||
end
|
||||
|
||||
it 'provides better option descriptions' do
|
||||
expect(dashboard_choices).to match_array [
|
||||
['Your Projects (default)', 'projects'],
|
||||
['Starred Projects', 'stars']
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -50,12 +50,13 @@
|
||||
# bitbucket_access_token :string(255)
|
||||
# bitbucket_access_token_secret :string(255)
|
||||
# location :string(255)
|
||||
# public_email :string(255) default(""), not null
|
||||
# encrypted_otp_secret :string(255)
|
||||
# encrypted_otp_secret_iv :string(255)
|
||||
# encrypted_otp_secret_salt :string(255)
|
||||
# otp_required_for_login :boolean
|
||||
# otp_backup_codes :text
|
||||
# public_email :string(255) default(""), not null
|
||||
# dashboard :integer default(0)
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
@@ -102,7 +102,6 @@ end
|
||||
# profile_token GET /profile/token(.:format) profile#token
|
||||
# profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token
|
||||
# profile GET /profile(.:format) profile#show
|
||||
# profile_design GET /profile/design(.:format) profile#design
|
||||
# profile_update PUT /profile/update(.:format) profile#update
|
||||
describe ProfilesController, "routing" do
|
||||
it "to #account" do
|
||||
@@ -120,9 +119,19 @@ describe ProfilesController, "routing" do
|
||||
it "to #show" do
|
||||
expect(get("/profile")).to route_to('profiles#show')
|
||||
end
|
||||
end
|
||||
|
||||
it "to #design" do
|
||||
expect(get("/profile/design")).to route_to('profiles#design')
|
||||
# profile_preferences GET /profile/preferences(.:format) profiles/preferences#show
|
||||
# PATCH /profile/preferences(.:format) profiles/preferences#update
|
||||
# PUT /profile/preferences(.:format) profiles/preferences#update
|
||||
describe Profiles::PreferencesController, 'routing' do
|
||||
it 'to #show' do
|
||||
expect(get('/profile/preferences')).to route_to('profiles/preferences#show')
|
||||
end
|
||||
|
||||
it 'to #update' do
|
||||
expect(put('/profile/preferences')).to route_to('profiles/preferences#update')
|
||||
expect(patch('/profile/preferences')).to route_to('profiles/preferences#update')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -195,11 +204,9 @@ end
|
||||
# dashboard GET /dashboard(.:format) dashboard#show
|
||||
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
|
||||
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
|
||||
# root / dashboard#show
|
||||
describe DashboardController, "routing" do
|
||||
it "to #index" do
|
||||
expect(get("/dashboard")).to route_to('dashboard#show')
|
||||
expect(get("/")).to route_to('dashboard#show')
|
||||
end
|
||||
|
||||
it "to #issues" do
|
||||
@@ -211,6 +218,14 @@ describe DashboardController, "routing" do
|
||||
end
|
||||
end
|
||||
|
||||
# root / root#show
|
||||
describe RootController, 'routing' do
|
||||
it 'to #show' do
|
||||
expect(get('/')).to route_to('root#show')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
|
||||
# user_session POST /users/sign_in(.:format) devise/sessions#create
|
||||
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
|
||||
|
||||
@@ -19,3 +19,36 @@ unless ENV['CI'] || ENV['CI_SERVER']
|
||||
# Keep only the screenshots generated from the last failing test suite
|
||||
Capybara::Screenshot.prune_strategy = :keep_last_run
|
||||
end
|
||||
|
||||
module CapybaraHelpers
|
||||
# Execute a block a certain number of times before considering it a failure
|
||||
#
|
||||
# The given block is called, and if it raises a `Capybara::ExpectationNotMet`
|
||||
# error, we wait `interval` seconds and then try again, until `retries` is
|
||||
# met.
|
||||
#
|
||||
# This allows for better handling of timing-sensitive expectations in a
|
||||
# sketchy CI environment, for example.
|
||||
#
|
||||
# interval - Delay between retries in seconds (default: 0.5)
|
||||
# retries - Number of times to execute before failing (default: 5)
|
||||
def allowing_for_delay(interval: 0.5, retries: 5)
|
||||
tries = 0
|
||||
|
||||
begin
|
||||
yield
|
||||
rescue Capybara::ExpectationNotMet => ex
|
||||
if tries <= retries
|
||||
tries += 1
|
||||
sleep interval
|
||||
retry
|
||||
else
|
||||
raise ex
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include CapybaraHelpers, type: :feature
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user