Merge branch 'master' into rails4

Conflicts:
	Gemfile.lock
	config/routes.rb
This commit is contained in:
Reinier Balt 2013-06-17 09:25:02 +02:00
commit 4a485558e2
51 changed files with 5475 additions and 5015 deletions

2
.gitignore vendored
View file

@ -3,6 +3,8 @@
.dotest
.idea
.rvmrc
.ruby-gemset
.ruby-version
.sass-cache/
.yardoc
/.bundle

View file

@ -9,9 +9,15 @@ GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
<<<<<<< HEAD
aasm (3.0.18)
actionmailer (4.0.0.rc1)
actionpack (= 4.0.0.rc1)
=======
aasm (3.0.19)
actionmailer (3.2.13)
actionpack (= 3.2.13)
>>>>>>> master
mail (~> 2.5.3)
actionpack (4.0.0.rc1)
activesupport (= 4.0.0.rc1)
@ -36,7 +42,11 @@ GEM
tzinfo (~> 0.3.37)
acts_as_list (0.2.0)
activerecord (>= 3.0)
<<<<<<< HEAD
arel (4.0.0)
=======
arel (3.0.2)
>>>>>>> master
aruba (0.5.3)
childprocess (>= 0.3.6)
cucumber (>= 1.1.1)
@ -73,6 +83,10 @@ GEM
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
<<<<<<< HEAD
=======
rails (~> 3.0)
>>>>>>> master
database_cleaner (1.0.1)
diff-lcs (1.2.4)
erubis (2.7.0)
@ -83,33 +97,49 @@ GEM
factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0)
railties (>= 3.0.0)
ffi (1.8.1)
ffi (1.9.0)
gherkin (2.12.0)
multi_json (~> 1.3)
hike (1.2.2)
hike (1.2.3)
htmlentities (4.3.1)
<<<<<<< HEAD
i18n (0.6.4)
jquery-rails (2.2.1)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
=======
i18n (0.6.1)
journey (1.0.4)
jquery-rails (3.0.1)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.0)
>>>>>>> master
libv8 (3.11.8.17)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.23)
<<<<<<< HEAD
minitest (4.7.4)
mocha (0.14.0)
metaclass (~> 0.0.1)
multi_json (1.7.3)
=======
mocha (0.14.0)
metaclass (~> 0.0.1)
multi_json (1.7.6)
>>>>>>> master
mysql2 (0.3.11)
nokogiri (1.5.9)
nokogiri (1.5.10)
polyglot (0.3.3)
rack (1.5.2)
rack-mini-profiler (0.1.26)
rack (>= 1.1.3)
rack-test (0.6.2)
rack (>= 1.0)
<<<<<<< HEAD
rails (4.0.0.rc1)
actionmailer (= 4.0.0.rc1)
actionpack (= 4.0.0.rc1)
@ -123,23 +153,49 @@ GEM
railties (4.0.0.rc1)
actionpack (= 4.0.0.rc1)
activesupport (= 4.0.0.rc1)
=======
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
bundler (~> 1.0)
railties (= 3.2.13)
rails_autolink (1.1.0)
rails (> 3.1)
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
>>>>>>> master
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (10.0.4)
<<<<<<< HEAD
=======
rdoc (3.12.2)
json (~> 1.4)
>>>>>>> master
ref (1.0.5)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rubyzip (0.9.9)
safe_yaml (0.9.1)
safe_yaml (0.9.3)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.2.9)
<<<<<<< HEAD
sass-rails (4.0.0.rc1)
railties (>= 4.0.0.beta, < 5.0)
=======
sass-rails (3.2.6)
railties (~> 3.2.0)
>>>>>>> master
sass (>= 3.1.10)
sprockets-rails (~> 2.0.0.rc0)
tilt (~> 1.3)
selenium-webdriver (2.32.1)
selenium-webdriver (2.33.0)
childprocess (>= 0.2.5)
multi_json (~> 1.0)
rubyzip
@ -172,7 +228,7 @@ GEM
tolk (1.3.9)
safe_yaml (~> 0.8)
will_paginate
treetop (1.4.12)
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.37)

View file

