stuff_to_do_plugin 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYRIGHT.txt +18 -0
- data/CREDITS.txt +6 -0
- data/GPL.txt +339 -0
- data/README.rdoc +61 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/app/controllers/stuff_to_do_controller.rb +161 -0
- data/app/helpers/stuff_to_do_helper.rb +88 -0
- data/app/models/stuff_to_do.rb +208 -0
- data/app/models/stuff_to_do_filter.rb +32 -0
- data/app/models/stuff_to_do_mailer.rb +16 -0
- data/app/views/settings/_stuff_to_do_settings.html.erb +27 -0
- data/app/views/stuff_to_do/_issue.html.erb +16 -0
- data/app/views/stuff_to_do/_item.html.erb +5 -0
- data/app/views/stuff_to_do/_left_panes.html.erb +51 -0
- data/app/views/stuff_to_do/_panes.html.erb +11 -0
- data/app/views/stuff_to_do/_project.html.erb +6 -0
- data/app/views/stuff_to_do/_right_panes.html.erb +25 -0
- data/app/views/stuff_to_do/_time_grid.html.erb +113 -0
- data/app/views/stuff_to_do/_time_grid_form.html.erb +32 -0
- data/app/views/stuff_to_do/index.html.erb +44 -0
- data/app/views/stuff_to_do_mailer/recommended_below_threshold.erb +3 -0
- data/app/views/stuff_to_do_mailer/recommended_below_threshold.text.html.rhtml +1 -0
- data/assets/images/b.png +0 -0
- data/assets/images/bl.png +0 -0
- data/assets/images/br.png +0 -0
- data/assets/images/closelabel.gif +0 -0
- data/assets/images/loading.gif +0 -0
- data/assets/images/tl.png +0 -0
- data/assets/images/tr.png +0 -0
- data/assets/javascripts/facebox.js +319 -0
- data/assets/javascripts/jquery-1.2.6.min.js +32 -0
- data/assets/javascripts/jquery-ui.js +2839 -0
- data/assets/javascripts/jquery.contextMenu.js +212 -0
- data/assets/javascripts/semantic.cache +15 -0
- data/assets/javascripts/stuff-to-do.js +270 -0
- data/assets/javascripts/ui/build.xml +24 -0
- data/assets/javascripts/ui/effects.blind.js +49 -0
- data/assets/javascripts/ui/effects.bounce.js +78 -0
- data/assets/javascripts/ui/effects.clip.js +54 -0
- data/assets/javascripts/ui/effects.core.js +510 -0
- data/assets/javascripts/ui/effects.drop.js +50 -0
- data/assets/javascripts/ui/effects.explode.js +79 -0
- data/assets/javascripts/ui/effects.fold.js +55 -0
- data/assets/javascripts/ui/effects.highlight.js +48 -0
- data/assets/javascripts/ui/effects.pulsate.js +55 -0
- data/assets/javascripts/ui/effects.scale.js +180 -0
- data/assets/javascripts/ui/effects.shake.js +57 -0
- data/assets/javascripts/ui/effects.slide.js +50 -0
- data/assets/javascripts/ui/effects.transfer.js +59 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ar.js +26 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-bg.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ca.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-cs.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-da.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-de.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-eo.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-es.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-fa.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-fi.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-fr.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-he.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-hr.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-hu.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-hy.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-id.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-is.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-it.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ja.js +26 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ko.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-lt.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-lv.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-nl.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-no.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-pl.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-pt-BR.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ro.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-ru.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-sk.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-sl.js +26 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-sq.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-sv.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-th.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-tr.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-uk.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-zh-CN.js +25 -0
- data/assets/javascripts/ui/i18n/ui.datepicker-zh-TW.js +25 -0
- data/assets/javascripts/ui/svn.log +11 -0
- data/assets/javascripts/ui/ui.accordion.js +400 -0
- data/assets/javascripts/ui/ui.core.js +533 -0
- data/assets/javascripts/ui/ui.datepicker.js +1754 -0
- data/assets/javascripts/ui/ui.dialog.js +630 -0
- data/assets/javascripts/ui/ui.draggable.js +696 -0
- data/assets/javascripts/ui/ui.droppable.js +314 -0
- data/assets/javascripts/ui/ui.progressbar.js +114 -0
- data/assets/javascripts/ui/ui.resizable.js +805 -0
- data/assets/javascripts/ui/ui.selectable.js +266 -0
- data/assets/javascripts/ui/ui.slider.js +552 -0
- data/assets/javascripts/ui/ui.sortable.js +1012 -0
- data/assets/javascripts/ui/ui.tabs.js +572 -0
- data/assets/stylesheets/stuff_to_do.css +216 -0
- data/config/locales/bg.yml +18 -0
- data/config/locales/ca-fr.yml +18 -0
- data/config/locales/cs.yml +16 -0
- data/config/locales/da.yml +16 -0
- data/config/locales/de.yml +18 -0
- data/config/locales/en.yml +24 -0
- data/config/locales/es.yml +19 -0
- data/config/locales/fr.yml +17 -0
- data/config/locales/hu.yml +16 -0
- data/config/locales/it.yml +16 -0
- data/config/locales/ja.yml +18 -0
- data/config/locales/ko.yml +18 -0
- data/config/locales/lt.yml +18 -0
- data/config/locales/nl.yml +20 -0
- data/config/locales/pt-BR.yml +18 -0
- data/config/locales/ru.yml +19 -0
- data/config/locales/sv.yml +19 -0
- data/config/locales/tr.yml +18 -0
- data/config/routes.rb +3 -0
- data/init.rb +54 -0
- data/lang/bg.yml +17 -0
- data/lang/ca-fr.yml +17 -0
- data/lang/cs.yml +15 -0
- data/lang/da.yml +15 -0
- data/lang/de.yml +17 -0
- data/lang/en.yml +21 -0
- data/lang/es.yml +18 -0
- data/lang/fr.yml +16 -0
- data/lang/hu.yml +15 -0
- data/lang/it.yml +15 -0
- data/lang/ja.yml +17 -0
- data/lang/ko.yml +17 -0
- data/lang/lt.yml +17 -0
- data/lang/pt-br.yml +17 -0
- data/lang/ru.yml +15 -0
- data/lang/sv.yml +18 -0
- data/lang/tr.yml +17 -0
- data/lib/redmine_stuff_to_do/stuff_to_do_compatibility.rb +15 -0
- data/lib/stuff_to_do_array_patch.rb +8 -0
- data/lib/stuff_to_do_issue_patch.rb +57 -0
- data/lib/stuff_to_do_project_patch.rb +31 -0
- data/lib/stuff_to_do_user_patch.rb +10 -0
- data/rails/init.rb +1 -0
- data/spec/controllers/stuff_to_do_controller_add_to_time_grid_spec.rb +58 -0
- data/spec/controllers/stuff_to_do_controller_index_spec.rb +155 -0
- data/spec/controllers/stuff_to_do_controller_remove_from_time_grid_spec.rb +56 -0
- data/spec/controllers/stuff_to_do_controller_reorder_spec.rb +179 -0
- data/spec/controllers/stuff_to_do_controller_save_time_entries_spec.rb +56 -0
- data/spec/controllers/stuff_to_do_private_methods_spec.rb +82 -0
- data/spec/lib/stuff_to_do_issue_patch_spec.rb +60 -0
- data/spec/lib/stuff_to_do_project_patch_spec.rb +50 -0
- data/spec/lib/stuff_to_do_user_patch_spec.rb +8 -0
- data/spec/models/stuff_to_do_filter_spec.rb +3 -0
- data/spec/models/stuff_to_do_mailer_spec.rb +42 -0
- data/spec/models/stuff_to_do_spec.rb +426 -0
- data/spec/sanity_spec.rb +7 -0
- data/spec/spec_helper.rb +130 -0
- metadata +211 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe StuffToDoController, '#save_time_entries' do
|
|
4
|
+
include Redmine::I18n
|
|
5
|
+
integrate_views
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@current_user = mock_model(User, :admin? => false, :logged? => true, :language => :en, :memberships => [], :anonymous? => false, :name => "A Test User", :projects => Project, :allowed_to? => true)
|
|
9
|
+
@current_user.stub!(:time_grid_issues).and_return(Issue)
|
|
10
|
+
User.stub!(:current).and_return(@current_user)
|
|
11
|
+
|
|
12
|
+
@project = mock_model(Project)
|
|
13
|
+
@issue = mock('issue', :project => @project)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def do_request(params={})
|
|
17
|
+
post :save_time_entry, {:format => 'js', :time_entry => []}.merge(params)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def make_time_entry_hash
|
|
21
|
+
{:comments => 'Test comment', :issue_id => '100', :activity_id => '1', :spent_on => '2009-08-05', :hours => '1'}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def make_time_entry_mock(entry)
|
|
25
|
+
TimeEntry.should_receive(:new).with(entry.stringify_keys).and_return do
|
|
26
|
+
te = mock_model(TimeEntry)
|
|
27
|
+
te.stub!(:issue).and_return(@issue) # So it can get the project
|
|
28
|
+
te.should_receive(:project=).with(@project)
|
|
29
|
+
te.stub!(:project).and_return(@project)
|
|
30
|
+
te.should_receive(:user=).with(User.current)
|
|
31
|
+
te.stub!(:comments).and_return(entry[:comments])
|
|
32
|
+
yield te if block_given?
|
|
33
|
+
te
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe 'with a successful save' do
|
|
38
|
+
before(:each) do
|
|
39
|
+
controller.stub!(:save_time_entry_from_time_grid).and_return(true)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it_should_behave_like 'get_time_grid_data'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe 'with a failed save' do
|
|
46
|
+
it 'should render the error messages as a string' do
|
|
47
|
+
do_request
|
|
48
|
+
response.should_not be_success
|
|
49
|
+
response.body.should match(/project/i)
|
|
50
|
+
response.body.should match(/activity/i)
|
|
51
|
+
response.body.should match(/comment/i)
|
|
52
|
+
response.body.should match(/date/i)
|
|
53
|
+
response.body.should match(/hours/i)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe StuffToDoController, '#filters_for_view (private)' do
|
|
4
|
+
it 'should return a StuffToDoFilter' do
|
|
5
|
+
controller.send(:filters_for_view).should be_an_instance_of(StuffToDoFilter)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should include all the Users in users' do
|
|
9
|
+
@user1 = mock_model(User)
|
|
10
|
+
@user2 = mock_model(User)
|
|
11
|
+
users = [@user1, @user2]
|
|
12
|
+
User.should_receive(:active).and_return(users)
|
|
13
|
+
|
|
14
|
+
filters = controller.send(:filters_for_view)
|
|
15
|
+
filters.users.should include(@user1)
|
|
16
|
+
filters.users.should include(@user2)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should include all the IssuePriorities in priorities' do
|
|
20
|
+
@priority1 = mock_model(IssuePriority)
|
|
21
|
+
@priority2 = mock_model(IssuePriority)
|
|
22
|
+
priorities = [@priority1, @priority2]
|
|
23
|
+
IssuePriority.should_receive(:all).and_return(priorities)
|
|
24
|
+
|
|
25
|
+
filters = controller.send(:filters_for_view)
|
|
26
|
+
filters.priorities.should include(@priority1)
|
|
27
|
+
filters.priorities.should include(@priority2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should include all the IssueStatuses in statuses' do
|
|
31
|
+
@status1 = mock_model(IssueStatus)
|
|
32
|
+
@status2 = mock_model(IssueStatus)
|
|
33
|
+
statuses = [@status1, @status2]
|
|
34
|
+
IssueStatus.should_receive(:find).with(:all).and_return(statuses)
|
|
35
|
+
|
|
36
|
+
filters = controller.send(:filters_for_view)
|
|
37
|
+
filters.statuses.should include(@status1)
|
|
38
|
+
filters.statuses.should include(@status2)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe StuffToDoController, '#save_time_entry_from_time_grid (private)' do
|
|
44
|
+
before(:each) do
|
|
45
|
+
@user = mock_model(User)
|
|
46
|
+
User.stub!(:current).and_return(@user)
|
|
47
|
+
@project = mock_model(Project)
|
|
48
|
+
User.current.stub!(:allowed_to?).and_return(true)
|
|
49
|
+
|
|
50
|
+
@time_entry = TimeEntry.new(:comments => 'A comment for validation')
|
|
51
|
+
@time_entry.stub!(:project).and_return(@project)
|
|
52
|
+
@time_entry.stub!(:valid?).and_return(true)
|
|
53
|
+
@time_entry.stub!(:save).and_return(true)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'should check if a TimeEntry is valid' do
|
|
57
|
+
@time_entry.should_receive(:valid?).and_return(true)
|
|
58
|
+
controller.send(:save_time_entry_from_time_grid, @time_entry)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should add an error if comments are blank' do
|
|
62
|
+
@time_entry.stub!(:valid?).and_return(true)
|
|
63
|
+
@time_entry.should_receive(:comments).at_least(:once).and_return('')
|
|
64
|
+
controller.send(:save_time_entry_from_time_grid, @time_entry)
|
|
65
|
+
|
|
66
|
+
@time_entry.errors.should have(1).error_on(:comments)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'should check if the user is allowed to log_time to the project' do
|
|
70
|
+
User.current.should_receive(:allowed_to?).with(:log_time, @project).and_return(true)
|
|
71
|
+
@time_entry.should_receive(:project).and_return(@project)
|
|
72
|
+
@time_entry.should_receive(:save).and_return(true)
|
|
73
|
+
|
|
74
|
+
controller.send(:save_time_entry_from_time_grid, @time_entry)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should save a valid TimeEntry' do
|
|
78
|
+
@time_entry.should_receive(:save).and_return(true)
|
|
79
|
+
controller.send(:save_time_entry_from_time_grid, @time_entry)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Issue, "associations" do
|
|
4
|
+
it 'should have a habtm time_grid_users' do
|
|
5
|
+
Issue.should have_association(:time_grid_users, :has_and_belongs_to_many)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe Issue, 'after_save' do
|
|
10
|
+
it 'should include update_next_issues' do
|
|
11
|
+
callbacks = Issue.after_save
|
|
12
|
+
callbacks.should_not be_nil
|
|
13
|
+
|
|
14
|
+
callbacks.should satisfy do |callbacks|
|
|
15
|
+
found = false
|
|
16
|
+
callbacks.each do |callback|
|
|
17
|
+
found = true if callback.method == :update_next_issues
|
|
18
|
+
end
|
|
19
|
+
found
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe Issue, 'update_next_issues' do
|
|
25
|
+
before(:each) do
|
|
26
|
+
@issue = Issue.new
|
|
27
|
+
@issue.stub!(:reload)
|
|
28
|
+
@issue.stub!(:closed?).and_return(false)
|
|
29
|
+
StuffToDo.stub!(:remove_stale_assignments)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should reload the issue to clear the cache holding its status' do
|
|
33
|
+
@issue.should_receive(:reload)
|
|
34
|
+
@issue.stub!(:closed?).and_return(true)
|
|
35
|
+
@issue.update_next_issues
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'should call StuffToDo#remove_associations_to if the issue is closed' do
|
|
39
|
+
@issue.should_receive(:closed?).and_return(true)
|
|
40
|
+
StuffToDo.should_receive(:remove_associations_to).with(@issue)
|
|
41
|
+
@issue.update_next_issues
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should not call StuffToDo#remove_associations_to if the issue is open' do
|
|
45
|
+
@issue.should_receive(:closed?).and_return(false)
|
|
46
|
+
StuffToDo.should_not_receive(:remove_associations_to)
|
|
47
|
+
@issue.update_next_issues
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'should return true for the callbacks' do
|
|
51
|
+
StuffToDo.stub!(:remove_associations_to)
|
|
52
|
+
|
|
53
|
+
@issue.update_next_issues.should be_true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'should call StuffToDo#remove_stale_assignments' do
|
|
57
|
+
StuffToDo.should_receive(:remove_stale_assignments).with(@issue)
|
|
58
|
+
@issue.update_next_issues
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Project, 'associations' do
|
|
4
|
+
it 'should have many stuff_to_dos as "stuff"' do
|
|
5
|
+
Project.should have_association(:stuff_to_dos, :has_many)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe Project, 'after_save' do
|
|
10
|
+
it 'should include update_stuff_to_do' do
|
|
11
|
+
callbacks = Project.after_save
|
|
12
|
+
callbacks.should_not be_nil
|
|
13
|
+
|
|
14
|
+
callbacks.should satisfy do |callbacks|
|
|
15
|
+
found = false
|
|
16
|
+
callbacks.each do |callback|
|
|
17
|
+
found = true if callback.method == :update_stuff_to_do
|
|
18
|
+
end
|
|
19
|
+
found
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe Project, 'update_stuff_to_do' do
|
|
25
|
+
before(:each) do
|
|
26
|
+
# Can't use a mock here due to a Rails/RSpec conflict
|
|
27
|
+
# https://rails.lighthouseapp.com/projects/8994/tickets/404-named_scope-bashes-critical-methods
|
|
28
|
+
#
|
|
29
|
+
@project = Project.new
|
|
30
|
+
@project.status = Project::STATUS_ACTIVE
|
|
31
|
+
@project.is_public = true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should call StuffToDo#remove_associations_to if the project is not active' do
|
|
35
|
+
@project.status = Project::STATUS_ARCHIVED
|
|
36
|
+
StuffToDo.should_receive(:remove_associations_to).with(@project)
|
|
37
|
+
@project.update_stuff_to_do
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should not call StuffToDo#remove_associations_to if the project is active' do
|
|
41
|
+
StuffToDo.should_not_receive(:remove_associations_to)
|
|
42
|
+
@project.update_stuff_to_do
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should return true for the callbacks' do
|
|
46
|
+
StuffToDo.stub!(:remove_associations_to)
|
|
47
|
+
|
|
48
|
+
@project.update_stuff_to_do.should be_true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe StuffToDoMailer, 'recommended_below_threshold' do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@settings = {'email_to' => 'user1@example.com,user2@example.com', 'threshold' => '5'}
|
|
7
|
+
Setting.stub!(:plugin_stuff_to_do_plugin).and_return(@settings)
|
|
8
|
+
Setting.stub!(:host_name).and_return('example.com')
|
|
9
|
+
Setting.stub!(:protocol).and_return('https')
|
|
10
|
+
@user = mock_model(User, :name => "Example User", :id => 100)
|
|
11
|
+
@next_item_count = 2
|
|
12
|
+
|
|
13
|
+
@mail = StuffToDoMailer.create_recommended_below_threshold(@user, @next_item_count)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should send to the users specified in the Settings' do
|
|
17
|
+
@mail.bcc.should have(2).things
|
|
18
|
+
@mail.bcc.should include("user1@example.com")
|
|
19
|
+
@mail.bcc.should include("user2@example.com")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should use the subject of "Whats Recommended is below the threshold"' do
|
|
23
|
+
@mail.subject.should match(/What's Recommended is below the threshold/i)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'should have the user name in the body' do
|
|
27
|
+
@mail.encoded.should match(/#{ @user.name }/)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should say the threshold amount in the body' do
|
|
31
|
+
@mail.encoded.should match(/threshold of 5/)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should say the number of StuffToDos for the user in the body' do
|
|
35
|
+
@mail.encoded.should match(/only 2 recommended items left/)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'should have a link to the users stuff_to_do page in the body' do
|
|
39
|
+
# '=3D' is the encoded version of '='
|
|
40
|
+
@mail.encoded.should include("https://example.com/stuff_to_do?user_id=3D#{@user.id}")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
module StuffToDoSpecHelper
|
|
4
|
+
def issue_factory(number, fields = { })
|
|
5
|
+
issues = []
|
|
6
|
+
number.times do |issue_number|
|
|
7
|
+
issues << mock_model(Issue, { :id => issue_number }.merge(fields))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
return issues
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def project_factory(number, fields = {})
|
|
14
|
+
projects = []
|
|
15
|
+
number.times do |project_number|
|
|
16
|
+
projects << mock_model(Project, { :id => project_number }.merge(fields))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return projects
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def next_issues_from_issues(issues, number_of_next_issues = nil)
|
|
23
|
+
number_of_next_issues ||= issues.size
|
|
24
|
+
|
|
25
|
+
next_issues = []
|
|
26
|
+
issues.each do |issue|
|
|
27
|
+
next if next_issues.size >= number_of_next_issues
|
|
28
|
+
next_issues << mock_model(StuffToDo, :stuff => issue)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return next_issues
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe StuffToDo, 'associations' do
|
|
36
|
+
it 'should belong to a polymorphic "stuff"' do
|
|
37
|
+
StuffToDo.should have_association(:stuff, :belongs_to)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should belong to a user' do
|
|
41
|
+
StuffToDo.should have_association(:user, :belongs_to)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe StuffToDo, '#available with no filter' do
|
|
46
|
+
|
|
47
|
+
before(:each) do
|
|
48
|
+
@user = mock_model(User)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should find nothing' do
|
|
52
|
+
StuffToDo.available(@user).should be_empty
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe StuffToDo, '#available for user' do
|
|
57
|
+
include StuffToDoSpecHelper
|
|
58
|
+
|
|
59
|
+
before(:each) do
|
|
60
|
+
@user = mock_model(User)
|
|
61
|
+
@find_options = { :conditions => ['1=1 AND (issue_statuses.is_closed = ?) AND (projects.status = ?) AND (assigned_to_id = ?)',false, 1, @user.id ], :include => [:status, :priority, :project], :order => 'issues.created_on DESC'}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should find all assigned issues for the user' do
|
|
65
|
+
issues = issue_factory(10, { :assigned_to => @user })
|
|
66
|
+
|
|
67
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
68
|
+
StuffToDo.available(@user, @user).should eql(issues)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should not include issues that are StuffToDos' do
|
|
72
|
+
issues = issue_factory(10, { :assigned_to => @user })
|
|
73
|
+
# Add in half the issues as StuffToDos
|
|
74
|
+
next_issues = next_issues_from_issues(issues, issues.size / 2)
|
|
75
|
+
|
|
76
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
77
|
+
StuffToDo.should_receive(:find).with(:all, { :conditions => { :user_id => @user.id }}).and_return(next_issues)
|
|
78
|
+
StuffToDo.available(@user, @user).should eql(issues - next_issues.collect(&:stuff))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should only include open issues' do
|
|
82
|
+
issues = issue_factory(10, { :assigned_to => @user })
|
|
83
|
+
|
|
84
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
85
|
+
available = StuffToDo.available(@user, @user)
|
|
86
|
+
available.should have(10).items
|
|
87
|
+
available.should eql(issues)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe StuffToDo, '#available for status' do
|
|
92
|
+
include StuffToDoSpecHelper
|
|
93
|
+
|
|
94
|
+
before(:each) do
|
|
95
|
+
@user = mock_model(User)
|
|
96
|
+
@status = mock_model(IssueStatus)
|
|
97
|
+
@find_options = { :conditions => ['1=1 AND (issue_statuses.is_closed = ?) AND (projects.status = ?) AND (issue_statuses.id = (?))', false, 1, @status.id ], :include => [:status, :priority, :project], :order => 'issues.created_on DESC'}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'should find all issues with the status' do
|
|
101
|
+
issues = issue_factory(10, { :status => @status })
|
|
102
|
+
|
|
103
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
104
|
+
StuffToDo.available(@user, @status).should eql(issues)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'should not include issues that are StuffToDos' do
|
|
108
|
+
issues = issue_factory(10, { :status => @status })
|
|
109
|
+
# Add in half the issues as StuffToDos
|
|
110
|
+
next_issues = next_issues_from_issues(issues, issues.size / 2)
|
|
111
|
+
|
|
112
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
113
|
+
StuffToDo.should_receive(:find).with(:all, { :conditions => { :user_id => @user.id }}).and_return(next_issues)
|
|
114
|
+
StuffToDo.available(@user, @status).should eql(issues - next_issues.collect(&:stuff))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'should only include open issues' do
|
|
118
|
+
issues = issue_factory(10, { :status => @status })
|
|
119
|
+
|
|
120
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
121
|
+
available = StuffToDo.available(@user, @status)
|
|
122
|
+
available.should have(10).items
|
|
123
|
+
available.should eql(issues)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe StuffToDo, '#available for priority' do
|
|
128
|
+
include StuffToDoSpecHelper
|
|
129
|
+
|
|
130
|
+
before(:each) do
|
|
131
|
+
@user = mock_model(User)
|
|
132
|
+
@priority = mock_model(Enumeration, :opt => 'IPRI')
|
|
133
|
+
@find_options = { :conditions => ['1=1 AND (issue_statuses.is_closed = ?) AND (projects.status = ?) AND (enumerations.id = (?))', false, 1, @priority.id ], :include => [:status, :priority, :project], :order => 'issues.created_on DESC'}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should find all issues with the priority' do
|
|
137
|
+
issues = issue_factory(10, { :priority => @priority })
|
|
138
|
+
|
|
139
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
140
|
+
StuffToDo.available(@user, @priority).should eql(issues)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should not include issues that are StuffToDos' do
|
|
144
|
+
issues = issue_factory(10, { :priority => @priority })
|
|
145
|
+
# Add in half the issues as StuffToDos
|
|
146
|
+
next_issues = next_issues_from_issues(issues, issues.size / 2)
|
|
147
|
+
|
|
148
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
149
|
+
StuffToDo.should_receive(:find).with(:all, { :conditions => { :user_id => @user.id }}).and_return(next_issues)
|
|
150
|
+
StuffToDo.available(@user, @priority).should eql(issues - next_issues.collect(&:stuff))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'should only include open issues' do
|
|
154
|
+
issues = issue_factory(10, { :priority => @priority })
|
|
155
|
+
|
|
156
|
+
Issue.should_receive(:find).with(:all, @find_options).and_return(issues)
|
|
157
|
+
available = StuffToDo.available(@user, @priority)
|
|
158
|
+
available.should have(10).items
|
|
159
|
+
available.should eql(issues)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe StuffToDo, '#available for project' do
|
|
164
|
+
include StuffToDoSpecHelper
|
|
165
|
+
|
|
166
|
+
before(:each) do
|
|
167
|
+
@user = mock_model(User)
|
|
168
|
+
@priority = mock_model(Enumeration, :opt => 'IPRI')
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'should find all active projects visible to the user' do
|
|
172
|
+
issues = issue_factory(10, { :priority => @priority })
|
|
173
|
+
projects = project_factory(10)
|
|
174
|
+
projects.should_receive(:sort).and_return(projects) # No need to test the sort order
|
|
175
|
+
|
|
176
|
+
Project.should_receive(:active).and_return(Project)
|
|
177
|
+
Project.should_receive(:visible).and_return(projects)
|
|
178
|
+
|
|
179
|
+
StuffToDo.available(@user, Project.new).should eql(projects)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'should not include projects that are StuffToDos already' do
|
|
183
|
+
projects = project_factory(10)
|
|
184
|
+
projects.stub!(:sort).and_return(projects) # No need to test the sort order
|
|
185
|
+
# Add in half the issues as StuffToDos
|
|
186
|
+
stuff_to_dos = next_issues_from_issues(projects, projects.size / 2)
|
|
187
|
+
|
|
188
|
+
Project.should_receive(:active).and_return(Project)
|
|
189
|
+
Project.should_receive(:visible).and_return(projects)
|
|
190
|
+
StuffToDo.should_receive(:find).with(:all, { :conditions => { :user_id => @user.id }}).and_return(stuff_to_dos)
|
|
191
|
+
|
|
192
|
+
StuffToDo.available(@user, Project.new).should eql(projects - stuff_to_dos.collect(&:stuff))
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
describe StuffToDo, '#remove_associations_to' do
|
|
197
|
+
before(:each) do
|
|
198
|
+
@issue = mock_model(Issue)
|
|
199
|
+
@issue.stub!(:closed?).and_return(true)
|
|
200
|
+
@project = mock_model(Project)
|
|
201
|
+
@project.stub!(:active).and_return(false)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'should delete all StuffToDos for a closed issue' do
|
|
205
|
+
next_issue_one = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => nil)
|
|
206
|
+
next_issue_one.should_receive(:destroy).and_return(true)
|
|
207
|
+
next_issue_two = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => nil)
|
|
208
|
+
next_issue_two.should_receive(:destroy).and_return(true)
|
|
209
|
+
next_issues = [next_issue_one, next_issue_two]
|
|
210
|
+
@issue.should_receive(:stuff_to_dos).and_return(next_issues)
|
|
211
|
+
|
|
212
|
+
StuffToDo.remove_associations_to(@issue)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'should delete all StuffToDos for a archived project' do
|
|
216
|
+
next_issue_one = mock_model(StuffToDo, :stuff_id => @project.id, :user_id => nil)
|
|
217
|
+
next_issue_one.should_receive(:destroy).and_return(true)
|
|
218
|
+
next_issue_two = mock_model(StuffToDo, :stuff_id => @project.id, :user_id => nil)
|
|
219
|
+
next_issue_two.should_receive(:destroy).and_return(true)
|
|
220
|
+
next_issues = [next_issue_one, next_issue_two]
|
|
221
|
+
@project.should_receive(:stuff_to_dos).and_return(next_issues)
|
|
222
|
+
|
|
223
|
+
StuffToDo.remove_associations_to(@project)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
describe StuffToDo, '#remove_associations_to' do
|
|
228
|
+
before(:each) do
|
|
229
|
+
@issue = mock_model(Issue)
|
|
230
|
+
@issue.stub!(:closed?).and_return(true)
|
|
231
|
+
|
|
232
|
+
@user = mock_model(User)
|
|
233
|
+
@next_issue_one = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user.id)
|
|
234
|
+
@next_issue_one.should_receive(:destroy).and_return(true)
|
|
235
|
+
@next_issue_two = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user.id)
|
|
236
|
+
@next_issue_two.should_receive(:destroy).and_return(true)
|
|
237
|
+
@next_issues = [@next_issue_one, @next_issue_two]
|
|
238
|
+
@issue.stub!(:stuff_to_dos).and_return(@next_issues)
|
|
239
|
+
@number_of_next_issues = 4
|
|
240
|
+
StuffToDo.stub!(:count).with(:conditions => { :user_id => @user.id }).and_return(@number_of_next_issues)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'should deliver a StuffToDoMailer notification if the StuffToDos are below the threshold' do
|
|
244
|
+
Setting.should_receive(:plugin_stuff_to_do_plugin).and_return({'threshold' => @number_of_next_issues + 1 })
|
|
245
|
+
User.should_receive(:find_by_id).with(@user.id).and_return(@user)
|
|
246
|
+
StuffToDoMailer.should_receive(:deliver_recommended_below_threshold).with(@user, 4)
|
|
247
|
+
StuffToDo.remove_associations_to(@issue)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it 'should deliver a StuffToDoMailer notification if the StuffToDos are at the threshold' do
|
|
251
|
+
Setting.should_receive(:plugin_stuff_to_do_plugin).and_return({'threshold' => @number_of_next_issues })
|
|
252
|
+
User.should_receive(:find_by_id).with(@user.id).and_return(@user)
|
|
253
|
+
StuffToDoMailer.should_receive(:deliver_recommended_below_threshold).with(@user, 4)
|
|
254
|
+
StuffToDo.remove_associations_to(@issue)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'should not deliver any StuffToDoMailer notification if the StuffToDos are above the threshold' do
|
|
258
|
+
Setting.should_receive(:plugin_stuff_to_do_plugin).and_return({'threshold' => @number_of_next_issues - 1 })
|
|
259
|
+
StuffToDoMailer.should_not_receive(:deliver_recommended_below_threshold)
|
|
260
|
+
StuffToDo.remove_associations_to(@issue)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
describe StuffToDo, '#remove_stale_assignments' do
|
|
266
|
+
it 'should destroy all StuffToDos for an issue except for the currently assigned user' do
|
|
267
|
+
@user = mock_model(User)
|
|
268
|
+
@user2 = mock_model(User)
|
|
269
|
+
@user3 = mock_model(User)
|
|
270
|
+
|
|
271
|
+
@issue = mock_model(Issue, :assigned_to_id => @user.id)
|
|
272
|
+
|
|
273
|
+
@next_issue_one = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user2.id)
|
|
274
|
+
@next_issue_two = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user3.id)
|
|
275
|
+
@next_issues = [@next_issue_one, @next_issue_two]
|
|
276
|
+
|
|
277
|
+
StuffToDo.should_receive(:destroy_all).with(['stuff_id = (?) AND user_id NOT IN (?)', @issue.id, @issue.assigned_to_id]).and_return(@next_issues)
|
|
278
|
+
|
|
279
|
+
StuffToDo.remove_stale_assignments(@issue)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'should destroy all StuffToDos for an issue if the currently assigned user is blank' do
|
|
283
|
+
@user = mock_model(User)
|
|
284
|
+
@user2 = mock_model(User)
|
|
285
|
+
@user3 = mock_model(User)
|
|
286
|
+
|
|
287
|
+
@issue = mock_model(Issue, :assigned_to_id => nil)
|
|
288
|
+
|
|
289
|
+
@next_issue_one = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user2.id)
|
|
290
|
+
@next_issue_two = mock_model(StuffToDo, :stuff_id => @issue.id, :user_id => @user3.id)
|
|
291
|
+
@next_issues = [@next_issue_one, @next_issue_two]
|
|
292
|
+
|
|
293
|
+
StuffToDo.should_receive(:destroy_all).with(['stuff_id = (?)', @issue.id]).and_return(@next_issues)
|
|
294
|
+
|
|
295
|
+
StuffToDo.remove_stale_assignments(@issue)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
describe StuffToDo, '#reorder_list' do
|
|
302
|
+
it 'should require a user_id' do
|
|
303
|
+
lambda {
|
|
304
|
+
StuffToDo.reorder_list
|
|
305
|
+
}.should raise_error
|
|
306
|
+
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it 'should require an array of ids' do
|
|
310
|
+
user = mock_model(User)
|
|
311
|
+
lambda {
|
|
312
|
+
StuffToDo.reorder_list(user)
|
|
313
|
+
}.should raise_error
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it 'should find all the Stuff To Do items' do
|
|
317
|
+
user = mock_model(User)
|
|
318
|
+
ids = ["598", "709", "746", "1492", "1491", "820", "1094", "1095"]
|
|
319
|
+
stuff_to_dos = []
|
|
320
|
+
ids.each do |id|
|
|
321
|
+
stuff_to_dos << mock_model(StuffToDo, :stuff_id => id, :insert_at => true)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
325
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
326
|
+
StuffToDo.reorder_list(user, ids)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
it 'should save the positions of the stuff to do items to the database' do
|
|
331
|
+
user = mock_model(User)
|
|
332
|
+
ids = ["598", "709", "746", "1492", "1491", "820", "1094", "1095"]
|
|
333
|
+
stuff_to_dos = []
|
|
334
|
+
ids.each_with_index do |id, array_position|
|
|
335
|
+
stuff_to_do = mock_model(StuffToDo, :stuff_id => id, :id => id)
|
|
336
|
+
stuff_to_do.should_receive(:insert_at).with(array_position + 1)
|
|
337
|
+
stuff_to_dos << stuff_to_do
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
341
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
342
|
+
StuffToDo.reorder_list(user, ids)
|
|
343
|
+
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it 'should add new StuffToDo that are in the list but not in the database' do
|
|
347
|
+
user = mock_model(User)
|
|
348
|
+
ids = ["598", "709", "746", "1492", "1491", "820", "1094", "1095"]
|
|
349
|
+
stuff_to_dos = []
|
|
350
|
+
ids.each_with_index do |id, array_position|
|
|
351
|
+
stuff_to_dos << mock_model(StuffToDo, :stuff_id => id, :insert_at => true) unless id == "820"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
stuff_to_do_for_820 = StuffToDo.new
|
|
355
|
+
stuff_to_do_for_820.should_receive(:stuff_id=).with(820)
|
|
356
|
+
stuff_to_do_for_820.should_receive(:user_id=).with(user.id)
|
|
357
|
+
stuff_to_do_for_820.should_receive(:save).and_return(true)
|
|
358
|
+
position = ids.index("820") + 1
|
|
359
|
+
stuff_to_do_for_820.should_receive(:insert_at).with(position)
|
|
360
|
+
StuffToDo.should_receive(:new).and_return(stuff_to_do_for_820)
|
|
361
|
+
|
|
362
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
363
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
364
|
+
StuffToDo.reorder_list(user, ids)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it 'should support adding new Project StuffToDo items' do
|
|
368
|
+
user = mock_model(User)
|
|
369
|
+
ids = ["598", "709", "746", "1492", "1491", "820", "1094", "1095"]
|
|
370
|
+
stuff_to_dos = []
|
|
371
|
+
ids.each_with_index do |id, array_position|
|
|
372
|
+
stuff_to_do = mock_model(StuffToDo, :stuff_id => id, :id => id)
|
|
373
|
+
stuff_to_do.should_receive(:insert_at).with(array_position + 1)
|
|
374
|
+
stuff_to_dos << stuff_to_do
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Project 42
|
|
378
|
+
ids << "project42"
|
|
379
|
+
new_project_stuff_to_do = mock_model(StuffToDo, :user_id => user.id, :insert_at => ids.size)
|
|
380
|
+
new_project_stuff_to_do.should_receive(:stuff_id=).with(42)
|
|
381
|
+
new_project_stuff_to_do.should_receive(:stuff_type=).with('Project')
|
|
382
|
+
new_project_stuff_to_do.should_receive(:user_id=).with(user.id)
|
|
383
|
+
new_project_stuff_to_do.should_receive(:save).and_return(true)
|
|
384
|
+
StuffToDo.should_receive(:new).and_return(new_project_stuff_to_do)
|
|
385
|
+
|
|
386
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
387
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
388
|
+
StuffToDo.reorder_list(user, ids)
|
|
389
|
+
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it 'should destroy any StuffToDos that are not in the list' do
|
|
393
|
+
user = mock_model(User)
|
|
394
|
+
ids = ["598", "709", "746", "1492", "1491", "820", "1094", "1095"]
|
|
395
|
+
stuff_to_dos = []
|
|
396
|
+
ids.each_with_index do |id, array_position|
|
|
397
|
+
stuff_to_dos << mock_model(StuffToDo, :stuff_id => id, :insert_at => true)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
extra_stuff_to_do = mock_model(StuffToDo, :stuff_id => 999)
|
|
401
|
+
extra_stuff_to_do.should_receive(:destroy).and_return(true)
|
|
402
|
+
StuffToDo.should_receive(:find_by_user_id_and_stuff_id).with(user.id, extra_stuff_to_do.stuff_id).and_return(extra_stuff_to_do)
|
|
403
|
+
stuff_to_dos << extra_stuff_to_do
|
|
404
|
+
|
|
405
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
406
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
407
|
+
StuffToDo.reorder_list(user, ids)
|
|
408
|
+
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it 'should destroy all StuffToDos if the list is empty' do
|
|
412
|
+
user = mock_model(User)
|
|
413
|
+
ids = nil
|
|
414
|
+
stuff_to_dos = []
|
|
415
|
+
|
|
416
|
+
extra_stuff_to_do = mock_model(StuffToDo, :stuff_id => 999)
|
|
417
|
+
extra_stuff_to_do.should_receive(:destroy).and_return(true)
|
|
418
|
+
StuffToDo.should_receive(:find_by_user_id_and_stuff_id).with(user.id, extra_stuff_to_do.stuff_id).and_return(extra_stuff_to_do)
|
|
419
|
+
stuff_to_dos << extra_stuff_to_do
|
|
420
|
+
|
|
421
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Issue').and_return(stuff_to_dos)
|
|
422
|
+
StuffToDo.should_receive(:find_all_by_user_id_and_stuff_type).with(user.id, 'Project').and_return([])
|
|
423
|
+
StuffToDo.reorder_list(user, ids)
|
|
424
|
+
|
|
425
|
+
end
|
|
426
|
+
end
|