admino 0.0.1

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.
@@ -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
+