@ -34,8 +34,9 @@ var TracksForm = {
$('#default_project_name_id').val(name);
$('#project_name').html(name);
},
set_tag_list: function (name) {
set_tag_list_and_default_tag_list: function (name) {
$('input#tag_list').val(name);
$('input#initial_tag_list').val(name);
},
set_tag_list_for_multi_add: function (name) {
$('#multi_tag_list').val(name);

View file

@ -21,6 +21,7 @@ class ApplicationController < ActionController::Base
before_filter :set_time_zone
before_filter :set_zindex_counter
before_filter :set_locale
append_before_filter :set_group_view_by
prepend_before_filter :login_required
prepend_before_filter :enable_mobile_content_negotiation
after_filter :set_charset
@ -291,4 +292,8 @@ class ApplicationController < ActionController::Base
render :template => 'todos/done'
end
def set_group_view_by
@group_view_by = params['_group_view_by'] || cookies['group_view_by'] || 'context'
end
end

View file

@ -42,6 +42,7 @@ class ContextsController < ApplicationController
@max_completed = current_user.prefs.show_number_completed
@done = @context.todos.completed.limit(@max_completed).reorder("todos.completed_at DESC, todos.created_at DESC").includes(Todo::DEFAULT_INCLUDES)
@not_done_todos = @context.todos.active.reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").includes(Todo::DEFAULT_INCLUDES)
@todos_without_project = @not_done_todos.select{|t| t.project.nil?}
@deferred_todos = @context.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
@pending_todos = @context.todos.pending.includes(Todo::DEFAULT_INCLUDES)
@ -49,6 +50,9 @@ class ContextsController < ApplicationController
@projects = current_user.projects
@contexts = current_user.contexts
@projects_to_show = @projects.active
@contexts_to_show = [@context]
@count = @not_done_todos.count + @deferred_todos.count + @pending_todos.count
@page_title = "TRACKS::Context: #{@context.name}"
respond_to do |format|

View file

@ -47,29 +47,6 @@ class LoginController < ApplicationController
logout_user
end
def expire_session
# this is a hack to enable cucumber to expire a session by calling this
# method. The method will be unavailable for production environment
@user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
unless Rails.env.production?
session['expiry_time'] = Time.now
respond_to do |format|
format.html { render :text => "Session expired for test purposes"}
format.js { render :text => "" }
end
else
respond_to do |format|
format.html { render :text => "Not available for production use"}
format.js { render :text => "" }
end
end
end
def check_expiry
# Gets called by periodically_call_remote to check whether
# the session has timed out yet

View file

@ -131,12 +131,14 @@ class ProjectsController < ApplicationController
@not_done_todos = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES)
@deferred_todos = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
@pending_todos = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES)
@contexts_to_show = current_user.contexts.active
@projects_to_show = [@project]
@done = {}
@done = @project.todos.completed.
reorder("todos.completed_at DESC").
limit(current_user.prefs.show_number_completed).
includes(Todo::DEFAULT_INCLUDES) unless current_user.prefs.show_number_completed == 0
includes(Todo::DEFAULT_INCLUDES) unless @max_completed == 0
@count = @not_done_todos.size
@down_count = @count + @deferred_todos.size + @pending_todos.size

View file

@ -3,7 +3,6 @@ class TodosController < ApplicationController
skip_before_filter :login_required, :only => [:index, :tag]
prepend_before_filter :login_or_feed_token_required, :only => [:index, :tag]
append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred]
append_before_filter :set_group_view_by, :only => [:index, :tag, :create, :list_deferred, :destroy, :defer, :update, :toggle_check]
protect_from_forgery :except => :check_deferred
@ -595,12 +594,7 @@ class TodosController < ApplicationController
@page_title = t('todos.tagged_page_title', :tag_name => @tag_title)
@source_view = params['_source_view'] || 'tag'
if mobile?
# mobile tags are routed with :name ending on .m. So we need to chomp it
@tag_name = @tag_name.chomp('.m')
else
init_data_for_sidebar
end
init_data_for_sidebar unless mobile?
todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr)
@ -833,8 +827,9 @@ class TodosController < ApplicationController
end
def get_params_for_tag_view
# use sanitize to prevent XSS attacks
filter_format_for_tag_view
# use sanitize to prevent XSS attacks
@tag_expr = []
@tag_expr << sanitize(params[:name]).split(',')
@tag_expr << sanitize(params[:and]).split(',') if params[:and]
@ -850,6 +845,27 @@ class TodosController < ApplicationController
@tag_title = @single_tag ? @tag_name : tag_title(@tag_expr)
end
def filter_format_for_tag_view
# routes for tag view do not set :format
if params[:name] =~ /.*\.m$/
set_format_for_tag_view(:m)
elsif params[:name] =~ /.*\.txt$/
set_format_for_tag_view(:txt)
# set content-type to text/plain or it remains text/html
response.headers["Content-Type"] = 'text/plain'
elsif params[:format].nil?
# if no format is given, default to html
# note that if url has ?format=m, we should not overwrite it here
request.format, params[:format] = :html, :html
end
end
def set_format_for_tag_view(format)
# tag name ends with .m, set format to :m en remove .m from name
request.format, params[:format] = format, format
params[:name] = params[:name].chomp(".#{format.to_s}")
end
def get_ids_from_tag_expr(tag_expr)
ids = []
tag_expr.each do |tag_list|

View file

