paginated_table 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +132 -0
  3. data/Rakefile +38 -0
  4. data/lib/paginated_table.rb +7 -0
  5. data/lib/paginated_table/controller_helpers.rb +14 -0
  6. data/lib/paginated_table/engine.rb +7 -0
  7. data/lib/paginated_table/page.rb +87 -0
  8. data/lib/paginated_table/railtie.rb +12 -0
  9. data/lib/paginated_table/version.rb +3 -0
  10. data/lib/paginated_table/view_helpers.rb +168 -0
  11. data/lib/tasks/paginated_table_tasks.rake +4 -0
  12. data/test/dummy/README.rdoc +261 -0
  13. data/test/dummy/Rakefile +7 -0
  14. data/test/dummy/app/assets/javascripts/application.js +16 -0
  15. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  16. data/test/dummy/app/controllers/application_controller.rb +3 -0
  17. data/test/dummy/app/controllers/data_controller.rb +27 -0
  18. data/test/dummy/app/helpers/application_helper.rb +2 -0
  19. data/test/dummy/app/views/data/_data.html.erb +7 -0
  20. data/test/dummy/app/views/data/index.html.erb +3 -0
  21. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  22. data/test/dummy/config.ru +4 -0
  23. data/test/dummy/config/application.rb +59 -0
  24. data/test/dummy/config/boot.rb +10 -0
  25. data/test/dummy/config/database.yml +25 -0
  26. data/test/dummy/config/environment.rb +5 -0
  27. data/test/dummy/config/environments/development.rb +37 -0
  28. data/test/dummy/config/environments/production.rb +67 -0
  29. data/test/dummy/config/environments/test.rb +37 -0
  30. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  31. data/test/dummy/config/initializers/inflections.rb +15 -0
  32. data/test/dummy/config/initializers/mime_types.rb +5 -0
  33. data/test/dummy/config/initializers/secret_token.rb +7 -0
  34. data/test/dummy/config/initializers/session_store.rb +8 -0
  35. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  36. data/test/dummy/config/locales/en.yml +5 -0
  37. data/test/dummy/config/routes.rb +59 -0
  38. data/test/dummy/db/test.sqlite3 +0 -0
  39. data/test/dummy/log/test.log +56930 -0
  40. data/test/dummy/public/404.html +26 -0
  41. data/test/dummy/public/422.html +26 -0
  42. data/test/dummy/public/500.html +25 -0
  43. data/test/dummy/public/favicon.ico +0 -0
  44. data/test/dummy/script/rails +6 -0
  45. data/test/dummy/tmp/cache/assets/CB7/360/sprockets%2F8334f01490cb91467dfd76ad25b67780 +0 -0
  46. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  47. data/test/dummy/tmp/cache/assets/D25/810/sprockets%2F4ff88255fbab9775241c5d8c8c6e2088 +0 -0
  48. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  49. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  50. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  51. data/test/dummy/tmp/cache/assets/D62/6D0/sprockets%2F9b8014b0c5371c3bf5dd4f018a5ec71e +0 -0
  52. data/test/dummy/tmp/cache/assets/D71/1A0/sprockets%2F32c631252aee35736d93e06f3edffd6d +0 -0
  53. data/test/dummy/tmp/cache/assets/DD3/F90/sprockets%2Fc24290dff33aff9c3d2f971f6d8ae04b +0 -0
  54. data/test/dummy/tmp/cache/assets/DD4/950/sprockets%2F09e7f24ef1ff59b4fc390bdf415c60af +0 -0
  55. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +9809 -0
  56. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +9809 -0
  57. data/test/dummy/tmp/capybara/capybara-201204041545476374284085.html +12 -0
  58. data/test/integration/paginated_table_integration_test.rb +235 -0
  59. data/test/test_helper.rb +31 -0
  60. data/test/units/controller_helpers_test.rb +43 -0
  61. data/test/units/page_test.rb +192 -0
  62. data/test/units/view_helpers_test.rb +317 -0
  63. metadata +276 -0
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <link href="/assets/application.css" media="all" rel="stylesheet" type="text/css">
6
+ <script src="/assets/application.js" type="text/javascript"></script>
7
+ </head>
8
+ <body>
9
+
10
+ <table class="paginated"><tbody></tbody></table>
11
+ </body>
12
+ </html>
@@ -0,0 +1,235 @@
1
+ require 'test_helper'
2
+ require 'capybara/webkit'
3
+
4
+ describe "paginated_table integration" do
5
+
6
+ describe "rendering" do
7
+ before do
8
+ visit "/data"
9
+ end
10
+
11
+ it "displays a pagination area" do
12
+ page.has_xpath?(pagination_xpath).must_equal true
13
+ end
14
+
15
+ it "displays a paginated table" do
16
+ page.has_xpath?(table_xpath).must_equal true
17
+ end
18
+
19
+ it "displays Name in the first column header" do
20
+ page.has_xpath?("#{th_xpath(1)}[.='Name']").must_equal true
21
+ end
22
+
23
+ it "displays Raw in the third column header" do
24
+ page.has_xpath?("#{th_xpath(3)}[.='Raw']").must_equal true
25
+ end
26
+
27
+ it "displays the data names in the first column" do
28
+ (1..10).each do |row|
29
+ page.has_xpath?("#{tr_xpath(row)}/td[1][.='Name #{row}']").must_equal true
30
+ end
31
+ end
32
+
33
+ it "displays links to the data in the second column" do
34
+ (1..10).each do |row|
35
+ page.has_xpath?("#{tr_xpath(row)}/td[2]/a[@href='/data/#{row}'][.='#{row}']").must_equal true
36
+ end
37
+ end
38
+
39
+ describe "with javascript" do
40
+ before do
41
+ Capybara.current_driver = Capybara.javascript_driver
42
+ end
43
+
44
+ it "updates only the pagination table from pagination table links" do
45
+ visit "/data"
46
+ click_link "2"
47
+ wait_for_ajax_request
48
+ page.has_xpath?("//h1[2]").must_equal false
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "pagination" do
54
+ describe "without javascript" do
55
+ it "displays one page of results" do
56
+ visit "/data"
57
+ pagination_info_text.must_equal "Displaying data controller/data 1 - 10 of 100 in total"
58
+ end
59
+
60
+ it "follows the link to the second page of results" do
61
+ visit "/data"
62
+ click_link "2"
63
+ pagination_info_text.must_equal "Displaying data controller/data 11 - 20 of 100 in total"
64
+ end
65
+ end
66
+
67
+ describe "with javascript" do
68
+ before do
69
+ Capybara.current_driver = Capybara.javascript_driver
70
+ end
71
+
72
+ it "displays one page of results" do
73
+ visit "/data"
74
+ pagination_info_text.must_equal "Displaying data controller/data 1 - 10 of 100 in total"
75
+ end
76
+
77
+ it "follows the link to the second page of results" do
78
+ visit "/data"
79
+ click_link "2"
80
+ wait_for_ajax_request
81
+ pagination_info_text.must_equal "Displaying data controller/data 11 - 20 of 100 in total"
82
+ end
83
+
84
+ # Ensures the AJAX content is decorated with event handlers
85
+ it "follows the link to the fourth page, then back to the third page" do
86
+ visit "/data"
87
+ click_link "4"
88
+ wait_for_ajax_request
89
+ pagination_info_text.must_equal "Displaying data controller/data 31 - 40 of 100 in total"
90
+ click_link "3"
91
+ wait_for_ajax_request
92
+ pagination_info_text.must_equal "Displaying data controller/data 21 - 30 of 100 in total"
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "sorting" do
98
+ describe "decoration" do
99
+ it "marks the sortable columns" do
100
+ visit "/data"
101
+ page.has_xpath?("#{th_xpath(1)}[@class='sortable'][.='Name']").must_equal true
102
+ end
103
+
104
+ it "marks the sort column when sorted in ascending order" do
105
+ visit "/data"
106
+ click_link "Name"
107
+ page.has_xpath?("#{th_xpath(1)}[@class='sortable sorted_asc'][.='Name']").must_equal true
108
+ end
109
+
110
+ it "marks the sort column when sorted in descending order" do
111
+ visit "/data"
112
+ click_link "Name"
113
+ click_link "Name"
114
+ page.has_xpath?("#{th_xpath(1)}[@class='sortable sorted_desc'][.='Name']").must_equal true
115
+ end
116
+ end
117
+
118
+ describe "without javascript" do
119
+ it "follows the link to sort the first column in ascending order" do
120
+ visit "/data"
121
+ click_link "Name"
122
+ [1, 10, 100, 11, 12, 13, 14, 15, 16, 17].each_with_index do |row, i|
123
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
124
+ end
125
+ end
126
+
127
+ it "follows the link to sort the first column twice in descending order" do
128
+ visit "/data"
129
+ click_link "Name"
130
+ click_link "Name"
131
+ [99, 98, 97, 96, 95, 94, 93, 92, 91, 90].each_with_index do |row, i|
132
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
133
+ end
134
+ end
135
+
136
+ it "follows the link to sort the first column, then to the second page" do
137
+ visit "/data"
138
+ click_link "Name"
139
+ click_link "2"
140
+ [18, 19, 2, 20, 21, 22, 23, 24, 25, 26].each_with_index do |row, i|
141
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
142
+ end
143
+ end
144
+
145
+ it "has no link to sort the second column" do
146
+ visit "/data"
147
+ page.has_xpath?("a[.='Link']").must_equal false
148
+ end
149
+ end
150
+
151
+ describe "with javascript" do
152
+ before do
153
+ Capybara.current_driver = Capybara.javascript_driver
154
+ end
155
+
156
+ it "follows the link to sort the first column in ascending order" do
157
+ visit "/data"
158
+ click_link "Name"
159
+ wait_for_ajax_request
160
+ [1, 10, 100, 11, 12, 13, 14, 15, 16, 17].each_with_index do |row, i|
161
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
162
+ end
163
+ end
164
+
165
+ it "follows the link to sort the first column twice in descending order" do
166
+ visit "/data"
167
+ click_link "Name"
168
+ wait_for_ajax_request
169
+ click_link "Name"
170
+ wait_for_ajax_request
171
+ [99, 98, 97, 96, 95, 94, 93, 92, 91, 90].each_with_index do |row, i|
172
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
173
+ end
174
+ end
175
+
176
+ it "follows the link to sort the first column, then to the second page" do
177
+ visit "/data"
178
+ click_link "Name"
179
+ wait_for_ajax_request
180
+ click_link "2"
181
+ wait_for_ajax_request
182
+ [18, 19, 2, 20, 21, 22, 23, 24, 25, 26].each_with_index do |row, i|
183
+ page.has_xpath?("#{tr_xpath(i + 1)}/td[1][.='Name #{row}']").must_equal true
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ def pagination_xpath
190
+ "//div[@class='paginated_table']"
191
+ end
192
+
193
+ def pagination_header_xpath
194
+ "#{pagination_xpath}/div[@class='header']"
195
+ end
196
+
197
+ def pagination_info_xpath
198
+ "#{pagination_header_xpath}/div[@class='info']"
199
+ end
200
+
201
+ def table_xpath
202
+ "#{pagination_xpath}/table[@class='paginated']"
203
+ end
204
+
205
+ def th_xpath(column)
206
+ "#{table_xpath}/thead/tr[1]/th[#{column}]"
207
+ end
208
+
209
+ def tbody_xpath
210
+ "#{table_xpath}/tbody[1]"
211
+ end
212
+
213
+ def tr_xpath(row)
214
+ "#{tbody_xpath}/tr[#{row}]"
215
+ end
216
+
217
+ def pagination_info_text
218
+ info = find(:xpath, pagination_info_xpath)
219
+ replace_nbsp(info.text)
220
+ end
221
+
222
+ def replace_nbsp(str)
223
+ if str.respond_to?(:valid_encoding?)
224
+ str.force_encoding('UTF-8').gsub(/\xc2\xa0/u, ' ')
225
+ else
226
+ str.gsub(/\xc2\xa0/u, ' ')
227
+ end
228
+ end
229
+
230
+ def wait_for_ajax_request
231
+ wait_until do
232
+ page.evaluate_script('jQuery.active') == 0
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,31 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
+ require "rails/test_help"
6
+ require 'minitest/autorun'
7
+ require 'capybara/rails'
8
+ require 'mocha/integration/mini_test'
9
+
10
+ Rails.backtrace_cleaner.remove_silencers!
11
+
12
+ # Load support files
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14
+
15
+ # Load fixtures from the engine
16
+ if ActiveSupport::TestCase.method_defined?(:fixture_path=)
17
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
18
+ end
19
+
20
+ Capybara.javascript_driver = :webkit
21
+
22
+ class IntegrationTest < MiniTest::Spec
23
+ include Capybara::DSL
24
+
25
+ after do
26
+ Capybara.reset_sessions!
27
+ Capybara.use_default_driver
28
+ end
29
+ end
30
+
31
+ MiniTest::Spec.register_spec_type(/integration$/, IntegrationTest)
@@ -0,0 +1,43 @@
1
+ module PaginatedTable
2
+ describe ControllerHelpers do
3
+ let(:params) { stub("params") }
4
+ let(:request) { stub("request", :xhr? => false) }
5
+ let(:controller) {
6
+ controller = Object.new
7
+ controller.extend(ControllerHelpers)
8
+ controller.stubs(:params => params, :request => request)
9
+ controller
10
+ }
11
+
12
+ describe "#paginated_table" do
13
+ let(:collection) { stub("collection") }
14
+ let(:tables) { { "collection_name" => collection } }
15
+ let(:page) { stub("page") }
16
+ let(:data) { stub("data") }
17
+ let(:data_page) { stub("data_page", :data => data, :page => page) }
18
+
19
+ before do
20
+ PageParams.stubs(:create_page_from_params).with(params).returns(page)
21
+ DataPage.stubs(:new).with(collection, page).returns(data_page)
22
+ end
23
+
24
+ it "sets an instance variable on the controller with the data page" do
25
+ controller.paginated_table(tables)
26
+ controller.instance_variable_get("@collection_name").must_equal data_page
27
+ end
28
+
29
+ it "renders the named partial without layout if request is xhr?" do
30
+ request.stubs(:xhr? => true)
31
+ controller.expects(:render).
32
+ with(:partial => "collection_name", :layout => false)
33
+ controller.paginated_table(tables)
34
+ end
35
+
36
+ it "does not render if request is not xhr?" do
37
+ request.stubs(:xhr? => false)
38
+ controller.expects(:render).never
39
+ controller.paginated_table(tables)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,192 @@
1
+ module PaginatedTable
2
+ describe Page do
3
+ let(:page) { Page.new(:number => 2, :rows => 5, :sort_column => 'name', :sort_direction => 'desc') }
4
+
5
+ it "has a page number" do
6
+ page.number.must_equal 2
7
+ end
8
+
9
+ it "does not accept a negative page number" do
10
+ lambda { Page.new(:number => -1) }.must_raise ArgumentError
11
+ end
12
+
13
+ it "does not accept a zero page number" do
14
+ lambda { Page.new(:number => 0) }.must_raise ArgumentError
15
+ end
16
+
17
+ it "does not accept an invalid page number" do
18
+ lambda { Page.new(:number => 'foo') }.must_raise ArgumentError
19
+ end
20
+
21
+ it "has a rows number" do
22
+ page.rows.must_equal 5
23
+ end
24
+
25
+ it "does not accept a negative number of rows" do
26
+ lambda { Page.new(:rows => -1) }.must_raise ArgumentError
27
+ end
28
+
29
+ it "does not accept a zero number of rows "do
30
+ lambda { Page.new(:rows => 0) }.must_raise ArgumentError
31
+ end
32
+
33
+ it "does not accept an invalid page number" do
34
+ lambda { Page.new(:rows => 'foo') }.must_raise ArgumentError
35
+ end
36
+
37
+ it "has a sort column" do
38
+ page.sort_column.must_equal 'name'
39
+ end
40
+
41
+ it "has a sort direction" do
42
+ page.sort_direction.must_equal 'desc'
43
+ end
44
+
45
+ it "does not accept an invalid sort direction" do
46
+ lambda { Page.new(:sort_direction => 'foo') }.must_raise ArgumentError
47
+ end
48
+
49
+ describe ".opposite_sort_direction" do
50
+ it "returns asc for desc" do
51
+ Page.opposite_sort_direction('asc').must_equal 'desc'
52
+ end
53
+
54
+ it "returns desc for asc" do
55
+ Page.opposite_sort_direction('desc').must_equal 'asc'
56
+ end
57
+ end
58
+
59
+ describe "#page_for_number" do
60
+ describe "with a new page number" do
61
+ let(:number_page) { page.page_for_number(3) }
62
+
63
+ it "returns a new page with the new page number" do
64
+ number_page.number.must_equal 3
65
+ end
66
+
67
+ it "returns a new page with the same number of rows" do
68
+ number_page.rows.must_equal 5
69
+ end
70
+
71
+ it "returns a new page with the same sort column" do
72
+ number_page.sort_column.must_equal 'name'
73
+ end
74
+
75
+ it "returns a new page with the same sort direction" do
76
+ number_page.sort_direction.must_equal 'desc'
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#page_for_sort_column" do
82
+ describe "on a new sort column" do
83
+ let(:sort_page) { page.page_for_sort_column('title') }
84
+
85
+ it "returns a new page with page number 1" do
86
+ sort_page.number.must_equal 1
87
+ end
88
+
89
+ it "returns a new page with the same number of rows" do
90
+ sort_page.rows.must_equal 5
91
+ end
92
+
93
+ it "returns a new page with the given sort column" do
94
+ sort_page.sort_column.must_equal 'title'
95
+ end
96
+
97
+ it "returns a new page with sort direction asc" do
98
+ sort_page.sort_direction.must_equal 'asc'
99
+ end
100
+ end
101
+
102
+ describe "on the same sort column" do
103
+ let(:sort_page) { page.page_for_sort_column('name') }
104
+
105
+ it "returns a new page with page number 1" do
106
+ sort_page.number.must_equal 1
107
+ end
108
+
109
+ it "returns a new page with the same number of rows" do
110
+ sort_page.rows.must_equal 5
111
+ end
112
+
113
+ it "returns a new page with the same sort column" do
114
+ sort_page.sort_column.must_equal 'name'
115
+ end
116
+
117
+ it "returns a new page with the opposite sort direction" do
118
+ sort_page.sort_direction.must_equal 'asc'
119
+ sort_page.page_for_sort_column('name').sort_direction.must_equal 'desc'
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ describe PageParams do
126
+ describe ".create_from_params" do
127
+ it "returns a new page created from the request params" do
128
+ page = PageParams.create_page_from_params(
129
+ :page => '2',
130
+ :per_page => '5',
131
+ :sort_column => 'name',
132
+ :sort_direction => 'desc'
133
+ )
134
+ page.number.must_equal 2
135
+ page.rows.must_equal 5
136
+ page.sort_column.must_equal 'name'
137
+ page.sort_direction.must_equal 'desc'
138
+ end
139
+ end
140
+
141
+ describe ".to_params" do
142
+ it "creates a params hash from the page" do
143
+ page = Page.new(
144
+ :number => 2,
145
+ :rows => 5,
146
+ :sort_column => 'name',
147
+ :sort_direction => 'desc'
148
+ )
149
+ PageParams.to_params(page).must_equal(
150
+ :page => '2',
151
+ :per_page => '5',
152
+ :sort_column => 'name',
153
+ :sort_direction => 'desc'
154
+ )
155
+ end
156
+ end
157
+ end
158
+
159
+ describe DataPage do
160
+ describe "#data" do
161
+ let(:page) {
162
+ Page.new(
163
+ :number => 2,
164
+ :rows => 5,
165
+ :sort_column => 'name',
166
+ :sort_direction => 'asc'
167
+ )
168
+ }
169
+ let(:collection) {
170
+ collection = (1..10).map { |i| "Name #{i}" }
171
+ def collection.order(clause)
172
+ raise unless clause == "name asc"
173
+ sort
174
+ end
175
+ collection
176
+ }
177
+
178
+ it "sorts the collection and pages to the given page number" do
179
+ DataPage.new(collection, page).data.must_equal(
180
+ ["Name 5", "Name 6", "Name 7", "Name 8", "Name 9"]
181
+ )
182
+ end
183
+
184
+ describe "#page" do
185
+ it "provides a reference to the given page" do
186
+ DataPage.new(collection, page).page.must_equal page
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ end