diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 90de3f949c..4c3ec9a9bd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -559,7 +559,7 @@ module ApplicationHelper I18n.defaultLocale = "#{I18n.default_locale}"; I18n.locale = "#{I18n.locale}"; }) - unless User.current.pref.warn_on_leaving_unsaved == '0' + if User.current.pref.warn_on_leaving_unsaved? tags += javascript_tag(%{ jQuery(document).ready(function(){ warnLeavingUnsaved('#{escape_javascript(l(:text_warn_on_leaving_unsaved))}'); diff --git a/app/models/user.rb b/app/models/user.rb index ea893d6076..47304e9b06 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -442,7 +442,7 @@ class User < Principal end def impaired - (anonymous? && Setting.accessibility_mode_for_anonymous?) || !!pref.impaired + (anonymous? && Setting.accessibility_mode_for_anonymous?) || pref.impaired? end def impaired? @@ -450,7 +450,7 @@ class User < Principal end def wants_comments_in_reverse_order? - pref[:comments_sorting] == 'desc' + pref.comments_in_reverse_order? end # Return user's RSS key (a 40 chars long string), used to access feeds diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index e8f5e74b23..7612ff6be0 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -32,6 +32,8 @@ class UserPreference < ActiveRecord::Base serialize :others validates_presence_of :user + validate :time_zone_correctness, if: -> { time_zone.present? } + validate :theme_correctness, if: -> { theme.present? } attr_accessible :user @@ -59,25 +61,81 @@ class UserPreference < ActiveRecord::Base others[:comments_sorting] = order end + def comments_in_reverse_order? + comments_sorting == 'desc' + end + + def warn_on_leaving_unsaved? + # Need to cast here as previous values were '0' / '1' + to_boolean(others.fetch(:warn_on_leaving_unsaved) { true }) + end + + def warn_on_leaving_unsaved=(value) + others[:warn_on_leaving_unsaved] = to_boolean(value) + end + + # Provide an alias to form builders + alias :comments_in_reverse_order :comments_in_reverse_order? + alias :warn_on_leaving_unsaved :warn_on_leaving_unsaved? + + def comments_in_reverse_order=(value) + others[:comments_sorting] = to_boolean(value) ? 'desc' : 'asc' + end + def theme others[:theme] || OpenProject::Themes.application_theme_identifier end - def theme=(order) - others[:theme] = order + def theme=(identifier) + others[:theme] = identifier.nil? ? nil : identifier.to_sym + end + + def canonical_time_zone + return if time_zone.nil? + + zone = ActiveSupport::TimeZone.new(time_zone) + unless zone.nil? + zone.tzinfo.canonical_identifier + end + end + + def impaired? + !!impaired + end + + def warn_on_leaving_unsaved? + # Need to cast here as previous values were '0' / '1' + to_boolean(others.fetch(:warn_on_leaving_unsaved) { true }) end def warn_on_leaving_unsaved - others.fetch(:warn_on_leaving_unsaved) { '1' } + warn_on_leaving_unsaved? end def warn_on_leaving_unsaved=(value) - others[:warn_on_leaving_unsaved] = value + others[:warn_on_leaving_unsaved] = to_boolean(value) end private + def to_boolean(value) + ActiveRecord::Type::Boolean.new.type_cast_from_user(value) + end + def init_other_preferences self.others ||= { no_self_notified: true } end + + def time_zone_correctness + errors.add(:time_zone, :inclusion) if time_zone.present? && canonical_time_zone.nil? + end + + def theme_correctness + return true if theme == OpenProject::Themes.application_theme_identifier + themes = OpenProject::Themes.all.map(&:identifier) + + unless themes.any? { |identifier| theme.to_sym == identifier } + errors.add(:theme, :inclusion) + end + end end diff --git a/features/work_packages/attachments.feature b/features/work_packages/attachments.feature index 5c3c6c8ac9..9206c4e7ff 100644 --- a/features/work_packages/attachments.feature +++ b/features/work_packages/attachments.feature @@ -44,7 +44,7 @@ Feature: Attachments on work packages And there is 1 user with the following: | login | bob| And the user "bob" has the following preferences - | warn_on_leaving_unsaved | 0 | + | warn_on_leaving_unsaved | false | And the user "bob" is a "member" in the project "parent" And there are the following issue status: | name | is_closed | is_default | diff --git a/features/work_packages/preview.feature b/features/work_packages/preview.feature index bb3b4230ee..6d31d18ec2 100644 --- a/features/work_packages/preview.feature +++ b/features/work_packages/preview.feature @@ -45,7 +45,7 @@ Feature: Switching types of work packages | lastname | Bobbit | # prevent alerts to occur that would impede subsequent scenarios And the user "bob" has the following preferences - | warn_on_leaving_unsaved | 0 | + | warn_on_leaving_unsaved | false | And the user "bob" is a "member" in the project "project1" And I am already logged in as "bob" diff --git a/lib/api/v3/root.rb b/lib/api/v3/root.rb index 30249887f5..0037df461c 100644 --- a/lib/api/v3/root.rb +++ b/lib/api/v3/root.rb @@ -47,6 +47,7 @@ module API mount ::API::V3::StringObjects::StringObjectsAPI mount ::API::V3::Types::TypesAPI mount ::API::V3::Users::UsersAPI + mount ::API::V3::UserPreferences::UserPreferencesAPI mount ::API::V3::Versions::VersionsAPI mount ::API::V3::WorkPackages::WorkPackagesAPI diff --git a/lib/api/v3/root_representer.rb b/lib/api/v3/root_representer.rb index d0472f37c3..90935271e8 100644 --- a/lib/api/v3/root_representer.rb +++ b/lib/api/v3/root_representer.rb @@ -38,6 +38,19 @@ module API } end + link :user do + { + href: api_v3_paths.user(current_user.id), + title: current_user.name + } if current_user.logged? + end + + link :userPreferences do + { + href: api_v3_paths.my_preferences + } if current_user.logged? + end + link :priorities do { href: api_v3_paths.priorities diff --git a/lib/api/v3/user_preferences/user_preferences_api.rb b/lib/api/v3/user_preferences/user_preferences_api.rb new file mode 100644 index 0000000000..67c881d69e --- /dev/null +++ b/lib/api/v3/user_preferences/user_preferences_api.rb @@ -0,0 +1,65 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require_dependency 'api/v3/user_preferences/user_preferences_representer' + +module API + module V3 + module UserPreferences + class UserPreferencesAPI < ::API::OpenProjectAPI + resource :my_preferences do + helpers do + def represent_preferences + UserPreferencesRepresenter.new(@preferences, current_user: current_user) + end + end + + before do + fail ::API::Errors::Unauthenticated unless current_user.logged? + @preferences = current_user.pref + end + + get do + represent_preferences + end + + patch do + representer = represent_preferences + representer.from_hash(request_body) + + if @preferences.save + representer + else + raise ::API::Errors::ErrorBase.create_and_merge_errors(@preferences.errors) + end + end + end + end + end + end +end diff --git a/lib/api/v3/user_preferences/user_preferences_representer.rb b/lib/api/v3/user_preferences/user_preferences_representer.rb new file mode 100644 index 0000000000..62866987a2 --- /dev/null +++ b/lib/api/v3/user_preferences/user_preferences_representer.rb @@ -0,0 +1,76 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module UserPreferences + class UserPreferencesRepresenter < ::API::Decorators::Single + link :self do + { + href: api_v3_paths.my_preferences + } + end + + link :user do + { + href: api_v3_paths.user(represented.user.id), + title: represented.user.name + } + end + + link :updateImmediately do + { + href: api_v3_paths.my_preferences, + method: :patch + } + end + + property :hide_mail + property :time_zone, + getter: -> (*) { canonical_time_zone }, + render_nil: true + + property :theme + property :warn_on_leaving_unsaved + property :comments_in_reverse_order, + as: :commentSortDescending + + property :impaired?, + as: :accessibilityMode + + def _type + 'UserPreferences' + end + end + end + end +end diff --git a/lib/api/v3/utilities/path_helper.rb b/lib/api/v3/utilities/path_helper.rb index d6fea71d4f..e18dbf5693 100644 --- a/lib/api/v3/utilities/path_helper.rb +++ b/lib/api/v3/utilities/path_helper.rb @@ -84,6 +84,10 @@ module API "#{work_packages_by_project(project_id)}/form" end + def self.my_preferences + "#{root}/my_preferences" + end + def self.priorities "#{root}/priorities" end diff --git a/lib/api/v3/work_packages/work_packages_shared_helpers.rb b/lib/api/v3/work_packages/work_packages_shared_helpers.rb index 52f6ee4c7a..63ee45a1d5 100644 --- a/lib/api/v3/work_packages/work_packages_shared_helpers.rb +++ b/lib/api/v3/work_packages/work_packages_shared_helpers.rb @@ -34,9 +34,6 @@ module API module WorkPackages module WorkPackagesSharedHelpers extend Grape::API::Helpers - def request_body - env['api.request.body'] - end def merge_hash_into_work_package!(hash, work_package) payload = ::API::V3::WorkPackages::WorkPackagePayloadRepresenter.create(work_package) diff --git a/spec/features/work_packages/details/activity_comments_spec.rb b/spec/features/work_packages/details/activity_comments_spec.rb index b0f2ef7ce9..2034394a03 100644 --- a/spec/features/work_packages/details/activity_comments_spec.rb +++ b/spec/features/work_packages/details/activity_comments_spec.rb @@ -20,7 +20,7 @@ describe 'activity comments', js: true do before do login_as(user) - allow(user.pref).to receive(:warn_on_leaving_unsaved).and_return('0') + allow(user.pref).to receive(:warn_on_leaving_unsaved?).and_return(false) end context 'with permission' do diff --git a/spec/lib/api/v3/root_representer_spec.rb b/spec/lib/api/v3/root_representer_spec.rb index a06387f66f..bea03a836f 100644 --- a/spec/lib/api/v3/root_representer_spec.rb +++ b/spec/lib/api/v3/root_representer_spec.rb @@ -31,7 +31,8 @@ require 'spec_helper' describe ::API::V3::RootRepresenter do include ::API::V3::Utilities::PathHelper - let(:representer) { described_class.new({}, current_user: double('current_user')) } + let(:user) { FactoryGirl.build(:user) } + let(:representer) { described_class.new({}, current_user: user) } let(:app_title) { 'Foo Project' } let(:version) { 'The version is over 9000!' } @@ -68,6 +69,29 @@ describe ::API::V3::RootRepresenter do let(:link) { 'workPackages' } let(:href) { api_v3_paths.work_packages } end + + it_behaves_like 'has a titled link' do + let(:link) { 'user' } + let(:href) { api_v3_paths.user(user.id) } + let(:title) { user.name } + end + + it_behaves_like 'has an untitled link' do + let(:link) { 'userPreferences' } + let(:href) { api_v3_paths.my_preferences } + end + + context 'anonymous user' do + let(:representer) { described_class.new({}, current_user: User.anonymous) } + + it_behaves_like 'has no link' do + let(:link) { 'user' } + end + + it_behaves_like 'has no link' do + let(:link) { 'userPreferences' } + end + end end it 'shows the name of the instance' do diff --git a/spec/lib/api/v3/user_preferences/user_preferences_representer_spec.rb b/spec/lib/api/v3/user_preferences/user_preferences_representer_spec.rb new file mode 100644 index 0000000000..ebf002b0ee --- /dev/null +++ b/spec/lib/api/v3/user_preferences/user_preferences_representer_spec.rb @@ -0,0 +1,103 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' + +describe ::API::V3::UserPreferences::UserPreferencesRepresenter do + include ::API::V3::Utilities::PathHelper + + let(:preference) { FactoryGirl.build(:user_preference) } + let(:user) { FactoryGirl.build_stubbed(:user, preference: preference) } + let(:representer) { described_class.new(preference, current_user: user) } + + before do + allow(preference).to receive(:user).and_return(user) + end + + context 'generation' do + subject(:generated) { representer.to_json } + + it { is_expected.to include_json('UserPreferences'.to_json).at_path('_type') } + it { is_expected.to have_json_path('hideMail') } + it { is_expected.to have_json_path('timeZone') } + it { is_expected.to have_json_path('theme') } + it { is_expected.to have_json_path('commentSortDescending') } + it { is_expected.to have_json_path('warnOnLeavingUnsaved') } + it { is_expected.to have_json_path('accessibilityMode') } + + describe 'timeZone' do + context 'no time zone set' do + let(:preference) { FactoryGirl.build(:user_preference, time_zone: '') } + + it 'shows the timeZone as nil' do + is_expected.to be_json_eql(nil.to_json).at_path('timeZone') + end + end + + context 'short timezone set' do + let(:preference) { FactoryGirl.build(:user_preference, time_zone: 'Berlin') } + + it 'shows the canonical time zone' do + is_expected.to be_json_eql('Europe/Berlin'.to_json).at_path('timeZone') + end + end + + context 'canonical timezone set' do + let(:preference) { FactoryGirl.build(:user_preference, time_zone: 'Europe/Paris') } + + it 'shows the canonical time zone' do + is_expected.to be_json_eql('Europe/Paris'.to_json).at_path('timeZone') + end + end + end + + describe '_links' do + it_behaves_like 'has an untitled link' do + let(:link) { 'self' } + let(:href) { api_v3_paths.my_preferences } + end + + it_behaves_like 'has a titled link' do + let(:link) { 'user' } + let(:title) { user.name } + let(:href) { api_v3_paths.user(user.id) } + end + + describe 'immediate update' do + it_behaves_like 'has an untitled link' do + let(:link) { 'updateImmediately' } + let(:href) { api_v3_paths.my_preferences } + end + + it 'is a patch link' do + is_expected.to be_json_eql('patch'.to_json).at_path('_links/updateImmediately/method') + end + end + end + end +end diff --git a/spec/lib/api/v3/utilities/path_helper_spec.rb b/spec/lib/api/v3/utilities/path_helper_spec.rb index 6e4456c00d..46e119baa2 100644 --- a/spec/lib/api/v3/utilities/path_helper_spec.rb +++ b/spec/lib/api/v3/utilities/path_helper_spec.rb @@ -120,6 +120,12 @@ describe ::API::V3::Utilities::PathHelper do it_behaves_like 'api v3 path', '/projects/42/work_packages/form' end + describe '#user_preferences' do + subject { helper.my_preferences } + + it_behaves_like 'api v3 path', '/my_preferences' + end + describe '#render_markup' do subject { helper.render_markup(format: 'super_fancy', link: 'link-ish') } diff --git a/spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb b/spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb index ad76361aa8..0013ce6c14 100644 --- a/spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb +++ b/spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb @@ -49,6 +49,10 @@ describe ::API::V3::WorkPackages::WorkPackagesSharedHelpers do @env end + def request_body + @env['api.request.body'] + end + def current_user @user end diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb index df5dbb34df..9ef9036070 100644 --- a/spec/models/user_preference_spec.rb +++ b/spec/models/user_preference_spec.rb @@ -29,7 +29,8 @@ require 'spec_helper' describe UserPreference do - subject { described_class.new } + let(:user) { FactoryGirl.build_stubbed(:user) } + subject { FactoryGirl.build(:user_preference, user: user) } describe 'default settings' do it 'hides the email address' do @@ -44,4 +45,103 @@ describe UserPreference do expect(subject.others[:no_self_notified]).to be_truthy end end + + shared_examples 'accepts real and false booleans' do |setter, getter| + it 'accepts true boolean' do + subject.send(setter, true) + expect(subject.send(getter)).to be true + + subject.send(setter, false) + expect(subject.send(getter)).to be false + end + + it 'accepts false booleans' do + %w(true 1).each do |str| + subject.send(setter, str) + expect(subject.send(getter)).to be true + end + + %w(false 0).each do |str| + subject.send(setter, str) + expect(subject.send(getter)).to be false + end + end + end + + describe 'sort order' do + it_behaves_like 'accepts real and false booleans', + :comments_in_reverse_order=, + :comments_in_reverse_order? + + it 'can be changed by string' do + subject.comments_sorting = 'desc' + expect(subject.comments_in_reverse_order?).to be true + + subject.comments_sorting = 'asc' + expect(subject.comments_in_reverse_order?).to be false + end + end + + describe 'warn on unsaved changes' do + it_behaves_like 'accepts real and false booleans', + :warn_on_leaving_unsaved=, + :warn_on_leaving_unsaved? + end + + describe 'time_zone' do + it 'allows to save short time zones' do + subject.time_zone = 'Berlin' + expect(subject).to be_valid + expect(subject.time_zone).to eq('Berlin') + expect(subject.canonical_time_zone).to eq('Europe/Berlin') + end + + it 'allows to set full time zones' do + subject.time_zone = 'Europe/Paris' + expect(subject).to be_valid + expect(subject.time_zone).to eq('Europe/Paris') + expect(subject.canonical_time_zone).to eq('Europe/Paris') + end + + it 'disallows invalid time zones' do + subject.time_zone = 'Berlin123' + expect(subject).not_to be_valid + end + + it 'allows empty values' do + subject.time_zone = nil + expect(subject).to be_valid + + subject.time_zone = '' + expect(subject).to be_valid + end + end + + describe 'theme' do + it 'allows to save valid themes' do + subject.theme = 'default' + expect(subject).to be_valid + expect(subject.theme).to eq(:default) + + subject.theme = :default + expect(subject).to be_valid + expect(subject.theme).to eq(:default) + end + + it 'allows empty values' do + subject.theme = nil + expect(subject).to be_valid + + subject.theme = '' + expect(subject).to be_valid + end + + it 'rejects invalid themes' do + subject.theme = :mycoolthemethatisnotavailableyet + expect(subject).not_to be_valid + + subject.theme = 'mycoolthemethatisnotavailableyet' + expect(subject).not_to be_valid + end + end end diff --git a/spec/requests/api/v3/root_resource_spec.rb b/spec/requests/api/v3/root_resource_spec.rb index 2f7e4238f4..49da811e4a 100644 --- a/spec/requests/api/v3/root_resource_spec.rb +++ b/spec/requests/api/v3/root_resource_spec.rb @@ -40,7 +40,8 @@ describe 'API v3 Root resource' do let(:project) { FactoryGirl.create(:project, is_public: false) } describe '#get' do - subject(:response) { last_response } + let(:response) { last_response } + subject { response.body } let(:get_path) { api_v3_paths.root } context 'anonymous user' do @@ -49,11 +50,11 @@ describe 'API v3 Root resource' do end it 'should respond with 200' do - expect(subject.status).to eq(200) + expect(response.status).to eq(200) end it 'should respond with a root representer' do - expect(subject.body).to have_json_path('instanceName') + expect(subject).to have_json_path('instanceName') end end @@ -65,11 +66,11 @@ describe 'API v3 Root resource' do end it 'should respond with 200' do - expect(subject.status).to eq(200) + expect(response.status).to eq(200) end it 'should respond with a root representer' do - expect(subject.body).to have_json_path('instanceName') + expect(subject).to have_json_path('instanceName') end end end diff --git a/spec/requests/api/v3/user_preferences/user_preferences_resource_spec.rb b/spec/requests/api/v3/user_preferences/user_preferences_resource_spec.rb new file mode 100644 index 0000000000..defa8cfcb9 --- /dev/null +++ b/spec/requests/api/v3/user_preferences/user_preferences_resource_spec.rb @@ -0,0 +1,141 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require 'rack/test' + +describe 'API v3 UserPreferences resource', type: :request do + include Rack::Test::Methods + include ::API::V3::Utilities::PathHelper + + let(:user) { FactoryGirl.create(:user) } + let(:preference) { FactoryGirl.create(:user_preference, user: user) } + let(:representer) { described_class.new(preference, current_user: user) } + let(:preference_path) { api_v3_paths.my_preferences } + subject(:response) { last_response } + + before do + allow(User).to receive(:current).and_return user + allow(User).to receive(:preference).and_return preference + end + + describe '#GET' do + before do + get preference_path + end + + context 'when not logged in' do + let(:user) { User.anonymous } + it 'should respond with 401' do + expect(subject.status).to eq(401) + end + end + + context 'when logged in' do + it 'should respond with 200' do + expect(subject.status).to eq(200) + end + it 'should respond with a UserPreferences representer' do + expect(subject.body).to be_json_eql('UserPreferences'.to_json).at_path('_type') + end + end + end + + describe '#PATCH' do + before do + patch preference_path, params.to_json, 'CONTENT_TYPE' => 'application/json' + preference.reload + end + + context 'when not logged in' do + let(:user) { User.anonymous } + let(:params) do + { whatever: true } + end + it 'should respond with 401' do + expect(subject.status).to eq(401) + end + end + + describe 'theme' do + context 'with invalid identifier' do + let(:params) do + { theme: 'mycoolthemethatisnotavailableyet' } + end + + it_behaves_like 'constraint violation' do + let(:message) { 'Theme is not set to one of the allowed values.' } + end + end + + context 'with correct identifier' do + let(:params) do + { theme: 'default' } + end + it 'should respond with a UserPreferences representer' do + expect(subject.body).to be_json_eql(:default.to_json).at_path('theme') + expect(preference.theme).to eq(:default) + end + end + end + + describe 'timezone' do + context 'with invalid timezone' do + let(:params) do + { timeZone: 'Europe/Awesomeland' } + end + + it_behaves_like 'constraint violation' do + let(:message) { 'Time zone is not set to one of the allowed values.' } + end + end + + context 'with full time zone' do + let(:params) do + { timeZone: 'Europe/Paris' } + end + it 'should respond with a UserPreferences representer' do + expect(subject.body).to be_json_eql('Europe/Paris'.to_json).at_path('timeZone') + expect(preference.time_zone).to eq('Europe/Paris') + end + end + + context 'with short time zone' do + let(:params) do + { timeZone: 'Hawaii' } + end + + it 'should respond with a UserPreferences representer' do + expect(subject.body).to be_json_eql('Pacific/Honolulu'.to_json).at_path('timeZone') + expect(preference.time_zone).to eq('Hawaii') + expect(preference.canonical_time_zone).to eq('Pacific/Honolulu') + end + end + end + end +end