@ -21,13 +21,13 @@ module TodosHelper
end
end
def show_grouped_todos
def show_grouped_todos(settings = {})
collection = (@group_view_by == 'context') ? @contexts_to_show : @projects_to_show
render(:partial => collection, :locals => { :settings => {
render(:partial => collection, :locals => { :settings => settings.reverse_merge!({
:collapsible => true,
:show_empty_containers => @show_empty_containers,
:parent_container_type => @group_view_by
}})
})})
end
def default_collection_settings
@ -80,14 +80,14 @@ module TodosHelper
:locals => {:settings => settings.reverse_merge!(default_collection_settings)}
end
def show_todos_without_project(todos_without_project)
def show_todos_without_project(todos_without_project, settings = {})
render :partial => 'todos/collection',
:object => todos_without_project,
:locals => {:settings => {
:locals => {:settings => settings.reverse_merge!({
:collapsible => true,
:container_name => "without_project",
:parent_container_type => "home"
}
})
}
end
@ -480,7 +480,7 @@ module TodosHelper
def should_show_new_item(todo = @todo)
return false if todo.nil?
source_view do |page|
page.todo { return !todo.hidden? }
page.todo { return !todo.hidden? && !todo.deferred? }
page.deferred { return todo.deferred? || todo.pending? }
page.context { return show_todo_on_current_context_page && todo_should_not_be_hidden_on_context_page }
page.tag { return todo.has_tag?(@tag_name) }
@ -512,18 +512,18 @@ module TodosHelper
end
def update_needs_to_hide_container
if source_view_is(:tag)
if source_view_is_one_of(:tag, :context, :project)
return @remaining_in_context == 0 && (
todo_moved_out_of_container ||
(@todo_hidden_state_changed && @todo.hidden?) ||
@todo_was_deferred_from_active_state ||
@tag_was_removed ||
@todo_was_destroyed ||
(@todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden))
(@todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden || @original_item_was_pending))
)
end
return false if source_view_is_one_of(:project, :calendar, :done, :context)
return false if source_view_is_one_of(:calendar, :done)
return @remaining_in_context == 0
end
@ -599,7 +599,7 @@ module TodosHelper
end
def todo_container_empty_id(todo)
raise Exception.new, "no todo set in TodosHelper::todo_container_empty_id. You probably not assign @original_item" if !todo
raise Exception.new, "no todo set in TodosHelper::todo_container_empty_id. You probably did not assign @original_item" if !todo
@group_view_by == "project" ? project_container_empty_id(todo) : context_container_empty_id(todo)
end
@ -609,8 +609,7 @@ module TodosHelper
return "#{@new_due_id}_container" if source_view_is :calendar
return "deferred_pending_container" if !source_view_is(:todo) && (todo.deferred? || todo.pending?)
return "completed_container" if todo.completed?
return "p#{todo.project_id}" if source_view_is :project
return project_container_id(todo) if source_view_is_one_of(:todo, :tag) && @group_view_by == 'project'
return project_container_id(todo) if source_view_is_one_of(:todo, :tag, :project, :context) && @group_view_by == 'project'
return context_container_id(todo)
end
@ -620,7 +619,7 @@ module TodosHelper
source_view do |page|
page.project {
return "deferred_pending_container-empty-d" if empty_criteria_met
return project_container_empty_id(todo)
return todo_container_empty_id(todo)
}
page.tag {
return "deferred_pending_container-empty-d" if empty_criteria_met
@ -633,7 +632,7 @@ module TodosHelper
}
page.context {
return "deferred_pending_container-empty-d" if empty_criteria_met
return context_container_empty_id(todo)
return todo_container_empty_id(todo)
}
page.todo {
return todo_container_empty_id(todo)

View file

@ -539,14 +539,13 @@ class RecurringTodo < ActiveRecord::Base
end
def get_monthly_date(previous)
start = determine_start(previous)
day = self.every_other1
n = self.every_other2
case self.recurrence_selector
when 0 # specific day of the month
if start.mday >= day
if start.mday > day
# there is no next day n in this month, search in next month
#
# start += n.months

View file

@ -99,7 +99,7 @@ class Todo < ActiveRecord::Base
end
event :block do
transitions :to => :pending, :from => [:active, :deferred]
transitions :to => :pending, :from => [:active, :deferred, :project_hidden]
end
end
@ -152,6 +152,14 @@ class Todo < ActiveRecord::Base
return !( uncompleted_predecessors.empty? || state == 'project_hidden' )
end
def guard_for_transition_from_deferred_to_pending
no_uncompleted_predecessors? && not_part_of_hidden_container?
end
def not_part_of_hidden_container?
!( (self.project && self.project.hidden?) || self.context.hidden? )
end
# Returns a string with description <context, project>
def specification
project_name = self.project.is_a?(NullProject) ? "(none)" : self.project.name
@ -195,7 +203,7 @@ class Todo < ActiveRecord::Base
def remove_predecessor(predecessor)
self.predecessors.delete(predecessor)
if self.predecessors.empty?
self.activate!
self.not_part_of_hidden_container? ? self.activate! : self.hide!
else
save!
end

View file

@ -3,7 +3,7 @@
# invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo
cache [context, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] do
%>
-%>
<%=
render :partial => 'todos/collection',
:object => @not_done,

View file

@ -1,7 +1,7 @@
<div class="list-stategroup-contexts-container" id="list-<%= state %>-contexts-container">
<h2>
<span id="<%= state %>-contexts-count" class="badge"><%= context_state_group.length %></span>
<%= t("states."+ state +"_plural")%> <%= t('common.contexts') %>
<%= t("states.contexts."+ state) %>
</h2>
<div id="<%= state%>-contexts-empty-nd" style="<%= no_contexts ? 'display:block' : 'display:none'%>">

View file

@ -2,9 +2,17 @@
suffix_completed = t('contexts.last_completed_in_context', :number=>prefs.show_number_completed)
deferred_pending_options = {:append_descriptor => nil, :parent_container_type => 'context'}
done_todo_options = {:append_descriptor => suffix_completed, :suppress_context => true, :parent_container_type => 'context'}
show_empty_containers = (@group_view_by == 'context')
-%>
<div id="display_box">
<%= render :partial => @context, :locals => { :settings => {:collapsible => false, :show_empty_containers => true, :parent_container_type => 'context' }} %>
<%= empty_message_holder("not_done_project", @not_done_todos.empty?) %>
<%= show_grouped_todos({:collapsible => false, :show_empty_containers => show_empty_containers, :parent_container_type => 'context'}) %>
<% if @group_view_by == 'project' -%>
<%= show_todos_without_project(@todos_without_project, {:collapsible => false, :parent_container_type => 'context', :title_param => @context.name}) -%>
<% end -%>
<%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %>

View file

@ -46,10 +46,6 @@
</h1>
</div>
<div id="minilinks">
<%= link_to(t('layouts.toggle_contexts'), "#", {:title => t('layouts.toggle_contexts_title'), :id => "toggle-contexts-nav"}) %>
&nbsp;|&nbsp;
<%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %>
&nbsp;|&nbsp;
<%= link_to("#{t('common.logout')} (#{current_user.display_name}) &raquo;".html_safe, logout_path) %>
</div>
<div id="navcontainer">
@ -66,13 +62,15 @@
<li><%= navigation_link( t('layouts.navigation.recurring_todos'), {:controller => "recurring_todos", :action => "index"}, :title => t('layouts.navigation.recurring_todos_title')) %></li>
</ul>
</li>
<li><a href="#"><%= t('layouts.navigation.view') %></a>
<li id="menu_view"><a href="#" id="menu_view_link"><%= t('layouts.navigation.view') %></a>
<ul>
<li><%= navigation_link( t('layouts.navigation.calendar'), calendar_path, :title => t('layouts.navigation.calendar_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.feeds'), feeds_path, :title => t('layouts.navigation.feeds_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.stats'), stats_path, :title => t('layouts.navigation.stats_title')) %></li>
<li><hr/></li>
<li id="menu_view_toggle_contexts"><%= link_to(t('layouts.toggle_contexts'), "#", {:title => t('layouts.toggle_contexts_title'), :id => "toggle-contexts-nav"}) %></li>
<li><%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %></li>
<%= group_view_by_menu_entry %>
</ul>
</li>

View file

@ -2,11 +2,8 @@
@not_done = @not_done_todos.select {|t| t.project_id == project.id }
# invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo
cache [project, @source_view, current_user.date.strftime("%Y%m%d")] do
cache [project, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] do
-%>
<% if source_view_is :project -%>
<%= render :partial => "project_settings_container", :locals => {:project => project} %>
<% end -%>
<%=
title = source_view_is(:project) ? t('projects.actions_in_project_title') : show_project_name(project)

View file

@ -8,14 +8,9 @@
<h2>
<span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length%><%= total_count_string%></span>
<%= t('common.last' ) unless ( ['review','stalled','blocked','current'].include?(state) )%>
<% if (I18n.locale == :fr) %>
<%= t('common.projects').downcase %>
<%= t('states.'+state+'_plural' ).downcase %><%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
<% else %>
<%= t('states.'+state+'_plural' )%>
<%= t('common.projects') %><%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
<% end %>
<%= t('common.last' ) if state == 'completed' %>
<%= t('states.projects.'+state) %>
<%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
</h2>
<% unless suppress_sort_menu %>

View file

@ -5,11 +5,19 @@
:suppress_project => true,
:parent_container_type => 'project'
}
if @not_done_todos.count == 0
# force project view so one empty container will be shown with an empty message
@group_view_by = 'project'
end
-%>
<div id="display_box">
<%= project_next_prev %>
<%= render :partial => @project, :locals => {:settings => {:collapsible => false, :show_empty_containers => true, :parent_container_type => 'project' }} %>
<%= render :partial => "project_settings_container", :locals => {:project => @project} %>
<%= empty_message_holder("not_done_context", @not_done_todos.empty?) %>
<%= show_grouped_todos({:collapsible => false, :show_empty_containers => false, :parent_container_type => 'project' }) %>
<%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %>

