admino 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Admino
4
+ module Query
5
+ describe Group do
6
+ subject(:group) { Group.new(config, params) }
7
+ let(:config) { Configuration::Group.new(:foo, [:bar]) }
8
+ let(:params) { {} }
9
+
10
+ describe '#current_scope' do
11
+ context 'with no param' do
12
+ let(:params) { {} }
13
+
14
+ it 'returns the param value for the field' do
15
+ expect(group.current_scope).to be_nil
16
+ end
17
+ end
18
+
19
+ context 'with an invalid value' do
20
+ let(:params) { { 'foo' => 'qux' } }
21
+
22
+ it 'returns the param value for the field' do
23
+ expect(group.current_scope).to be_nil
24
+ end
25
+ end
26
+
27
+ context 'with a valid value' do
28
+ let(:params) { { 'foo' => 'bar' } }
29
+
30
+ it 'returns nil' do
31
+ expect(group.current_scope).to eq :bar
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#augment_scope' do
37
+ let(:result) { group.augment_scope(scope) }
38
+ let(:scope) { ScopeMock.new('original') }
39
+
40
+ context 'if the field has a value' do
41
+ let(:params) { { 'foo' => 'bar' } }
42
+
43
+ it 'returns the original scope chained with the group scope' do
44
+ expect(result.chain).to eq [:bar, []]
45
+ end
46
+ end
47
+
48
+ context 'else' do
49
+ it 'returns the original scope' do
50
+ expect(result).to eq scope
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#is_scope_active?' do
56
+ let(:params) { { 'foo' => 'bar' } }
57
+
58
+ it 'returns true if the provided scope is the one currently active' do
59
+ expect(group.is_scope_active?(:bar)).to be_true
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ module Admino
4
+ module Table
5
+ describe HeadRow do
6
+ subject(:row) { HeadRow.new(klass, view) }
7
+ let(:view) { RailsViewContext.new }
8
+ let(:klass) { Post }
9
+
10
+ it 'takes a class and a view context' do
11
+ expect(row.resource_klass).to eq klass
12
+ expect(row.view_context).to eq view
13
+ end
14
+
15
+ describe '#column' do
16
+ subject { row.to_html }
17
+
18
+ context 'text' do
19
+ context 'with label' do
20
+ before do
21
+ row.column(:title, 'This is a title')
22
+ end
23
+
24
+ it 'generates a label with it' do
25
+ should have_tag(:th, text: 'This is a title')
26
+ end
27
+ end
28
+
29
+ context 'with no label' do
30
+ before { row.column(:title) }
31
+
32
+ it 'generates a label with the titleized attribute name' do
33
+ should have_tag(:th, text: 'Title')
34
+ end
35
+ end
36
+
37
+ context 'with I18n set up' do
38
+ before do
39
+ I18n.backend.store_translations(
40
+ :en,
41
+ activemodel: { attributes: { post: { title: 'Post title' } } }
42
+ )
43
+ end
44
+
45
+ before { row.column(:title) }
46
+
47
+ it 'generates a label with the human attribute name' do
48
+ should have_tag(:th, text: 'Post title')
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'role' do
54
+ before { row.column(:author_name) }
55
+
56
+ it 'generates a role attribute with the snake-cased name of the attribute' do
57
+ should have_tag(:th, with: { role: 'author-name' })
58
+ end
59
+ end
60
+
61
+ context 'with html options param' do
62
+ before { row.column(:title, class: 'foo') }
63
+
64
+ it 'uses it to build attributes' do
65
+ should have_tag(:th, with: { class: 'foo' })
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#actions' do
71
+ subject { row.to_html }
72
+
73
+ context do
74
+ before { row.actions }
75
+
76
+ it 'renders a th cell with role "actions"' do
77
+ should have_tag(:th, with: { role: 'actions' })
78
+ end
79
+
80
+ it 'renders a th cell with text "Actions"' do
81
+ should have_tag(:th, text: 'Actions')
82
+ end
83
+ end
84
+
85
+ context 'with generic I18n set up' do
86
+ before do
87
+ I18n.backend.store_translations(
88
+ :en,
89
+ table: { actions: { title: 'Available actions' } }
90
+ )
91
+ end
92
+
93
+ it 'renders a th cell with I18n text' do
94
+ row.actions
95
+ should have_tag(:th, text: 'Available actions')
96
+ end
97
+
98
+ context 'and specific I18n set up' do
99
+ before do
100
+ I18n.backend.store_translations(
101
+ :en,
102
+ table: { actions: { post: { title: 'Post actions' } } }
103
+ )
104
+ end
105
+
106
+ it 'uses the specific I18n text' do
107
+ row.actions
108
+ should have_tag(:th, text: 'Post actions')
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ module Admino
5
+ module Table
6
+ describe Presenter do
7
+ subject(:presenter) { presenter_klass.new(collection, Post, view) }
8
+ let(:presenter_klass) { Presenter }
9
+ let(:view) { RailsViewContext.new }
10
+
11
+ let(:collection) { [ first_post, second_post ] }
12
+ let(:first_post) { Post.new('1') }
13
+ let(:first_post_presenter) { double('PresentedPost', dom_id: 'post_1') }
14
+ let(:second_post) { Post.new('2') }
15
+ let(:second_post_presenter) { double('PresentedPost', dom_id: 'post_2') }
16
+
17
+ let(:head_row) { double('HeadRow', to_html: '<td id="thead_td"></td>'.html_safe) }
18
+ let(:resource_row) { double('ResourceRow', to_html: '<td id="tbody_td"></td>'.html_safe) }
19
+
20
+ before do
21
+ PostPresenter.stub(:new).with(first_post, view).and_return(first_post_presenter)
22
+ PostPresenter.stub(:new).with(second_post, view).and_return(second_post_presenter)
23
+
24
+ HeadRow.stub(:new).with(Post, view).and_return(head_row)
25
+ ResourceRow.stub(:new).with(first_post_presenter, view).and_return(resource_row)
26
+ ResourceRow.stub(:new).with(second_post_presenter, view).and_return(resource_row)
27
+ end
28
+
29
+ describe '#.to_html' do
30
+ let(:result) { presenter.to_html }
31
+
32
+ it 'outputs a table' do
33
+ expect(result).to have_tag(:table)
34
+ end
35
+
36
+ it 'outputs a thead with a single row' do
37
+ expect(result).to have_tag('table thead tr')
38
+ end
39
+
40
+ it 'outputs a tbody with a row for each collection member' do
41
+ expect(result).to have_tag('table tbody tr', count: 2)
42
+ end
43
+
44
+ it 'adds a record idenfier to each collection row' do
45
+ expect(result).to have_tag('tbody tr#post_1')
46
+ expect(result).to have_tag('tbody tr#post_2')
47
+ end
48
+
49
+ it 'adds zebra classes to each collection row' do
50
+ expect(result).to have_tag('tbody #post_1.is-even')
51
+ expect(result).to have_tag('tbody #post_2.is-odd')
52
+ end
53
+
54
+ it 'delegates thead columns creation to .to_html HeadRow' do
55
+ expect(result).to have_tag('thead tr td#thead_td')
56
+ end
57
+
58
+ it 'delegates tbody columns creation to .to_html ResourceRow' do
59
+ expect(result).to have_tag('tbody tr td#tbody_td')
60
+ end
61
+
62
+ it 'allows passing table HTML options' do
63
+ expect(presenter.to_html(id: 'table')).to have_tag(:table, with: { id: 'table' })
64
+ end
65
+
66
+ context 'with a block' do
67
+ let(:block_call_args) do
68
+ block_call_args = []
69
+ presenter.to_html do |*args|
70
+ block_call_args << args
71
+ end
72
+ block_call_args
73
+ end
74
+
75
+ it 'calls it once passing the HeadRow instance' do
76
+ expect(block_call_args[0]).to eq [head_row, nil]
77
+ end
78
+
79
+ it 'calls it once for each collection member passing the ResourceRow instance and the member itself' do
80
+ expect(block_call_args[1]).to eq [resource_row, first_post_presenter]
81
+ expect(block_call_args[2]).to eq [resource_row, second_post_presenter]
82
+ end
83
+ end
84
+
85
+ context 'custom table HTML options' do
86
+ let(:presenter_klass) do
87
+ Class.new(Presenter) do
88
+ private
89
+
90
+ def table_html_options
91
+ { id: 'table' }
92
+ end
93
+
94
+ def thead_html_options
95
+ { id: 'thead' }
96
+ end
97
+
98
+ def thead_tr_html_options
99
+ { id: 'thead_tr' }
100
+ end
101
+
102
+ def tbody_html_options
103
+ { id: 'tbody' }
104
+ end
105
+
106
+ def tbody_tr_html_options(resource, index)
107
+ { class: "index-#{index}" }
108
+ end
109
+
110
+ def zebra_css_classes
111
+ %w(one two)
112
+ end
113
+ end
114
+ end
115
+
116
+ it "allows customizing the default table html attributes" do
117
+ expect(presenter.to_html).to have_tag(:table, with: { id: 'table' })
118
+ end
119
+
120
+ it "allows customizing the the default thead html attributes" do
121
+ expect(presenter.to_html).to have_tag(:thead, with: { id: 'thead' })
122
+ end
123
+
124
+ it "allows customizing the the default thead_tr html attributes" do
125
+ expect(presenter.to_html).to have_tag('thead tr#thead_tr')
126
+ end
127
+
128
+ it "allows customizing the the default tbody html attributes" do
129
+ expect(presenter.to_html).to have_tag(:tbody, with: { id: 'tbody' })
130
+ end
131
+
132
+ it "allows customizing the tbody_tr html attributes" do
133
+ expect(presenter.to_html).to have_tag(:tr, with: { id: 'post_1', class: 'index-0' })
134
+ end
135
+
136
+ it 'allows customizing zebra classes' do
137
+ expect(presenter.to_html).to have_tag(:tr, with: { id: 'post_1', class: 'one' })
138
+ end
139
+ end
140
+
141
+ context 'custom row builders' do
142
+ let(:presenter_klass) do
143
+ Class.new(Presenter) do
144
+ private
145
+
146
+ def head_row(*args)
147
+ OpenStruct.new(to_html: '<td id="custom_thead_td"></td>'.html_safe)
148
+ end
149
+
150
+ def resource_row(*args)
151
+ OpenStruct.new(to_html: '<td id="custom_tbody_td"></td>'.html_safe)
152
+ end
153
+ end
154
+ end
155
+
156
+ it 'allows customizing head row renderers' do
157
+ expect(presenter.to_html).to have_tag('thead tr td#custom_thead_td')
158
+ end
159
+
160
+ it 'allows customizing resource row renderers' do
161
+ expect(presenter.to_html).to have_tag('tbody tr td#custom_tbody_td')
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
@@ -0,0 +1,206 @@
1
+ require 'spec_helper'
2
+
3
+ module Admino
4
+ module Table
5
+ describe ResourceRow do
6
+ subject(:row) { ResourceRow.new(resource, view) }
7
+ let(:view) { RailsViewContext.new }
8
+ let(:resource) { Post.new('1') }
9
+
10
+ it 'takes a resource and a view context' do
11
+ expect(row.resource).to eq resource
12
+ expect(row.view_context).to eq view
13
+ end
14
+
15
+ describe '#column' do
16
+ subject { row.to_html }
17
+
18
+ context 'if block is present' do
19
+ before do
20
+ row.column { 'foo' }
21
+ end
22
+
23
+ it 'fills the cell with the block content' do
24
+ should have_tag(:td, text: 'foo')
25
+ end
26
+ end
27
+
28
+ context 'if attribute is present' do
29
+ before { row.column(:title) }
30
+
31
+ it 'fills the cell with the attribute value' do
32
+ should have_tag(:td, text: 'Post 1')
33
+ end
34
+ end
35
+
36
+ context 'if both attribute and block are missing' do
37
+ it 'raises an ArgumentError' do
38
+ expect { row.column('Title') }.to raise_error(ArgumentError)
39
+ end
40
+ end
41
+
42
+ context 'role attribute' do
43
+ before { row.column(:author_name) }
44
+
45
+ it 'generates a role attribute with the snake-cased name of the attribute' do
46
+ should have_tag(:td, with: { role: 'author-name' })
47
+ end
48
+ end
49
+
50
+ context 'with HTML options param' do
51
+ before { row.column(:title, class: 'title') }
52
+
53
+ it 'uses it to build attributes' do
54
+ should have_tag(:td, with: { class: 'title' })
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#actions' do
60
+ context 'block given' do
61
+ it 'yields the block' do
62
+ called = false
63
+ result = row.actions do
64
+ called = true
65
+ end
66
+
67
+ expect(called).to be_true
68
+ end
69
+ end
70
+
71
+ context 'no block' do
72
+ before do
73
+ row.stub(:action)
74
+ end
75
+
76
+ before do
77
+ row.actions(:show, :destroy)
78
+ end
79
+
80
+ it 'calls #action for each passed param' do
81
+ expect(row).to have_received(:action).with(:show)
82
+ expect(row).to have_received(:action).with(:destroy)
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '#action' do
88
+ subject { row.to_html }
89
+
90
+ context 'URL' do
91
+ context 'with an explicit URL' do
92
+ before { row.action(:show, '/') }
93
+
94
+ it 'generates a link with the specified URL' do
95
+ should have_tag(:a, with: { href: '/' })
96
+ end
97
+ end
98
+
99
+ context 'with no explicit URL' do
100
+ let(:row) { row_subclass.new(resource, view) }
101
+ let(:row_subclass) do
102
+ Class.new(ResourceRow) do
103
+ def show_action_url
104
+ "/posts/#{resource.to_param}"
105
+ end
106
+ end
107
+ end
108
+
109
+ before { row.action(:show) }
110
+
111
+ it 'uses a method to build the URL (ie. show_url)' do
112
+ should have_tag(:a, with: { href: '/posts/1' })
113
+ end
114
+ end
115
+
116
+ context 'with no explicit URL and no action name' do
117
+ it 'raises an ArgumentError' do
118
+ expect { row.action(:show) }.to raise_error(ArgumentError)
119
+ end
120
+ end
121
+ end
122
+
123
+ context 'with no arguments' do
124
+ it 'raises an ArgumentError' do
125
+ expect { row.action }.to raise_error(ArgumentError)
126
+ end
127
+ end
128
+
129
+ context 'td cell' do
130
+ before { row.action(:show, '/') }
131
+
132
+ it 'generates a td cell with actions role' do
133
+ should have_tag(:td, with: { role: 'actions' })
134
+ end
135
+ end
136
+
137
+ context 'link role' do
138
+ before { row.action(:show, '/') }
139
+
140
+ it 'generates a link with role' do
141
+ should have_tag(:a, with: { role: 'show' })
142
+ end
143
+ end
144
+
145
+ context 'link text' do
146
+ context do
147
+ before { row.action(:show, '/') }
148
+
149
+ it 'generates a link with a titleized attribute' do
150
+ should have_tag(:a, text: 'Show')
151
+ end
152
+ end
153
+
154
+ context 'if I18n is set up' do
155
+ before do
156
+ I18n.backend.store_translations(
157
+ :en,
158
+ table: { actions: { post: { show: 'Show post' } } }
159
+ )
160
+ end
161
+
162
+ before { row.action(:show, '/') }
163
+
164
+ it 'generates a I18n text' do
165
+ should have_tag(:a, text: 'Show post')
166
+ end
167
+ end
168
+ end
169
+
170
+ context 'with html options' do
171
+ before { row.action(:show, '/', class: 'foo') }
172
+
173
+ it 'renders them as attributes' do
174
+ should have_tag(:a, with: { class: 'foo' })
175
+ end
176
+
177
+ context 'with a class that implements a <action_name>_html_options' do
178
+ let(:row) { row_subclass.new(resource, view) }
179
+ let(:row_subclass) do
180
+ Class.new(ResourceRow) do
181
+ def show_action_html_options
182
+ { class: 'button' }
183
+ end
184
+ end
185
+ end
186
+
187
+ it 'renders them as attributes' do
188
+ should have_tag(:a, with: { class: 'foo button' })
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'with block' do
194
+ before do
195
+ row.action { 'Foo' }
196
+ end
197
+
198
+ it 'renders it' do
199
+ should have_tag(:td, text: 'Foo')
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+