simple_update_field 0.2.3 → 1.0.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.
Files changed (55) hide show
  1. data/.rvmrc +1 -0
  2. data/Gemfile +19 -5
  3. data/Gemfile.lock +150 -0
  4. data/Rakefile +6 -0
  5. data/VERSION +1 -1
  6. data/app/assets/images/rails.png +0 -0
  7. data/app/assets/javascripts/application.js +14 -0
  8. data/app/assets/stylesheets/application.css +7 -0
  9. data/app/controllers/application_controller.rb +3 -0
  10. data/app/controllers/phrases_controller.rb +12 -0
  11. data/app/helpers/application_helper.rb +2 -0
  12. data/app/mailers/.gitkeep +0 -0
  13. data/app/models/.gitkeep +0 -0
  14. data/app/models/phrase.rb +4 -0
  15. data/app/views/layouts/application.html.erb +14 -0
  16. data/app/views/phrases/_phrase.html.erb +8 -0
  17. data/app/views/phrases/index.html.erb +1 -0
  18. data/config/application.rb +54 -0
  19. data/config/boot.rb +6 -0
  20. data/config/database.yml +25 -0
  21. data/config/environment.rb +5 -0
  22. data/config/environments/development.rb +30 -0
  23. data/config/environments/production.rb +60 -0
  24. data/config/environments/test.rb +42 -0
  25. data/config/initializers/backtrace_silencers.rb +7 -0
  26. data/config/initializers/inflections.rb +10 -0
  27. data/config/initializers/mime_types.rb +5 -0
  28. data/config/initializers/secret_token.rb +7 -0
  29. data/config/initializers/session_store.rb +8 -0
  30. data/config/initializers/wrap_parameters.rb +14 -0
  31. data/config/locales/en.yml +5 -0
  32. data/config/routes.rb +4 -0
  33. data/config.ru +4 -0
  34. data/db/migrate/20120213182817_create_phrases.rb +9 -0
  35. data/db/schema.rb +22 -0
  36. data/db/seeds.rb +7 -0
  37. data/lib/assets/.gitkeep +0 -0
  38. data/lib/tasks/.gitkeep +0 -0
  39. data/lib/tasks/admin.rake +9 -0
  40. data/lib/tasks/jasmine.rake +8 -0
  41. data/script/rails +6 -0
  42. data/simple_update_field.gemspec +76 -2
  43. data/spec/controllers/phrases_controller_spec.rb +22 -0
  44. data/spec/javascripts/editable_list_spec.js +411 -0
  45. data/spec/javascripts/helpers/mock-ajax.js +207 -0
  46. data/spec/javascripts/spec.css +3 -0
  47. data/spec/javascripts/spec.js +2 -0
  48. data/spec/{simple_update_field_spec.rb → lib/simple_update_field_spec.rb} +1 -2
  49. data/spec/models/phrase_spec.rb +11 -0
  50. data/spec/routing/phrase_spec.rb +12 -0
  51. data/spec/routing/root_spec.rb +7 -0
  52. data/spec/spec_helper.rb +28 -8
  53. data/spec/views/_phrase.html.erb_spec.rb +9 -0
  54. data/spec/views/index.html.erb_spec.rb +10 -0
  55. metadata +157 -11
data/db/seeds.rb ADDED
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7
+ # Mayor.create(name: 'Emanuel', city: cities.first)
File without changes
File without changes
@@ -0,0 +1,9 @@
1
+ namespace :admin do
2
+ desc 'create some random phrases'
3
+ task :create_phrases => :environment do
4
+ 100.times do
5
+ Phrase.create!(text: Faker::Lorem.sentence(1))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'jasmine'
3
+ load 'jasmine/tasks/jasmine.rake'
4
+ rescue LoadError
5
+ task :jasmine do
6
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
7
+ end
8
+ end
data/script/rails ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "simple_update_field"
8
- s.version = "0.2.3"
8
+ s.version = "1.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Curtis Schofield"]
@@ -19,16 +19,63 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
+ ".rvmrc",
22
23
  "Gemfile",
23
24
  "Gemfile.lock",
24
25
  "LICENSE.txt",
25
26
  "README.rdoc",
26
27
  "Rakefile",
27
28
  "VERSION",
