simple_bootstrap_form 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/Gemfile +9 -0
- data/Guardfile +9 -0
- data/README.md +84 -1
- data/Rakefile +9 -0
- data/circle.yml +5 -0
- data/lib/simple_bootstrap_form/action_view_extensions.rb +5 -5
- data/lib/simple_bootstrap_form/css_class_list.rb +1 -1
- data/lib/simple_bootstrap_form/field_factory.rb +58 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/base_field.rb +118 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/boolean_field.rb +16 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/datetime_field.rb +55 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/email_field.rb +10 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/password_field.rb +10 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/text_field.rb +10 -0
- data/lib/simple_bootstrap_form/horizontal_form/fields/textarea_field.rb +14 -0
- data/lib/simple_bootstrap_form/horizontal_form/form_builder.rb +59 -0
- data/lib/simple_bootstrap_form/version.rb +1 -1
- data/lib/simple_bootstrap_form.rb +9 -8
- data/simple_bootstrap_form.gemspec +1 -1
- data/spec/field_factory_spec.rb +65 -0
- data/spec/simple_bootstrap_form_spec.rb +360 -83
- data/spec/spec_helper.rb +3 -0
- data/spec/support/have_element.rb +109 -0
- metadata +19 -13
- data/lib/simple_bootstrap_form/fields/base_field.rb +0 -103
- data/lib/simple_bootstrap_form/fields/boolean_field.rb +0 -14
- data/lib/simple_bootstrap_form/fields/datetime_field.rb +0 -53
- data/lib/simple_bootstrap_form/fields/email_field.rb +0 -8
- data/lib/simple_bootstrap_form/fields/password_field.rb +0 -8
- data/lib/simple_bootstrap_form/fields/text_field.rb +0 -8
- data/lib/simple_bootstrap_form/fields/textarea_field.rb +0 -12
- data/lib/simple_bootstrap_form/form_builder.rb +0 -51
- data/spec/support/input_matchers.rb +0 -72
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
def pretty_print(html)
|
4
|
+
Nokogiri::XML(html, &:noblanks).to_xhtml
|
5
|
+
end
|
6
|
+
|
3
7
|
describe SimpleBootstrapForm, type: :helper do
|
4
8
|
|
5
9
|
def account_form
|
@@ -23,133 +27,406 @@ describe SimpleBootstrapForm, type: :helper do
|
|
23
27
|
let(:model) { Account.new }
|
24
28
|
let(:form_id) { 'new_account' }
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#it "should generate a form" do
|
31
|
-
# expect(subject).to eq(
|
32
|
-
# '<form accept-charset="UTF-8" action="/accounts" class="form-horizontal" id="new_account" method="post">' +
|
33
|
-
# '<div style="margin:0;padding:0;display:inline">'+
|
34
|
-
# '<input name="utf8" type="hidden" value="✓" />'+
|
35
|
-
# '</div>'+
|
36
|
-
# '<div class="form-group">'+
|
37
|
-
# '<label class="control-label col-sm-3" for="account_email"><abbr title="required">*</abbr> Email</label>'+
|
38
|
-
# '<div class="col-sm-6">'+
|
39
|
-
# '<input class="form-control" id="account_email" name="account[email]" placeholder="Email" required="required" type="email" />'+
|
40
|
-
# '</div>'+
|
41
|
-
# '</div>'+
|
42
|
-
# '</form>'
|
43
|
-
# )
|
44
|
-
#end
|
45
|
-
|
46
|
-
describe "the form" do
|
47
|
-
it "should have and ID" do
|
48
|
-
should have_selector %(form##{form_id}[action="/accounts"])
|
30
|
+
describe "all forms" do
|
31
|
+
subject do
|
32
|
+
account_form
|
49
33
|
end
|
50
34
|
|
51
|
-
it "should
|
52
|
-
should
|
35
|
+
it "should have an ID" do
|
36
|
+
should have_element %(form##{form_id}[action="/accounts"])
|
53
37
|
end
|
54
38
|
|
55
39
|
it "should have role 'form'" do
|
56
|
-
should
|
40
|
+
should have_element "form##{form_id}[role=form]"
|
57
41
|
end
|
58
|
-
end
|
59
42
|
|
60
|
-
|
61
|
-
|
43
|
+
describe "f.input" do
|
44
|
+
let(:field_id) { 'account_email' }
|
45
|
+
|
46
|
+
describe "form-group" do
|
47
|
+
it "should add a class describing the form group" do
|
48
|
+
should have_element '.form-group.account_email_group'
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when a :group_class option is provided" do
|
52
|
+
subject {
|
53
|
+
helper.bootstrap_form_for model do |f|
|
54
|
+
f.input :email, group_class: "my_class"
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
it "should add that value as a class to the group" do
|
59
|
+
should have_element '.form-group.my_class'
|
60
|
+
end
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
context "when :group_class is set to false on the input" do
|
64
|
+
subject {
|
65
|
+
helper.bootstrap_form_for model do |f|
|
66
|
+
f.input :email, group_class: false
|
67
|
+
end
|
68
|
+
}
|
69
|
+
|
70
|
+
it "should not add a field-specific class to that group" do
|
71
|
+
should have_element('.form-group').with_only_classes('form-group')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when :group_class is set to false on the form" do
|
76
|
+
subject {
|
77
|
+
helper.bootstrap_form_for model, group_class: false do |f|
|
78
|
+
f.input :email
|
79
|
+
end
|
80
|
+
}
|
81
|
+
|
82
|
+
it "should not add a field-specific class to the groups" do
|
83
|
+
should have_element('.form-group').with_only_classes('form-group')
|
84
|
+
end
|
85
|
+
end
|
66
86
|
end
|
67
|
-
end
|
68
87
|
|
69
|
-
|
70
|
-
|
88
|
+
describe "label" do
|
89
|
+
it { should have_element "form##{form_id} > .form-group " +
|
90
|
+
"> label.control-label[for=#{field_id}]" }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "string field" do
|
94
|
+
let(:field_id) { 'account_first_name' }
|
71
95
|
|
72
|
-
|
73
|
-
|
96
|
+
it "should give the input an ID, and class form-control" do
|
97
|
+
should have_element %(input##{field_id}.form-control[name="account[first_name]"])
|
98
|
+
end
|
99
|
+
|
100
|
+
it { should have_element("input##{field_id}").with_type('text') }
|
101
|
+
it { should have_element("input##{field_id}").with_placeholder('First name') }
|
74
102
|
end
|
75
|
-
end
|
76
103
|
|
77
|
-
|
78
|
-
|
104
|
+
describe "integer field" do
|
105
|
+
let(:field_id) { "account_id" }
|
106
|
+
subject do
|
107
|
+
helper.bootstrap_form_for model do |f|
|
108
|
+
f.input :id
|
109
|
+
end
|
110
|
+
end
|
111
|
+
it { should have_element("input##{field_id}").with_type('text') }
|
112
|
+
end
|
79
113
|
|
80
|
-
|
81
|
-
|
114
|
+
describe "email field (required)" do
|
115
|
+
let(:field_id) { "account_email" }
|
116
|
+
|
117
|
+
it { should have_element("input##{field_id}").with_type('email') }
|
118
|
+
it { should have_element("input##{field_id}").with_placeholder('Email') }
|
119
|
+
it { should have_element("input##{field_id}").with_attr_value(:required, 'required') }
|
82
120
|
end
|
83
121
|
|
84
|
-
|
85
|
-
|
122
|
+
describe "option :as =>" do
|
123
|
+
let(:field_id) { "account_email" }
|
124
|
+
subject do
|
125
|
+
helper.bootstrap_form_for model do |f|
|
126
|
+
f.input :email, as: :text
|
127
|
+
end
|
128
|
+
end
|
129
|
+
it "should override the type" do
|
130
|
+
should have_element("input##{field_id}").with_type('text')
|
131
|
+
end
|
86
132
|
end
|
87
133
|
|
88
|
-
|
89
|
-
|
90
|
-
|
134
|
+
describe "password field" do
|
135
|
+
let(:field_id) { "account_password" }
|
136
|
+
|
137
|
+
it { should have_element("input##{field_id}").with_type('password') }
|
138
|
+
end
|
139
|
+
|
140
|
+
context "Using the Article form" do
|
141
|
+
let(:model) { Article.new }
|
142
|
+
subject { article_form }
|
143
|
+
|
144
|
+
describe "text field (optional)" do
|
145
|
+
let(:field_id) { 'article_body' }
|
146
|
+
|
147
|
+
it { should have_element(:textarea).with_id(field_id) }
|
148
|
+
it { should have_element(:textarea).with_id(field_id)
|
149
|
+
.with_placeholder('Body') }
|
150
|
+
it { should_not have_element(:textarea).with_id(field_id)
|
151
|
+
.with_attr_value(:required, 'required') }
|
152
|
+
end
|
91
153
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
154
|
+
describe "datetime field" do
|
155
|
+
let(:field_id) { "article_published_at" }
|
156
|
+
|
157
|
+
it { should have_element("input##{field_id}").with_type('datetime') }
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "boolean field" do
|
161
|
+
let(:field_id) { 'article_visible' }
|
162
|
+
|
163
|
+
it { should have_element("input##{field_id}").with_type('checkbox') }
|
97
164
|
end
|
98
165
|
end
|
99
|
-
it { should have_input("##{field_id}").with_type('text') }
|
100
166
|
end
|
167
|
+
end
|
101
168
|
|
102
|
-
|
103
|
-
|
169
|
+
#describe "for vertical forms" do
|
170
|
+
#end
|
171
|
+
|
172
|
+
#describe "for inline forms" do
|
173
|
+
# subject {
|
174
|
+
# helper.bootstrap_form_for model, layout: 'inline' do |f|
|
175
|
+
# f.input(:email)
|
176
|
+
# end
|
177
|
+
# }
|
178
|
+
# it { should have_element "form.form-inline" }
|
179
|
+
#end
|
104
180
|
|
105
|
-
|
106
|
-
|
107
|
-
|
181
|
+
describe "horizontal forms" do
|
182
|
+
subject {
|
183
|
+
helper.bootstrap_form_for(model, layout: 'horizontal') {}
|
184
|
+
}
|
185
|
+
|
186
|
+
describe "the form" do
|
187
|
+
it { should have_element "form.form-horizontal" }
|
108
188
|
end
|
109
189
|
|
110
|
-
describe "
|
190
|
+
describe "f.input" do
|
111
191
|
let(:field_id) { "account_email" }
|
112
|
-
|
113
|
-
|
114
|
-
|
192
|
+
|
193
|
+
context "using size defaults" do
|
194
|
+
subject {
|
195
|
+
helper.bootstrap_form_for model, layout: 'horizontal' do |f|
|
196
|
+
f.input(:email)
|
197
|
+
end
|
198
|
+
}
|
199
|
+
|
200
|
+
it "should make the label col-sm-3 wide" do
|
201
|
+
should have_element "label.col-sm-3[for=#{field_id}]"
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should place the input inside a col-sm-6" do
|
205
|
+
should have_element "form##{form_id} > .form-group > .col-sm-6 > input##{field_id}"
|
115
206
|
end
|
116
207
|
end
|
117
|
-
|
118
|
-
|
208
|
+
|
209
|
+
context "when sizes are supplied to the form" do
|
210
|
+
subject {
|
211
|
+
helper.bootstrap_form_for model, layout: 'horizontal',
|
212
|
+
label_size: 'col-md-3',
|
213
|
+
input_size: 'col-md-6' do |f|
|
214
|
+
f.input :email
|
215
|
+
end
|
216
|
+
}
|
217
|
+
|
218
|
+
it "should use the form sizes for label and input" do
|
219
|
+
should have_element "label.col-md-3[for=#{field_id}]"
|
220
|
+
should have_element ".form-group > .col-md-6 > input##{field_id}"
|
221
|
+
end
|
222
|
+
|
223
|
+
context "when sizes are supplied to the input" do
|
224
|
+
subject {
|
225
|
+
helper.bootstrap_form_for model, layout: 'horizontal' do |f|
|
226
|
+
f.input :email, label_size: 'col-xs-2', input_size: 'col-xs-4'
|
227
|
+
end
|
228
|
+
}
|
229
|
+
|
230
|
+
it "input sizes should override the form sizes" do
|
231
|
+
should have_element "label.col-xs-2[for=#{field_id}]"
|
232
|
+
should have_element ".form-group > .col-xs-4 > input##{field_id}"
|
233
|
+
end
|
234
|
+
end
|
119
235
|
end
|
120
236
|
end
|
237
|
+
end
|
238
|
+
end
|
121
239
|
|
122
|
-
|
123
|
-
|
240
|
+
describe "getbootstrap.com examples" do
|
241
|
+
describe "Basic example" do
|
242
|
+
class Model1
|
243
|
+
include ActiveModel::Validations
|
244
|
+
include ActiveModel::Conversion
|
245
|
+
include ActiveModel::Naming
|
124
246
|
|
125
|
-
|
247
|
+
def self.model_path
|
248
|
+
"foo"
|
249
|
+
end
|
250
|
+
|
251
|
+
attr_accessor :exampleInputEmail1
|
252
|
+
attr_accessor :exampleInputPassword1
|
253
|
+
attr_accessor :exampleInputFile
|
254
|
+
attr_accessor :check_me_out
|
126
255
|
end
|
127
256
|
|
128
|
-
|
129
|
-
|
130
|
-
|
257
|
+
let!(:model) do
|
258
|
+
Model1.new
|
259
|
+
end
|
131
260
|
|
132
|
-
|
133
|
-
|
261
|
+
let(:basic_example_output_from_getbootstrap_dot_com) {
|
262
|
+
<<-BASIC_EXAMPLE
|
263
|
+
<form role="form">
|
264
|
+
<div class="form-group">
|
265
|
+
<label for="exampleInputEmail1">Email address</label>
|
266
|
+
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
|
267
|
+
</div>
|
268
|
+
<div class="form-group">
|
269
|
+
<label for="exampleInputPassword1">Password</label>
|
270
|
+
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
|
271
|
+
</div>
|
272
|
+
<div class="form-group">
|
273
|
+
<label for="exampleInputFile">File input</label>
|
274
|
+
<input type="file" id="exampleInputFile">
|
275
|
+
<p class="help-block">Example block-level help text here.</p>
|
276
|
+
</div>
|
277
|
+
<div class="checkbox">
|
278
|
+
<label>
|
279
|
+
<input type="checkbox"> Check me out
|
280
|
+
</label>
|
281
|
+
</div>
|
282
|
+
<button type="submit" class="btn btn-default">Submit</button>
|
283
|
+
</form>
|
284
|
+
BASIC_EXAMPLE
|
285
|
+
}
|
134
286
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
287
|
+
subject {
|
288
|
+
helper.bootstrap_form_for model, url: '/foo' do |f|
|
289
|
+
f.input(:exampleInputEmail1) +
|
290
|
+
f.input(:exampleInputPassword1) +
|
291
|
+
f.input(:exampleInputFile, help: "Example block-level help text here.") +
|
292
|
+
f.input(:check_me_out, as: :boolean)
|
140
293
|
end
|
294
|
+
}
|
141
295
|
|
142
|
-
|
143
|
-
|
296
|
+
it "should generate the correct output"
|
297
|
+
end
|
144
298
|
|
145
|
-
|
299
|
+
describe "Inline form" do
|
300
|
+
let(:inline_form_from_getbootstrap_dot_com) {
|
301
|
+
<<-INLINE_FORM
|
302
|
+
<form class="form-inline" role="form">
|
303
|
+
<div class="form-group">
|
304
|
+
<label class="sr-only" for="exampleInputEmail2">Email address</label>
|
305
|
+
<input type="email" class="form-control" id="exampleInputEmail2" placeholder="Enter email">
|
306
|
+
</div>
|
307
|
+
<div class="form-group">
|
308
|
+
<label class="sr-only" for="exampleInputPassword2">Password</label>
|
309
|
+
<input type="password" class="form-control" id="exampleInputPassword2" placeholder="Password">
|
310
|
+
</div>
|
311
|
+
<div class="checkbox">
|
312
|
+
<label>
|
313
|
+
<input type="checkbox"> Remember me
|
314
|
+
</label>
|
315
|
+
</div>
|
316
|
+
<button type="submit" class="btn btn-default">Sign in</button>
|
317
|
+
</form>
|
318
|
+
INLINE_FORM
|
319
|
+
}
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "Horizontal form" do
|
323
|
+
class Model3
|
324
|
+
include ActiveModel::Validations
|
325
|
+
include ActiveModel::Conversion
|
326
|
+
include ActiveModel::Naming
|
327
|
+
|
328
|
+
def self.model_path
|
329
|
+
"foo"
|
146
330
|
end
|
147
331
|
|
148
|
-
|
149
|
-
|
332
|
+
attr_accessor :inputEmail3
|
333
|
+
attr_accessor :inputPassword3
|
334
|
+
attr_accessor :remember_me
|
335
|
+
end
|
336
|
+
|
337
|
+
let!(:model) do
|
338
|
+
Model3.new
|
339
|
+
end
|
340
|
+
|
341
|
+
let(:horizontal_form_output) {
|
342
|
+
# Original copy from getbootstrap.com
|
343
|
+
#<form class="form-horizontal" role="form">
|
344
|
+
# <div class="form-group">
|
345
|
+
# <label for="inputEmail3" class="col-sm-2 control-label">Email</label>
|
346
|
+
# <div class="col-sm-10">
|
347
|
+
# <input type="email" class="form-control" id="inputEmail3" placeholder="Email">
|
348
|
+
# </div>
|
349
|
+
# </div>
|
350
|
+
# <div class="form-group">
|
351
|
+
# <label for="inputPassword3" class="col-sm-2 control-label">Password</label>
|
352
|
+
# <div class="col-sm-10">
|
353
|
+
# <input type="password" class="form-control" id="inputPassword3" placeholder="Password">
|
354
|
+
# </div>
|
355
|
+
# </div>
|
356
|
+
# <div class="form-group">
|
357
|
+
# <div class="col-sm-offset-2 col-sm-10">
|
358
|
+
# <div class="checkbox">
|
359
|
+
# <label>
|
360
|
+
# <input type="checkbox"> Remember me
|
361
|
+
# </label>
|
362
|
+
# </div>
|
363
|
+
# </div>
|
364
|
+
# </div>
|
365
|
+
# <div class="form-group">
|
366
|
+
# <div class="col-sm-offset-2 col-sm-10">
|
367
|
+
# <button type="submit" class="btn btn-default">Sign in</button>
|
368
|
+
# </div>
|
369
|
+
# </div>
|
370
|
+
#</form>
|
150
371
|
|
151
|
-
|
372
|
+
# Railsified version
|
373
|
+
# <form>
|
374
|
+
# add accept-charset, action, id, method, model-specific class
|
375
|
+
# <label>
|
376
|
+
# changed for= to model_field
|
377
|
+
# <input>
|
378
|
+
# reorder input attributes to match Rails' order
|
379
|
+
# add name=
|
380
|
+
<<-HORIZONTAL_FORM
|
381
|
+
<form accept-charset="UTF-8" action="/foo" class="new_model3 form-horizontal" id="new_model3" method="post" role="form">
|
382
|
+
<div style=\"margin:0;padding:0;display:inline\">
|
383
|
+
<input name="utf8" type="hidden" value="✓" />
|
384
|
+
</div>
|
385
|
+
<div class="form-group">
|
386
|
+
<label class="col-sm-2 control-label" for="model3_inputEmail3">Email</label>
|
387
|
+
<div class="col-sm-10">
|
388
|
+
<input class="form-control" id="model3_inputEmail3" name="model3[inputEmail3]" placeholder="Email" type="email" />
|
389
|
+
</div>
|
390
|
+
</div>
|
391
|
+
<div class="form-group">
|
392
|
+
<label class="col-sm-2 control-label" for="model3_inputPassword3">Password</label>
|
393
|
+
<div class="col-sm-10">
|
394
|
+
<input class="form-control" id="model3_inputPassword3" name="model3[inputPassword3]" placeholder="Password" type="password" />
|
395
|
+
</div>
|
396
|
+
</div>
|
397
|
+
<div class="form-group">
|
398
|
+
<div class="col-sm-offset-2 col-sm-10">
|
399
|
+
<div class="checkbox">
|
400
|
+
<label>
|
401
|
+
<input type="checkbox"> Remember me
|
402
|
+
</label>
|
403
|
+
</div>
|
404
|
+
</div>
|
405
|
+
</div>
|
406
|
+
<div class="form-group">
|
407
|
+
<div class="col-sm-offset-2 col-sm-10">
|
408
|
+
<button type="submit" class="btn btn-default">Sign in</button>
|
409
|
+
</div>
|
410
|
+
</div>
|
411
|
+
</form>
|
412
|
+
HORIZONTAL_FORM
|
413
|
+
}
|
414
|
+
|
415
|
+
subject {
|
416
|
+
helper.bootstrap_form_for model,
|
417
|
+
layout: :horizontal,
|
418
|
+
label_size: 'col-sm-2',
|
419
|
+
input_size: 'col-sm-10',
|
420
|
+
group_class: false,
|
421
|
+
url: '/foo' do |f|
|
422
|
+
f.input(:inputEmail3, label: "Email", placeholder: "Email") +
|
423
|
+
f.input(:inputPassword3, label: "Password", placeholder: "Password") +
|
424
|
+
f.input(:remember_me, as: :boolean, label_size: 'col-sm-offset-2 col-sm-10')
|
152
425
|
end
|
426
|
+
}
|
427
|
+
|
428
|
+
it "should generate the correct output" do
|
429
|
+
#expect(pretty_print subject).to eq horizontal_form_output
|
153
430
|
end
|
154
431
|
end
|
155
432
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :have_element do |selector|
|
4
|
+
match_for_should do |markup|
|
5
|
+
@selector = selector
|
6
|
+
@markup = markup
|
7
|
+
element_is_present && classes_match
|
8
|
+
end
|
9
|
+
|
10
|
+
match_for_should_not do |markup|
|
11
|
+
@selector = selector
|
12
|
+
@markup = markup
|
13
|
+
!element_is_present || !classes_match
|
14
|
+
end
|
15
|
+
|
16
|
+
failure_message_for_should do |markup|
|
17
|
+
if the_element.nil?
|
18
|
+
"expected to find an element matching #{selector} in #{markup}"
|
19
|
+
elsif @expected_classes
|
20
|
+
actual_classes = @element['class'].split(' ')
|
21
|
+
"expected #{inspect_the_element} to have classes \"#{@expected_classes.sort.join(' ')}\" but it has classes \"#{actual_classes.sort.join(' ')}\""
|
22
|
+
else
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
failure_message_for_should_not do |markup|
|
27
|
+
"expected #{inspect_the_element} not to have classes \"#{@expected_classes.sort.join(' ')}\" in #{markup}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def element_is_present
|
31
|
+
the_element.is_a? Nokogiri::XML::Element
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_element(element)
|
35
|
+
if @expected_classes
|
36
|
+
else
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
########## Chain assertions that will affect the selector
|
42
|
+
|
43
|
+
chain :with_id do |id|
|
44
|
+
@id = id
|
45
|
+
end
|
46
|
+
|
47
|
+
chain :with_class do |css_class|
|
48
|
+
@class = css_class
|
49
|
+
end
|
50
|
+
|
51
|
+
def attrs
|
52
|
+
@attrs ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
chain :with_attr_value do |attr, value|
|
56
|
+
attrs[attr] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
chain :with_type do |type|
|
60
|
+
attrs['type'] = type
|
61
|
+
end
|
62
|
+
|
63
|
+
chain :with_placeholder do |placeholder|
|
64
|
+
attrs['placeholder'] = placeholder
|
65
|
+
end
|
66
|
+
|
67
|
+
def actual_selector
|
68
|
+
@actual_selector ||= begin
|
69
|
+
s = @selector.to_s.dup
|
70
|
+
s << "##{@id}" if @id
|
71
|
+
s << ".#{@class}" if @class
|
72
|
+
attrs.each do |attr, value|
|
73
|
+
s << "[#{attr}=\"#{value}\"]"
|
74
|
+
end
|
75
|
+
s
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
########## Chain assertions that are run once the tag has been recovered
|
80
|
+
|
81
|
+
chain :with_only_classes do |classes|
|
82
|
+
@expected_classes = classes.split ' '
|
83
|
+
end
|
84
|
+
|
85
|
+
def actual_classes
|
86
|
+
the_element['class'].split ' '
|
87
|
+
end
|
88
|
+
|
89
|
+
def classes_match
|
90
|
+
return true unless @expected_classes
|
91
|
+
actual_classes.sort == @expected_classes.sort
|
92
|
+
end
|
93
|
+
|
94
|
+
########## Utility methods
|
95
|
+
|
96
|
+
def the_element
|
97
|
+
@element ||= begin
|
98
|
+
doc = Nokogiri::XML @markup
|
99
|
+
elements = doc.css actual_selector
|
100
|
+
raise "found multiple elements matching \"#{actual_selector}\"" if elements.count > 1
|
101
|
+
elements.first
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def inspect_the_element
|
106
|
+
the_element.children = ""
|
107
|
+
the_element
|
108
|
+
end
|
109
|
+
end
|