View file

@ -36,7 +36,7 @@ function update_project_page() {
TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>");
<% end %>
<% if @project.default_tags %>
TracksForm.set_tag_list("<%= escape_javascript(@project.default_tags)%>");
TracksForm.set_tag_list_and_default_tag_list("<%= escape_javascript(@project.default_tags)%>");
<% end %>
TracksPages.update_sidebar(html_for_sidebar());
}

View file

@ -84,12 +84,12 @@
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2))) %><br/>
</div>
<div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.show_option_always') %>
<label><%= t('todos.recurrence.recurrence_on.options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%>
<%= raw t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3})) %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.from_tickler') %><br/>
<%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3})) %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
<br/>
</div>
<% end %>

View file

@ -76,13 +76,13 @@
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month))) %><br/>
</div>
<div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.show_option_always') %>
<label><%= t('todos.recurrence.recurrence_on.options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false)%>
<%= raw t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3})) %>
<%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3})) %>
<br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.from_tickler') %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
<br/>
</div>

View file

@ -31,7 +31,7 @@
$('#todo-form-new-action').clearDeps();
TracksForm.set_context_name('<%=escape_javascript @initial_context_name%>');
TracksForm.set_project_name('<%=escape_javascript @initial_project_name%>');
TracksForm.set_tag_list('<%=escape_javascript @initial_tags%>');
TracksForm.set_tag_list_and_default_tag_list('<%=escape_javascript @initial_tags%>');
$('#todo-form-new-action input:text:first').focus();
$('#new_todo_starred_link .todo_star').removeClass('starred');
$('#new_todo_starred').val('false');

View file