29
+ "app/assets/images/rails.png",
30
+ "app/assets/javascripts/application.js",
31
+ "app/assets/stylesheets/application.css",
32
+ "app/controllers/application_controller.rb",
33
+ "app/controllers/phrases_controller.rb",
34
+ "app/helpers/application_helper.rb",
35
+ "app/mailers/.gitkeep",
36
+ "app/models/.gitkeep",
37
+ "app/models/phrase.rb",
38
+ "app/views/layouts/application.html.erb",
39
+ "app/views/phrases/_phrase.html.erb",
40
+ "app/views/phrases/index.html.erb",
41
+ "config.ru",
42
+ "config/application.rb",
43
+ "config/boot.rb",
44
+ "config/database.yml",
45
+ "config/environment.rb",
46
+ "config/environments/development.rb",
47
+ "config/environments/production.rb",
48
+ "config/environments/test.rb",
49
+ "config/initializers/backtrace_silencers.rb",
50
+ "config/initializers/inflections.rb",
51
+ "config/initializers/mime_types.rb",
52
+ "config/initializers/secret_token.rb",
53
+ "config/initializers/session_store.rb",
54
+ "config/initializers/wrap_parameters.rb",
55
+ "config/locales/en.yml",
56
+ "config/routes.rb",
57
+ "db/migrate/20120213182817_create_phrases.rb",
58
+ "db/schema.rb",
59
+ "db/seeds.rb",
60
+ "lib/assets/.gitkeep",
28
61
  "lib/simple_update_field.rb",
62
+ "lib/tasks/.gitkeep",
63
+ "lib/tasks/admin.rake",
64
+ "lib/tasks/jasmine.rake",
65
+ "script/rails",
29
66
  "simple_update_field.gemspec",
30
- "spec/simple_update_field_spec.rb",
67
+ "spec/controllers/phrases_controller_spec.rb",
68
+ "spec/javascripts/editable_list_spec.js",
69
+ "spec/javascripts/helpers/mock-ajax.js",
70
+ "spec/javascripts/spec.css",
71
+ "spec/javascripts/spec.js",
72
+ "spec/lib/simple_update_field_spec.rb",
73
+ "spec/models/phrase_spec.rb",
74
+ "spec/routing/phrase_spec.rb",
75
+ "spec/routing/root_spec.rb",
31
76
  "spec/spec_helper.rb",
77
+ "spec/views/_phrase.html.erb_spec.rb",
78
+ "spec/views/index.html.erb_spec.rb",
32
79
  "vendor/assets/javascripts/editable_list.js"
33
80
  ]
34
81
  s.homepage = "http://github.com/robotarmy/simple_update_field"
@@ -45,17 +92,44 @@ Gem::Specification.new do |s|
45
92
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
46
93
  s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
47
94
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
95
+ s.add_development_dependency(%q<rails>, ["= 3.1.0"])
96
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
97
+ s.add_development_dependency(%q<jquery-rails>, [">= 0"])
98
+ s.add_development_dependency(%q<rspec-rails>, [">= 0"])
99
+ s.add_development_dependency(%q<autotest>, [">= 0"])
100
+ s.add_development_dependency(%q<capybara>, [">= 0"])
101
+ s.add_development_dependency(%q<jasminerice>, [">= 0"])
102
+ s.add_development_dependency(%q<guard-jasmine>, [">= 0"])
103
+ s.add_development_dependency(%q<faker>, [">= 0"])
48
104
  else
49
105
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
50
106
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
51
107
  s.add_dependency(%q<bundler>, ["~> 1.1.0"])
52
108
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
109
+ s.add_dependency(%q<rails>, ["= 3.1.0"])
110
+ s.add_dependency(%q<sqlite3>, [">= 0"])
111
+ s.add_dependency(%q<jquery-rails>, [">= 0"])
112
+ s.add_dependency(%q<rspec-rails>, [">= 0"])
113
+ s.add_dependency(%q<autotest>, [">= 0"])
114
+ s.add_dependency(%q<capybara>, [">= 0"])
115
+ s.add_dependency(%q<jasminerice>, [">= 0"])
116
+ s.add_dependency(%q<guard-jasmine>, [">= 0"])
117
+ s.add_dependency(%q<faker>, [">= 0"])
53
118
  end
54
119
  else
55
120
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
56
121
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
122
  s.add_dependency(%q<bundler>, ["~> 1.1.0"])
58
123
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
124
+ s.add_dependency(%q<rails>, ["= 3.1.0"])
125
+ s.add_dependency(%q<sqlite3>, [">= 0"])
126
+ s.add_dependency(%q<jquery-rails>, [">= 0"])
127
+ s.add_dependency(%q<rspec-rails>, [">= 0"])
128
+ s.add_dependency(%q<autotest>, [">= 0"])
129
+ s.add_dependency(%q<capybara>, [">= 0"])
130
+ s.add_dependency(%q<jasminerice>, [">= 0"])
131
+ s.add_dependency(%q<guard-jasmine>, [">= 0"])
132
+ s.add_dependency(%q<faker>, [">= 0"])
59
133
  end
