diff --git a/spec/fixtures/contexts.yml b/spec/fixtures/contexts.yml index 5bf02933..a0513ccc 100644 --- a/spec/fixtures/contexts.yml +++ b/spec/fixtures/contexts.yml @@ -1,7 +1,22 @@ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -# one: -# column: value -# -# two: -# column: value +agenda: + id: 2 + name: agenda + position: 2 + hide: false + user: admin_user + +call: + id: 3 + name: call + position: 3 + hide: true + user: admin_user + +email: + id: 4 + name: email + position: 4 + hide: false + user: admin_user diff --git a/spec/fixtures/preferences.yml b/spec/fixtures/preferences.yml new file mode 100644 index 00000000..66d41ee8 --- /dev/null +++ b/spec/fixtures/preferences.yml @@ -0,0 +1,34 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +admin_user_prefs: + user: admin_user + staleness_starts: 7 + date_format: "%d/%m/%Y" + title_date_format: "%A, %d %B %Y" + show_number_completed: 5 + show_completed_projects_in_sidebar: true + show_hidden_contexts_in_sidebar: true + show_hidden_projects_in_sidebar: true + admin_email: butshesagirl@rousette.org.uk + week_starts: 1 + due_style: 0 + refresh: 0 + time_zone: "London" + verbose_action_descriptors: true + show_project_on_todo_done: false + +other_user_prefs: + user: jane + staleness_starts: 7 + date_format: "%d/%m/%Y" + title_date_format: "%A, %d %B %Y" + show_number_completed: 5 + show_completed_projects_in_sidebar: true + show_hidden_contexts_in_sidebar: true + show_hidden_projects_in_sidebar: true + admin_email: butshesagirl@rousette.org.uk + week_starts: 1 + due_style: 0 + refresh: 0 + time_zone: "London" + verbose_action_descriptors: false + show_project_on_todo_done: true diff --git a/spec/fixtures/todos.yml b/spec/fixtures/todos.yml new file mode 100644 index 00000000..412feac8 --- /dev/null +++ b/spec/fixtures/todos.yml @@ -0,0 +1,57 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +<% + +def today + Time.zone.now.beginning_of_day.to_s(:db) +end + +def next_week + 1.week.from_now.beginning_of_day.to_s(:db) +end + +def last_week + 1.week.ago.beginning_of_day.to_s(:db) +end + +def two_weeks_ago + 2.weeks.ago.beginning_of_day.to_s(:db) +end + +def two_weeks_hence + 2.weeks.from_now.beginning_of_day.to_s(:db) +end + +%> + +billgates: + id: 2 + context_id: 2 + project_id: 2 + description: Call Bill Gates to find out how much he makes per day + notes: ~ + state: active + due: <%= two_weeks_hence %> + completed_at: ~ + user: admin_user + +dinoexterm: + id: 3 + context_id: 2 + project_id: 3 + description: Call dinosaur exterminator + notes: Ask him if I need to hire a skip for the corpses. + state: active + due: <%= two_weeks_hence %> + completed_at: ~ + user: admin_user + +buymilk: + id: 4 + context_id: 2 + project_id: ~ + description: Buy milk + notes: ~ + state: completed + due: ~ + completed_at: <%= today %> + user: admin_user diff --git a/spec/fixtures/users.yml b/spec/fixtures/users.yml new file mode 100644 index 00000000..5d027653 --- /dev/null +++ b/spec/fixtures/users.yml @@ -0,0 +1,27 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +admin_user: + login: admin + crypted_password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--") %> + token: <%= Digest::SHA1.hexdigest("adminSat Feb 25 17:14:00 GMT 20060.236961325863376") %> + is_admin: true + first_name: Admin + last_name: Schmadmin + auth_type: database + +other_user: + login: jane + crypted_password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--") %> + token: <%= Digest::SHA1.hexdigest("janeSun Feb 19 14:42:45 GMT 20060.408173979260027") %> + is_admin: false + first_name: Jane + last_name: Doe + auth_type: database + +ldap_user: + login: john + crypted_password: test + token: <%= Digest::SHA1.hexdigest("johnSun Feb 19 14:42:45 GMT 20060.408173979260027") %> + is_admin: false + first_name: John + last_name: Deere + auth_type: ldap diff --git a/spec/models/context_spec.rb b/spec/models/context_spec.rb index 74ece6d0..ffb6b121 100644 --- a/spec/models/context_spec.rb +++ b/spec/models/context_spec.rb @@ -13,56 +13,139 @@ module ContextSpecHelper end - describe Context do +describe "Context validations" do include ContextSpecHelper - - before(:each) do - @context = Context.new - end - - it "should be valid" do - @context.name = "FooBar" - @context.should be_valid + + before(:each) do + @context = Context.new end - + + it "should be valid" do + @context.attributes = valid_context_attributes + @context.should be_valid + @context.save + Context.should have(4).records # 3 in fixtures 1 set up here + end + + it "should have two errors with a missing name" do + @context.attributes = valid_context_attributes.except(:name) + @context.should_not be_valid + @context.should have(2).error_on(:name) + @context.errors.on(:name)[0].should eql("context must have a name") + @context.errors.on(:name)[1].should eql("context name must be less than 256 characters") + end + it "should have one error with a name of more than 255 characters" do @context.name = "z" * 256 + @context.should_not be_valid @context.should have(1).error_on(:name) + @context.errors.on(:name).should eql("context name must be less than 256 characters") end - + it "should have one error with name containing comma" do @context.name = "Foo,Bar" + @context.should_not be_valid @context.should have(1).error_on(:name) + @context.errors.on(:name).should eql("cannot contain the comma (',') character") end + it "should have one error if name already exists for user" do + @existing_context = Context.new + @existing_context.attributes = valid_context_attributes + @existing_context.save + @context.attributes = valid_context_attributes + @context.should_not be_valid + @context.should have(1).error_on(:name) + @context.errors.on(:name).should eql("already exists") + end + it "should have one record in Context model class" do @context.name = "FooBar" @context.save - Context.should have(1).record + Context.should have(4).records # 3 in fixture, one set up here + end + +end + +describe "Context model" do + fixtures :users, :todos, :contexts, :preferences + + include ContextSpecHelper + + it "should show hidden" do + contexts(:call).should be_hide # :hide should be true end - it "should be hidden" do - @context.attributes = valid_context_attributes - @context.should be_hide # :hide should be true + it "should be produce correct .summary text for hidden context" do + contexts(:call).summary(1).should eql("
1. Context is Hidden.
") + end + + it "should show not hidden" do + contexts(:call).hide = false + contexts(:call).should_not be_hide # :hide should be true + end + + it "should produce correct .summary text for active context" do + contexts(:call).hide = false + contexts(:call).summary(1).should eql("1. Context is Active.
") + end + + it "should return .title which matches name" do + contexts(:agenda).title.should eql(contexts(:agenda).name) end - it "should be produce correct summary text for hidden context" do - @context.attributes = valid_context_attributes - @context.summary(1).should eql("1. Context is Hidden.
") + it "should .find_by_namepart with exact match" do + @found = Context.find_by_namepart('agenda') + @found.should_not eql(nil) + @found.id.should eql(contexts(:agenda).id) end - it "should be active" do - @context.attributes = valid_context_attributes - @context.hide = false - @context.save - @context.should_not be_hide # :hide should be true + it "should .find_by_namepart with partial match" do + @found = Context.find_by_namepart('ag') + @found.should_not eql(nil) + @found.id.should eql(contexts(:agenda).id) end - it "should produce correct summary text for active context" do - @context.attributes = valid_context_attributes - @context.hide = false - @context.save - @context.summary(1).should eql("1. Context is Active.
") + it "should return id with .to_param" do + Context.find(2).to_param.should eql("2") end - end + it "should return feed options" do + opts = Context.feed_options(users(:admin_user)) + opts[:title].should eql("Tracks Contexts") + opts[:description].should eql("Lists all the contexts for Admin Schmadmin") + end + + it "should create null Context with .null_object" do + @empty = Context.null_object + @empty.should be_an_instance_of(NullContext) + @empty.id.should eql(nil) + @empty.name.should eql('') + end + + it "should delete todos within context when context deleted" do + contexts(:agenda).todos.count.should eql(3) + agenda_todo_ids = contexts(:agenda).todos.collect{|t| t.id } + contexts(:agenda).destroy + agenda_todo_ids.each do |todo_id| + Todo.find(:all).should_not include(todo_id) + end + end + + it "should return correct number of done todos" do + contexts(:agenda).done_todos.size.should eql(1) + t = contexts(:agenda).not_done_todos[0] + t.complete! + t.save! + Context.find(contexts(:agenda)).done_todos.size.should eql(2) + end + + it "should return correct number of not done todos" do + contexts(:agenda).not_done_todos.size.should eql(2) + t = contexts(:agenda).not_done_todos[0] + t.complete! + t.save! + Context.find(contexts(:agenda)).not_done_todos.size.should eql(1) + end + +end diff --git a/stories/context_detail/change_context_name.story b/stories/context_detail/change_context_name.story new file mode 100644 index 00000000..0dffeebe --- /dev/null +++ b/stories/context_detail/change_context_name.story @@ -0,0 +1,15 @@ +Story: Change context name + + As a Tracks user + I want to change the name of a context + So that it can best reflect my daily life + + Scenario: In place edit of context name + Given a logged in user Luis + And Luis has a context Errands + When Luis visits the Errands context page + And he edits the Errands context name in place to be OutAndAbout + Then he should see the context name is OutAndAbout + When Luis visits the context listing page + Then he should see that a context named Errands is not present + And he should see that a context named OutAndAbout is present diff --git a/stories/steps/context_detail.rb b/stories/steps/context_detail.rb new file mode 100644 index 00000000..f02ae1aa --- /dev/null +++ b/stories/steps/context_detail.rb @@ -0,0 +1,34 @@ +steps_for :context_detail do + include_steps_for :users + + Given "Luis has a context Errands" do + @errands = @luis.contexts.create!(:name => 'Errands') + end + + When "Luis visits the Errands context page" do + visits "/contexts/#{@errands.to_param}" + end + + When "he edits the Errands context name in place to be OutAndAbout" do + selenium.click 'context_name_in_place_editor' + wait_for_ajax_and_effects + selenium.type "css=#context_name_in_place_editor-inplaceeditor input.editor_field", "OutAndAbout" + clicks_button "ok", :wait => :ajax + end + + When "Luis visits the context listing page" do + visits "/contexts" + end + + Then "he should see the context name is OutAndAbout" do + should_see 'OutAndAbout' + end + + Then "he should see that a context named Errands is not present" do + should_not_see 'Errands' + end + + Then "he should see that a context named OutAndAbout is present" do + should_see 'OutAndAbout' + end +end diff --git a/vendor/plugins/webrat/lib/webrat/selenium/selenium_session.rb b/vendor/plugins/webrat/lib/webrat/selenium/selenium_session.rb index b00735f3..9dfbb524 100644 --- a/vendor/plugins/webrat/lib/webrat/selenium/selenium_session.rb +++ b/vendor/plugins/webrat/lib/webrat/selenium/selenium_session.rb @@ -10,8 +10,13 @@ module Webrat @selenium.open(url) end - def fills_in(label_text, options) - @selenium.type("webrat=#{label_text}", "#{options[:with]}") + def fills_in(field_identifier, options) + locator = if field_identifier == :current + "css=:focus" + else + "webrat=#{Regexp.escape(field_identifier)}" + end + @selenium.type(locator, "#{options[:with]}") end def response_body @@ -25,11 +30,16 @@ module Webrat wait_for_result(options[:wait]) end - def clicks_link(link_text, options = {}) + def clicks_link(link_text, options = {}) @selenium.click("webratlink=#{link_text}") wait_for_result(options[:wait]) end + def clicks_link_within(selector, link_text, options = {}) + @selenium.click("webratlinkwithin=#{selector}|#{link_text}") + wait_for_result(options[:wait]) + end + def wait_for_result(wait_type) if wait_type == :ajax wait_for_ajax @@ -55,8 +65,8 @@ module Webrat def wait_for_ajax_and_effects wait_for_ajax wait_for_effects - end - + end + def selects(option_text, options = {}) id_or_name_or_label = options[:from] @@ -130,6 +140,24 @@ module Webrat return candidateLinks.first(); JS + @selenium.add_location_strategy('webratlinkwithin', <<-JS) + var locatorParts = locator.split('|'); + var cssAncestor = locatorParts[0]; + var linkText = locatorParts[1]; + var matchingElements = cssQuery(cssAncestor, inDocument); + var candidateLinks = matchingElements.collect(function(ancestor){ + var links = ancestor.getElementsByTagName('a'); + return $A(links).select(function(candidateLink) { + return PatternMatcher.matches(linkText, getText(candidateLink)); + }); + }).flatten().compact(); + if (candidateLinks.length == 0) { + return null; + } + candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort + return candidateLinks.first(); + JS + @selenium.add_location_strategy('webratselectwithoption', <<-JS) var optionElements = inDocument.getElementsByTagName('option'); var locatedOption = $A(optionElements).find(function(candidate){