@ -15,7 +15,7 @@
animation << "add_todo_to_container" unless source_view_is(:done)
animation << "block_predecessors"
end
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred)
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred, :project, :context)
animation << "regenerate_predecessor_family"
else
animation << "replace_todo"
@ -63,7 +63,6 @@ function replace_todo(next_steps) {
}
function add_todo_to_container(next_steps) {
// <%= @group_view_by %>
$('#<%= item_container_id(@todo) %>_items').append(html_for_todo());
<% if should_make_context_visible -%>
$('#<%= item_container_id(@todo) %>').slideDown(500, function() {
@ -120,7 +119,7 @@ function highlight_updated_todo(next_steps) {
function activate_pending_todos(next_steps) {
<% # Activate pending todos that are successors of the completed
if @saved && @pending_to_activate
if @pending_to_activate
# do not render the js in case of an error or if no todos to activate
@pending_to_activate.each do |t|
html = escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))
@ -173,7 +172,7 @@ end
function html_for_recurring_todo() {
<%-
js = @saved && @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : ""
js = @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : ""
-%>
return "<%= js %>";
}
@ -181,7 +180,7 @@ function html_for_recurring_todo() {
function html_for_todo() {
<%-
js = ""
if @saved && !source_view_is(:done)
if !source_view_is(:done)
js = escape_javascript(render(
:partial => @todo,
:locals => {

View file

@ -20,7 +20,7 @@
end
animation << "hide_container" if update_needs_to_hide_container
animation << "highlight_updated_todo"
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred)
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred, :project, :context)
animation << "update_predecessors"
%>
TracksPages.page_notify('notice', '<%=escape_javascript @status_message%>', 5);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -333,6 +333,14 @@ en:
hide_action_form_title: Hide new action form
make_actions_dependent: Make actions dependent on each other
states:
contexts:
hidden: Hidden contexts
active: Active contexts
closed: Closed contexts
projects:
hidden: Hidden projects
active: Active projects
closed: Closed projects
hidden_plural: Hidden
completed: Completed
completed_plural: Completed
@ -427,6 +435,8 @@ en:
completed: Currently there are no completed actions
title: No actions found
not_done_with_tag: "Currently there are no incomplete actions with the tag '%{param}'"
not_done_project: Currently there are no incomplete actions in this project
not_done_context: Currently there are no incomplete actions in this context
completed_recurring: Currently there are no completed recurring todos
not_done: Currently there are no incomplete actions
project: Currently there are no incomplete actions in this project
@ -444,6 +454,7 @@ en:
home_completed: Completed actions
tag_completed: "Completed actions tagged with '%{param}'"
home_without_project: "Actions without project"
context_without_project: "Actions without project in %{param}"
project_project: "Actions in this project"
project_deferred_pending: Deferred/pending actions in this project
context_deferred_pending: Deferred/pending actions in this context
@ -584,7 +595,6 @@ en:
ends_on_number_times: Ends after %{number} times
ends_on_date: Ends on %{date}
every_work_day: Every work day
recurrence_on_due_date: the date that the todo is due
weekly_options: Settings for weekly recurring actions
weekly: Weekly
monthly_options: Settings for monthly recurring actions
@ -633,16 +643,18 @@ en:
due: due
until: until
every_month: every month
show_option_always: always
daily: Daily
yearly_every_x_day: Every %{month} %{day}
recurrence_on_options: Set recurrence on
recurrence_on:
options: Use the calculated date to
due_date: set the actions due date
show_options: Show the action
show_always: always
show_days_before: "not until %{days} days before the due date"
from_tickler: set the date the action should be shown (do not set a due date)
daily_every_number_day: Every %{number} day(s)
show_options: Show the todo
weekly_every_number_week: Returns every %{number} week on
ends_on: Ends on
show_days_before: "%{days} days before the todo is due"
from_tickler: the date todo comes from tickler (no due date set)
no_end_date: No end date
day_x_on_every_x_month: Day %{day} on every %{month} month
yearly_options: Settings for yearly recurring actions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@ Tracksapp::Application.routes.draw do
post 'login' => 'login#login'
get 'login' => 'login#login'
get 'login/expire_session' => 'login#expire_session'
get 'login/check_expiry' => 'login#check_expiry'
get 'logout' => 'login#logout'
@ -102,9 +101,13 @@ Tracksapp::Application.routes.draw do
post 'add_predecessor'
end
end
get 'todos/tag/:name' => 'todos#tag', :as => :tag
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
# match /todos/tag and put everything in :name, including extensions like .m and .txt.
# This means the controller action needs to parse the extension and set format/content type
# Needed for /todos/tag/first.last.m to work
get 'todos/tag/:name' => 'todos#tag', :as => :tag, :format => false, :name => /.*/
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
get 'todos/done/tag/:name' => "todos#done_tag", :as => :done_tag
get 'todos/all_done/tag/:name' => "todos#all_done_tag", :as => :all_done_tag
get 'auto_complete_for_predecessor' => 'todos#auto_complete_for_predecessor'

View file

@ -16,10 +16,23 @@
== Version 2.3devel
New and changed features
* you can select to group todos on the home page by context or by
project (using the view menu). This also works for tag page
* You can select to group todos on the home page by context or by
project (using the view menu). This also works for tag page, the project page,
the tickler and the context page
* You can now change the state of a context to closed
* Czech locale has been renamed from cz to cs to follow ISO standards
* The toggle-notes and toggle-collapsed-containers have been moved into the view
menu
* Bugfixes
* Tracks is tested on Ruby 1.9.3 and on Ruby 2.0
Removed features
* This version of Tracks dropped support for Ruby 1.8.x. Please use Ruby 1.9 or
Ruby 2.0
Under the hood
* Upgrade to Rails 3.213
* Several refactorings al over the place for easier maintenance
== Version 2.2.2

View file

@ -68,10 +68,10 @@ Feature: dependencies
And "test 2" depends on "test 1"
When I go to the "dependencies" project
Then I should see "test 2" in the deferred container
And I should see "test 1" in the action container
And I should see "test 1" in the context container for "@pc"
When I mark "test 1" as complete
Then I should see "test 1" in the completed container
And I should see "test 2" in the action container
And I should see "test 2" in the context container for "@pc"
And I should not see "test 2" in the deferred container
And I should see empty message for deferred todos of project
@ -86,9 +86,9 @@ Feature: dependencies
And "test 2" depends on "test 1"
When I go to the "dependencies" project
Then I should see "test 2" in the deferred container
And I should see "test 1" in the action container
And I should see "test 1" in the context container for "@pc"
When I delete the action "test 1"
Then I should see "test 2" in the action container
Then I should see "test 2" in the context container for "@pc"
And I should not see "test 2" in the deferred container
And I should see empty message for deferred todos of project
@ -122,7 +122,7 @@ Feature: dependencies
When I go to the "dependencies" project
And I drag "test 1" to "test 3"
Then I should see an error flash message saying "Cannot add this action as a dependency to a completed action!"
And I should see "test 1" in the project container of "dependencies"
And I should see "test 1" in the context container for "@pc"
@javascript
Scenario Outline: Marking a successor as complete will update predecessor
@ -171,5 +171,6 @@ Feature: dependencies
Scenarios:
| page | grouping |
| "dependencies" project | project |
| "dependencies" project | context |
| tag page for "bla" | context |
| tag page for "bla" | project |

View file

@ -55,7 +55,7 @@ Feature: Edit a next action from every page
@javascript @wip
Scenario Outline: Changing container of the todo in that container will hide it
# this script fails on https://code.google.com/p/selenium/issues/detail?id=3075 for selenium-webdriver > 2.14.
# and selenium-webdriver < 2.20 fails on firefox 11 :-( So @wip for now. This may work on webkit though
# and selenium-webdriver < 2.20 fails on firefox 11 :-( So @wip for now. This may work with webkit though
Given I have a todo "delete me" in the context "@home" in the project "do it"
And I have a project "go for it"
And I have selected the view for group by <grouping>
@ -77,6 +77,7 @@ Feature: Edit a next action from every page
@javascript
Scenario Outline: Deleting the last todo in container will show empty message # only project, context, tag, not todo
Given I have a context called "@home"
And I have selected the view for group by <grouping>
And I have a project "my project" that has the following todos
| context | description | tags |
| @home | first action | test, bla |
@ -90,10 +91,13 @@ Feature: Edit a next action from every page
Then I should see empty message for todos of <page type>
Scenarios:
| page | page type |
| "my project" project | project |
| context page for "@home" | context |
| tag page for "bla" | tag |
| page | page type | grouping |
| "my project" project | project | project |
| "my project" project | project | context |
| context page for "@home" | context | context |
| context page for "@home" | context | project |
| tag page for "bla" | tag | context |
| tag page for "bla" | tag | project |
@javascript
Scenario Outline: I can mark an active todo complete and it will update empty messages
@ -156,7 +160,7 @@ Feature: Edit a next action from every page
| home page | home | project | container for project "visible project" |
@javascript
Scenario Outline: I can mark a completed todo active and it will update empty messages for pages without context containers
Scenario Outline: I can mark a completed todo active and it will update empty messages for pages without hideable containers
Given I have a completed todo with description "visible todo" in project "visible project" with tags "starred" in the context "visible context"
When I go to the <page>
Then I should see empty message for todos of <page type>

View file

@ -123,7 +123,7 @@ Feature: Edit a project
Scenario: Moving the todo to the tickler will move todo to tickler container and update empty messages
Given I have a project "test" with 1 todos
When I go to the "test" project
Then I should see "todo 1" in the action container
Then I should see "todo 1" in the context container for "Context A"
And I should see empty message for deferred todos of project
And I should see empty message for completed todos of project
When I defer "todo 1" for 1 day

View file

@ -97,7 +97,9 @@ Feature: Add new next action from every page
| tickler page | context | not see |
| tickler page | project | not see |
| "test project" project | context | see |
| "test project" project | project | see |
| context page for "test context" | context | see |
| context page for "test context" | project | see |
| tag page for "starred" | context | see |
| tag page for "starred" | project | see |
@ -124,7 +126,9 @@ Feature: Add new next action from every page
| tickler page | not see | 0 | 3 | context |
| tickler page | not see | 0 | 3 | project |
| "testing" project | see | 3 | 3 | context |
| "testing" project | see | 3 | 3 | project |
| context page for "test context" | see | 2 | 3 | context |
| context page for "test context" | see | 2 | 3 | project |
| tag page for "starred" | see | 2 | 3 | context |
| tag page for "starred" | see | 2 | 3 | project |
@ -199,9 +203,13 @@ Feature: Add new next action from every page
| tickler page | context | not see | not see |
| tickler page | project | not see | not see |
| "visible project" project | project | not see | see |
| "visible project" project | context | not see | see |
| "hidden project" project | project | see | not see |
| "hidden project" project | context | see | not see |
| context page for "visible context" | context | not see | see |
| context page for "visible context" | project | not see | see |
| context page for "other context" | context | not see | not see |
| context page for "other context" | project | not see | not see |
| tag page for "starred" | context | not see | not see |
| tag page for "starred" | project | not see | not see |
| tag page for "test" | context | see | see |

View file

@ -11,8 +11,10 @@ When(/^I collapse the project container of "(.*?)"$/) do |project_name|
end
When /^I toggle all collapsed context containers$/ do
open_view_menu do
click_link 'Toggle collapsed contexts'
end
end
####### Context #######
@ -91,7 +93,6 @@ Then(/^I should (not see|see) "([^"]*)" in the (completed|done today|done this w
id = 'completed_rest_of_month_container' if container == 'done this month'
css = "div##{id} div#line_todo_#{find_todo(todo_description).id}"
page.should have_css(css)
check_css_visibility(visible, css)
end
@ -138,9 +139,8 @@ end
Then /^I should (see|not see) empty message for (done today|done this week|done this month|completed todos|deferred todos|todos) (of done actions|of context|of project|of home|of tag)/ do |visible, state, type|
css = "error: wrong state"
css = "div#c#{@context.id}-empty-d" if state == "todos" && type == "of context"
css = "div#p#{@project.id}-empty-d" if state == "todos" && type == "of project"
css = "div#no_todos_in_view" if state == "todos" && (type == "of home" || type == "of tag")
css = "div#c#{@context.id}-empty-d" if state == "todos"
css = "div#no_todos_in_view" if state == "todos" && ["of home", "of tag", "of context", "of project"].include?(type)
css = "div#completed_today_container" if state == "done today"
css = "div#completed_rest_of_week_container" if state == "done this week"
css = "div#completed_rest_of_month_container" if state == "done this month"

View file

@ -1,13 +1,7 @@
Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password|
step "I go to the login page"
fill_in "user_login", :with => username
fill_in "user_password", :with => password
uncheck "user_noexpiry"
click_button "Sign in"
logout_regexp = @mobile_interface ? "Logout" : "Logout \(#{username}\)"
page.should have_content(logout_regexp)
@current_user = User.where(:login => username).first
user = User.where(:login => username).first
request_signin_as(user)
@current_user = user
end
When /^I submit the login form as user "([^\"]*)" with password "([^\"]*)"$/ do |username, password|

View file

@ -30,7 +30,7 @@ When /^I edit the first note to "([^"]*)"$/ do |note_body|
end
end
When /^I toggle the note of "([^"]*)"$/ do |todo_description|
When(/^I toggle the note of "([^"]*)"$/) do |todo_description|
todo = @current_user.todos.where(:description => todo_description).first
todo.should_not be_nil
@ -39,8 +39,10 @@ When /^I toggle the note of "([^"]*)"$/ do |todo_description|
end
When /^I click Toggle Notes$/ do
open_view_menu do
click_link 'Toggle notes'
end
end
When /^I toggle all notes$/ do
step "I click Toggle Notes"