60
134
  end
61
135
 
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+ describe PhrasesController do
3
+ context "GET" do
4
+ context "index" do
5
+ it "should be ok" do
6
+ get :index
7
+ response.should be_success
8
+ end
9
+ it "should render template" do
10
+ get :index
11
+ response.should render_template('index')
12
+ end
13
+
14
+ it "assigns phrases to @phrases" do
15
+ Phrase.create!(:text => 'Cake')
16
+ Phrase.create!(:text => 'Cake 2')
17
+ get :index
18
+ assigns[:phrases].should == Phrase.limit(2).all # all executes the ActiveRecord::Relation
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,411 @@
1
+ describe("Editable",function() {
2
+ var selector = '.phrase .text'
3
+
4
+ var should_be_an_input_after_click = function() {
5
+ // best jquery practice - use pseudo selectors after
6
+ // using natural css selectors
7
+ expect($(selector)
8
+ .filter(':input')
9
+ .size()
10
+ ).toBe(0)
11
+ $(selector)
12
+ .filter(':first')
13
+ .trigger('click.editable')
14
+ expect($(selector)
15
+ .filter(':input')
16
+ .size()
17
+ ).toBe(1)
18
+ }
19
+
20
+
21
+ it("it has a constructor", function() {
22
+ expect(Editable).toBeDefined()
23
+ expect(Editable()).toBeDefined()
24
+ })
25
+
26
+ describe("needs an attribute editable-resource-uri", function() {
27
+ beforeEach(function(){
28
+ var fixture = "<div class='phrase'>"+
29
+ "<span class='text'>CAKE</span>"+
30
+ "</div>"
31
+ // removed from dom each
32
+ // test
33
+ setFixtures(fixture)
34
+ });
35
+ describe("with editable declared",function() {
36
+ beforeEach(function() {
37
+ // Construct
38
+ Editable(selector)
39
+ })
40
+ describe("error messages in dom",function() {
41
+ it("resource-attribute attribute",function() {
42
+
43
+ $(selector)
44
+ .filter(':first')
45
+ .trigger('click.editable')
46
+ $(selector)
47
+ .filter(':first')
48
+ .trigger('blur.editable')
49
+
50
+ expect($('.editable-errors')
51
+ .text()
52
+ ).toMatch(
53
+ "expected editable-resource-attribute attribute"
54
+ )
55
+ })
56
+ it("resource-model attribute",function() {
57
+
58
+ $(selector)
59
+ .filter(':first')
60
+ .trigger('click.editable')
61
+ $(selector)
62
+ .filter(':first')
63
+ .trigger('blur.editable')
64
+
65
+ expect($('.editable-errors')
66
+ .text()
67
+ ).toMatch(
68
+ "expected editable-resource-model attribute"
69
+ )
70
+ })
71
+ it("resource-uri attribute",function() {
72
+
73
+ $(selector)
74
+ .filter(':first')
75
+ .trigger('click.editable')
76
+ $(selector)
77
+ .filter(':first')
78
+ .trigger('blur.editable')
79
+
80
+ expect($('.editable-errors')
81
+ .text()
82
+ ).toMatch(
83
+ "expected editable-resource-uri attribute"
84
+ )
85
+ })
86
+ })
87
+ })
88
+ })
89
+
90
+ describe("with a single match in dom",function() {
91
+
92
+ beforeEach(function(){
93
+ var fixture = "<div class='phrase'>"+
94
+ "<span "+
95
+ " editable-resource-uri='http://s/r/i' "+
96
+ " class='text'>CAKE</span>"+
97
+ "</div>"
98
+ setFixtures(fixture)
99
+ });
100
+
101
+ it("selector modifies dom",function() {
102
+ Editable(selector)
103
+ expect($(selector).data('editable')).toBeDefined()
104
+ })
105
+
106
+ describe("after installation",function() {
107
+ beforeEach(function() {
108
+
109
+ Editable(selector)
110
+ })
111
+
112
+ describe("ajax request is sent",function() {
113
+ beforeEach(function() {
114
+ spyOn(jQuery.ajaxSettings, 'xhr').andCallFake(function() {
115
+ var newXhr = new FakeXMLHttpRequest();
116
+ ajaxRequests.push(newXhr);
117
+ return newXhr;
118
+ })
119
+ })
120
+ it("when editing is complete",function() {
121
+ $(selector).filter(':first').attr('editable-resource-uri',"http://cake123/")
122
+ $(selector).filter(':first').attr('editable-resource-model',"phrase")
123
+ $(selector).filter(':first').attr('editable-resource-attribute',"text")
124
+
125
+ // become editable
126
+ $(selector).filter(':first').trigger('click.editable')
127
+ // change value
128
+ $(selector).filter(':first').val("passion")
129
+ // complete editing
130
+ $(selector).filter(':first').trigger('blur.editable')
131
+
132
+ request = mostRecentAjaxRequest()
133
+ console.log(request)
134
+ expect(request).not.toBeNull()
135
+ expect(request.params).toMatch(/phrase%5Btext%5D=passion/)
136
+ expect(request.method).toMatch(/PUT/)
137
+ expect(request.url).toMatch("http://cake123/")
138
+ })
139
+ })
140
+
141
+ describe("when editing begins", function() {
142
+ describe("an element ",function() {
143
+ it("is an input",should_be_an_input_after_click)
144
+ it("has same classes",function() {
145
+ $(selector).addClass('cake').addClass('oat')
146
+ $(selector).filter(':first').trigger('click.editable')
147
+ expect($(selector).filter('.cake').size()).toBe(1)
148
+ expect($(selector).filter('.oat').size()).toBe(1)
149
+ })
150
+ it("has same id",function() {
151
+ $(selector).attr('id','boom')
152
+ $(selector).filter(':first').trigger('click.editable')
153
+ expect($(selector).filter('#boom').size()).toBe(1)
154
+ })
155
+ it("stores its text as input value",function() {
156
+ $(selector).filter(':first').text("Lily")
157
+ $(selector).filter(':first').trigger('click.editable')
158
+ expect($(selector).filter(':first').val()).toEqual("Lily")
159
+ })
160
+ it("has a unique position attribute: editable-index",function() {
161
+ $(selector).attr('editable-index','333')
162
+ $(selector).filter(':first').trigger('click.editable')
163
+ expect($(selector).filter(':input').attr('editable-index')).toBe('333')
164
+ })
165
+ it("has a resource uri : editable-resource-uri",function() {
166
+ $(selector).attr('editable-resource-uri','http://11')
167
+ $(selector).filter(':first').trigger('click.editable')
168
+ expect($(selector)
169
+ .filter(':input')
170
+ .attr('editable-resource-uri')).toBe('http://11')
171
+ })
172
+ it("has a resource name : editable-resource-model",function() {
173
+ $(selector).attr('editable-resource-model','dream')
174
+ $(selector).filter(':first').trigger('click.editable')
175
+ expect($(selector)
176
+ .filter(':input')
177
+ .attr('editable-resource-model')).toBe('dream')
178
+ })
179
+ it("has a resource attribute : editable-resource-attribute",function() {
180
+ $(selector).attr('editable-resource-attribute','vision')
181
+ $(selector).filter(':first').trigger('click.editable')
182
+ expect($(selector)
183
+ .filter(':input')
184
+ .attr('editable-resource-attribute')).toBe('vision')
185
+ })
186
+ it("has same jquery data",function() {
187
+ // data is used in rails and jquery for storage of various properties
188
+ $(selector).data('hi',1)
189
+ $(selector).data('hello',2)
190
+ var expected_data = $(selector).data();
191
+ $(selector).filter(':first').trigger('click.editable')
192
+ // this is the general pattern for iterating correctly in a hash
193
+ // in javascript
194
+ for(var prop in expected_data) {
195
+ // ignore parent prototype properties
196
+ if(expected_data.hasOwnProperty(prop)) {
197
+ // use proprety expected_data[prop]
198
+ expect($(selector).filter(':input').data(prop)).toEqual(expected_data[prop])
199
+ }
200
+ }
201
+ $(selector).filter(':input').data()
202
+ })
203
+ it("stores previous incarnation: tagName",function() {
204
+ var expected_tag = $(selector).filter(':first').get(0).tagName
205
+ $(selector).filter(':first').trigger('click.editable')
206
+ expect($(selector).filter(':input').data('editable-was')).toEqual(expected_tag)
207
+ })
208
+ it("is focused",function() {
209
+ expect($(selector).filter(':focus').size()).toBe(0)
210
+ $(selector).filter(':first').trigger('click.editable')
211
+ expect($(selector).filter(':focus').size()).toBe(1)
212
+
213
+ })
214
+ })
215
+ })
216
+ describe("when editing is complete", function() {
217
+ it("an element becomes again editable-was",function() {
218
+ // first there is a mountain
219
+ var expected_tag = $(selector).filter(':first').get(0).tagName
220
+ expect($(selector).filter(expected_tag).size()).toBe(1)
221
+ $(selector).filter(':first').trigger('click.editable')
222
+ // then there is no mountain
223
+ expect($(selector).filter(expected_tag).size()).toBe(0)
224
+ $(selector).filter(':first').trigger('blur.editable')
225
+ // then there is
226
+ expect($(selector).filter(expected_tag).size()).toBe(1)
227
+ })
228
+ it("the input value becomes the CDATA",function () {
229
+ $(selector).filter(':first').text("SOUP")
230
+ $(selector).filter(':first').trigger('click.editable')
231
+ expect($(selector).filter(':first').val()).toEqual("SOUP")
232
+ $(selector).filter(':first').val("are you a kitty?")
233
+ $(selector).filter(':first').trigger('blur.editable')
234
+ expect($(selector).filter(':first').text()).toEqual("are you a kitty?")
235
+
236
+ })
237
+ })
238
+ describe("hover event handler", function() {
239
+ it("turns on editable hover class",function() {
240
+ expect($(selector).filter(':first').hasClass('editable-hover')).toBeFalsy()
241
+ $(selector).filter(':first').trigger('mouseover.editable')
242
+ expect($(selector).filter(':first').hasClass('editable-hover')).toBeTruthy()
243
+ })
244
+ it("turns off editable hover class",function() {
245
+ expect($(selector).filter(':first').hasClass('editable-hover')).toBeFalsy()
246
+ $(selector).filter(':first').trigger('mouseover.editable')
247
+ expect($(selector).filter(':first').hasClass('editable-hover')).toBeTruthy()
248
+ $(selector).filter(':first').trigger('mouseout.editable')
249
+ expect($(selector).filter(':first').hasClass('editable-hover')).toBeFalsy()
250
+ })
251
+ })
252
+
253
+ describe("editable life-cycle",function() {
254
+
255
+ it("moves from editable to to completed to editable to completed to editable",function() {
256
+ // invariant:
257
+ // the selector always exists in the dom
258
+ //
259
+ // invariant:
260
+ // the selector must become and input on click
261
+ //
262
+ expect($(selector).size()).toBe(1)
263
+ expect($(selector).filter(':input').size()).toBe(0)
264
+ $(selector).filter(':first').trigger('click.editable')
265
+ expect($(selector).size()).toBe(1)
266
+ expect($(selector).filter(':input').size()).toBe(1)
267
+ $(selector).filter(':first').trigger('blur.editable')
268
+ expect($(selector).size()).toBe(1)
269
+ expect($(selector).filter(':input').size()).toBe(0)
270
+ $(selector).filter(':first').trigger('click.editable')
271
+ expect($(selector).size()).toBe(1)
272
+ expect($(selector).filter(':input').size()).toBe(1)
273
+ $(selector).filter(':first').trigger('blur.editable')
274
+ expect($(selector).size()).toBe(1)
275
+ expect($(selector).filter(':input').size()).toBe(0)
276
+ $(selector).filter(':first').trigger('click.editable')
277
+ expect($(selector).size()).toBe(1)
278
+ expect($(selector).filter(':input').size()).toBe(1)
279
+ })
280
+ })
281
+
282
+
283
+ describe("escape key", function() {
284
+ beforeEach(function() {
285
+ $(selector).filter(':first').trigger('click.editable')
286
+ // triffer ESC event
287
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.ESC_KEY })
288
+ $(selector).filter(':first').trigger(e)
289
+ })
290
+ describe("followed by a click",function() {
291
+ it("is an input",should_be_an_input_after_click)
292
+ })
293
+ })
294
+ })
295
+ })
296
+
297
+ describe("with multiple matches in dom",function() {
298
+ beforeEach(function(){
299
+ var fixture = "<div class='phrase'><span id='first' editable-resource-uri='http://s/r/i' class='text'>CAKE1</span></div>"
300
+ fixture += "<div class='phrase'><span id='second' editable-resource-uri='http://s/r/i' class='text'>CAKE2</span></div>"
301
+ fixture += "<div class='phrase'><span id='third' editable-resource-uri='http://s/r/i' class='text'>CAKE3</span></div>"
302
+ setFixtures(fixture)
303
+ });
304
+ describe("with editable declared",function() {
305
+ beforeEach(function() {
306
+ Editable(selector)
307
+ })
308
+ it("has a data editable-index",function() {
309
+ expect($(selector).filter('#first').attr("editable-index")).toEqual('1')
310
+ expect($(selector).filter('#second').attr("editable-index")).toEqual('2')
311
+ expect($(selector).filter('#third').attr("editable-index")).toEqual('3')
312
+
313
+ })
314
+ describe("editable life-cycle",function() {
315
+ describe("on editing node there is a keydown handler", function() {
316
+
317
+ describe("the esc key", function() {
318
+ it("returns the input to it's original value", function () {
319
+ // nothing if focused
320
+ expect($(selector).filter(':focus').size()).toBe(0)
321
+ $(selector).filter(':first').trigger('click.editable')
322
+ // #first becomes focused
323
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
324
+
325
+ var expected_value = $(selector).filter(':first').val()
326
+
327
+ // modify element value
328
+ $(selector).filter('#first').val("are you a kitty?")
329
+ // triffer ESC event
330
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.ESC_KEY })
331
+ $(selector).filter('#first').trigger(e)
332
+
333
+ expect($(selector).filter('#first').text()).toEqual(expected_value)
334
+
335
+ })
336
+ })
337
+
338
+ describe("the enter key",function() {
339
+ it("completes edit and moves to next editable", function () {
340
+ // nothing if focused
341
+ expect($(selector).filter(':focus').size()).toBe(0)
342
+ $(selector).filter(':first').trigger('click.editable')
343
+ // #first becomes focused
344
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
345
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.ENTER_KEY })
346
+ $(selector).filter(':first').trigger(e)
347
+
348
+ // #second becomes focused
349
+ expect($(selector).filter('#second').filter(':focus').size()).toBe(1)
350
+ // #second becomes input
351
+ expect($(selector).filter('#second').filter(':input').size()).toBe(1)
352
+ })
353
+ it("moves back to first editable after last", function () {
354
+ expect($(selector).filter(':focus').size()).toBe(0)
355
+
356
+ $(selector).filter('#third').trigger('click.editable')
357
+ expect($(selector).filter('#third').filter(':focus').size()).toBe(1)
358
+
359
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.ENTER_KEY })
360
+ $(selector).filter('#third').trigger(e)
361
+
362
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
363
+ expect($(selector).filter('#first').filter(':input').size()).toBe(1)
364
+ })
365
+ })
366
+ describe("the tab key",function() {
367
+ it("completes edit and moves to next editable", function () {
368
+ // nothing if focused
369
+ expect($(selector).filter(':focus').size()).toBe(0)
370
+ $(selector).filter(':first').trigger('click.editable')
371
+ // #first becomes focused
372
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
373
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.TAB_KEY })
374
+ $(selector).filter(':first').trigger(e)
375
+
376
+ // #second becomes focused
377
+ expect($(selector).filter('#second').filter(':focus').size()).toBe(1)
378
+ // #second becomes input
379
+ expect($(selector).filter('#second').filter(':input').size()).toBe(1)
380
+ })
381
+ it("moves back to first editable after last", function () {
382
+ expect($(selector).filter(':focus').size()).toBe(0)
383
+
384
+ $(selector).filter('#third').trigger('click.editable')
385
+ expect($(selector).filter('#third').filter(':focus').size()).toBe(1)
386
+
387
+ var e = jQuery.Event("keydown.editable", { keyCode: Editable.TAB_KEY })
388
+ $(selector).filter('#third').trigger(e)
389
+
390
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
391
+ expect($(selector).filter('#first').filter(':input').size()).toBe(1)
392
+ })
393
+ })
394
+ it("other keys do nothing", function () {
395
+ // nothing if focused
396
+ expect($(selector).filter(':focus').size()).toBe(0)
397
+ $(selector).filter(':first').trigger('click.editable')
398
+ // #first becomes focused
399
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
400
+ var e = jQuery.Event("keydown.editable", { keyCode: 99 })
401
+ $(selector).filter(':first').trigger(e)
402
+ // nothing changes - still only one
403
+ expect($(selector).filter(':input').size()).toBe(1)
404
+ // and it is the same one
405
+ expect($(selector).filter('#first').filter(':focus').size()).toBe(1)
406
+ })
407
+ })
408
+ })
409
+ })
410
+ })
411
+ })