View file

@ -70,9 +70,9 @@ Given /^I have ([0-9]+) todos$/ do |count|
end
Given /^I have a todo with description "([^"]*)" in project "([^"]*)" with tags "([^"]*)" in the context "([^"]*)"$/ do |action_description, project_name, tags, context_name|
context = @current_user.contexts.where(:name => context_name).first_or_create
project = @current_user.projects.where(:name => project_name).first_or_create
@todo = @current_user.todos.create!(:context_id => context.id, :project_id => project.id, :description => action_description)
@context = @current_user.contexts.where(:name => context_name).first_or_create
@project = @current_user.projects.where(:name => project_name).first_or_create
@todo = @current_user.todos.create!(:context_id => @context.id, :project_id => @project.id, :description => action_description)
@todo.tag_with(tags)
@todo.save
end

View file

@ -0,0 +1,52 @@
class SessionBackdoorController < ::ApplicationController
skip_before_filter :login_required
def create
session['user_id'] = params[:user_id]
user = User.find(params[:user_id])
set_current_user(user)
user.remember_me
cookies[:auth_token] = { :value => user.remember_token, :expires => user.remember_token_expires_at }
redirect_to root_path
end
def expire_session
current_user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
session['expiry_time'] = Time.now
respond_to do |format|
format.html { render :text => "Session expired for test purposes"}
format.js { render :text => "" }
end
end
end
module TracksLoginHelper
begin
_routes = Rails.application.routes
_routes.disable_clear_and_finalize = true
_routes.clear!
Rails.application.routes_reloader.paths.each{ |path| load(path) }
_routes.draw do
# here you can add any route you want
match "/test_login_backdoor", to: "session_backdoor#create"
match "login/expire_session", to: "session_backdoor#expire_session"
end
ActiveSupport.on_load(:action_controller) { _routes.finalize! }
ensure
_routes.disable_clear_and_finalize = false
end
def request_signin_as(user)
visit "/test_login_backdoor?user_id=#{user.id}"
end
def signin_as(user)
session[:user_id] = user.id
@current_user = user
end
end

View file

@ -97,6 +97,23 @@ module TracksStepHelper
execute_javascript("$('#{sortable_css}').simulateDragSortable({move: #{delta}, handle: '.grip'});")
end
def open_view_menu
view_menu = "ul.sf-menu li#menu_view"
# click menu
view_menu_link = "#{view_menu} a#menu_view_link"
page.should have_css(view_menu_link, :visible => true)
page.find(view_menu_link).click
# wait for menu to be visible
view_menu_item = "#{view_menu} li#menu_view_toggle_contexts"
page.should have_css(view_menu_item)
within view_menu do
yield
end
end
def open_submenu_for(todo)
submenu_arrow = "div#line_todo_#{todo.id} img.todo-submenu"
page.should have_css(submenu_arrow, :visible=>true)

View file

@ -1,3 +1,4 @@
World(TracksLoginHelper)
World(TracksStepHelper)
World(TracksFormHelper)
World(TracksIdHelper)

View file

@ -235,6 +235,52 @@ class RecurringTodosControllerTest < ActionController::TestCase
assert_equal true, recurring_todo.show_always?
end
def test_start_on_monthly_rec_todo
Timecop.travel(Time.local(2012,1,1)) do
login_as(:admin_user)
put :create,
"context_name"=>"library",
"project_name"=>"Build a working time machine",
"recurring_todo" =>
{
"daily_every_x_days"=>"1",
"daily_selector"=>"daily_every_x_day",
"description"=>"new recurring pattern",
"end_date" => nil,
"ends_on" => "no_end_date",
"monthly_day_of_week" => "2",
"monthly_every_x_day" => "2",
"monthly_every_x_month2" => "1",
"monthly_every_x_month" => "3",
"monthly_every_xth_day"=>"1",
"monthly_selector"=>"monthly_every_x_day",
"notes"=>"with some notes",
"number_of_occurences" => nil,
"recurring_period"=>"monthly",
"recurring_show_days_before"=>"0",
"recurring_target"=>"show_from_date",
"recurring_show_always" => "1",
"start_from"=>"2/1/2013",
"weekly_every_x_week"=>"1",
"weekly_return_monday"=>"m",
"yearly_day_of_week"=>"1",
"yearly_every_x_day"=>"8",
"yearly_every_xth_day"=>"1",
"yearly_month_of_year2"=>"8",
"yearly_month_of_year"=>"6",
"yearly_selector"=>"yearly_every_x_day"
},
"tag_list"=>"one, two, three, four"
assert_equal "new recurring pattern", assigns['recurring_todo'].description
assert_equal "2013-01-02 00:00:00 +0000", assigns['recurring_todo'].start_from.to_s
todo = assigns['recurring_todo'].todos.first
assert_equal "2013-01-02 00:00:00 +0000", todo.show_from.to_s
end
end
def test_find_and_inactivate
login_as(:admin_user)

View file

@ -121,6 +121,28 @@ class TodosControllerTest < ActionController::TestCase
assert_equal 3, @tagged
end
def test_find_tagged_with_terms_separated_with_dot
login_as :admin_user
create_todo(description: "test dotted tag", tag_list: "first.last, second")
t = assigns['todo']
assert_equal "first.last, second", t.tag_list
get :tag, name: 'first.last.m'
assert_equal "text/html", request.format, "controller should set right content type"
assert_equal "text/html", @response.content_type
assert_equal "first.last", assigns['tag_name'], ".m should be chomped"
get :tag, name: 'first.last.txt'
assert_equal "text/plain", request.format, "controller should set right content type"
assert_equal "text/plain", @response.content_type
assert_equal "first.last", assigns['tag_name'], ".txt should be chomped"
get :tag, name: 'first.last'
assert_equal "text/html", request.format, "controller should set right content type"
assert_equal "text/html", @response.content_type
assert_equal "first.last", assigns['tag_name'], ":name should be correct"
end
def test_get_boolean_expression_from_parameters_of_tag_view_single_tag
login_as(:admin_user)
get :tag, :name => "single"
@ -669,7 +691,7 @@ class TodosControllerTest < ActionController::TestCase
recurring_todo_1 = RecurringTodo.find(1)
#set_user_to_current_time_zone(recurring_todo_1.user)
todo_1 = Todo.where(:recurring_todo_id => 1).first
today = Time.zone.now.at_midnight
today = Time.zone.now.at_midnight - 1.day
# change recurrence pattern to monthly and set show_from to today
recurring_todo_1.target = 'show_from_date'
@ -708,7 +730,7 @@ class TodosControllerTest < ActionController::TestCase
assert !new_todo.show_from.nil?
# do not use today here. It somehow gets messed up with the timezone calculation.
next_month = (Time.zone.now + 1.month).at_midnight
next_month = (Time.zone.now - 1.day + 1.month).at_midnight
assert_equal next_month.utc.to_date.to_s(:db), new_todo.show_from.utc.to_date.to_s(:db)
end
@ -920,4 +942,19 @@ class TodosControllerTest < ActionController::TestCase
assert t4.pending?, "t4 should remain pending"
assert t4.predecessors.map(&:id).include?(t3.id)
end
private
def create_todo(params={})
defaults = { source_view: 'todo',
context_name: "library", project_name: "Build a working time machine",
notes: "note", description: "a new todo", due: nil, tag_list: "a,b,c"}
params=params.reverse_merge(defaults)
put :create, _source_view: params[:_source_view],
context_name: params[:context_name], project_name: params[:project_name], tag_list: params[:tag_list],
todo: {notes: params[:notes], description: params[:description], due: params[:due]}
end
end

View file

@ -175,6 +175,9 @@ class RecurringTodoTest < ActiveSupport::TestCase
assert_equal @sunday, due_date # june 8th
due_date = @monthly.get_due_date(@sunday) # june 8th
assert_equal Time.zone.local(2008,6,8), due_date # june 8th
due_date = @monthly.get_due_date(@monday) # june 9th
assert_equal Time.zone.local(2008,8,8), due_date # aug 8th
end

View file

@ -11,6 +11,6 @@ class TaggingTest < ActiveSupport::TestCase
tagging.destroy
assert_nil Tag.where(:name => "hello").first
assert_nil Tag.where(:name => "hello").first, "Tag should be destroyed when last use in tagging was removed"
end
end

View file

@ -239,6 +239,26 @@ class TodoTest < ActiveSupport::TestCase
assert !@not_completed1.starred?
end
def test_hidden_todo_remains_hidden_after_getting_unblokked
todo = todos(:call_bill)
project=todo.project
project.hide!
assert todo.reload.hidden?, "todo in hidden project should be hidden"
todo2 = todos(:call_dino_ext)
todo.add_predecessor(todo2)
todo.block!
assert todo.pending?, "todo with predecessor should be blocked"
# cannot activate if part of hidden project
assert_raise(AASM::InvalidTransition) { todo.activate! }
todo.remove_predecessor(todo2)
assert todo.reload.hidden?, "todo should be put back in hidden state"
end
def test_todo_specification_handles_null_project
# @not_completed1 has a project
todo_desc = @not_completed1.description