formulary 0.0.1 → 0.0.2

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 (34) hide show
  1. data/README.md +50 -6
  2. data/formulary.gemspec +1 -0
  3. data/lib/formulary.rb +13 -0
  4. data/lib/formulary/html_form.rb +39 -58
  5. data/lib/formulary/html_form/fields.rb +21 -0
  6. data/lib/formulary/html_form/fields/checkbox_group.rb +43 -0
  7. data/lib/formulary/html_form/fields/email_input.rb +23 -0
  8. data/lib/formulary/html_form/fields/field.rb +45 -0
  9. data/lib/formulary/html_form/fields/field_group.rb +26 -0
  10. data/lib/formulary/html_form/fields/hidden_input.rb +7 -0
  11. data/lib/formulary/html_form/fields/input.rb +32 -0
  12. data/lib/formulary/html_form/fields/radio_button_group.rb +29 -0
  13. data/lib/formulary/html_form/fields/select.rb +24 -0
  14. data/lib/formulary/html_form/fields/tel_input.rb +7 -0
  15. data/lib/formulary/html_form/fields/text_input.rb +7 -0
  16. data/lib/formulary/html_form/fields/textarea.rb +12 -0
  17. data/lib/formulary/version.rb +1 -1
  18. data/spec/html_form/fields/checkbox_group_spec.rb +141 -0
  19. data/spec/html_form/fields/email_input_spec.rb +55 -0
  20. data/spec/html_form/fields/field_spec.rb +10 -0
  21. data/spec/html_form/fields/hidden_input_spec.rb +37 -0
  22. data/spec/html_form/fields/input_spec.rb +17 -0
  23. data/spec/html_form/fields/radio_button_group_spec.rb +94 -0
  24. data/spec/html_form/fields/select_spec.rb +62 -0
  25. data/spec/html_form/fields/tel_input_spec.rb +29 -0
  26. data/spec/html_form/fields/text_input_spec.rb +29 -0
  27. data/spec/html_form/fields/textarea_spec.rb +28 -0
  28. data/spec/html_form_spec.rb +53 -109
  29. data/spec/spec_helper.rb +5 -10
  30. data/spec/support/element_helper.rb +15 -0
  31. data/spec/support/shared_examples_for_pattern.rb +15 -0
  32. data/spec/support/shared_examples_for_required.rb +35 -0
  33. metadata +149 -94
  34. checksums.yaml +0 -7
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::Select do
4
+ describe ".compatible_with?" do
5
+ subject { Formulary::HtmlForm::Fields::Select.compatible_with?(element) }
6
+
7
+ context "with a select" do
8
+ let(:markup) { %{<select name="name"></select>} }
9
+ it { should be_true }
10
+ end
11
+
12
+ context "with a textarea" do
13
+ let(:markup) { %{<textarea name="name"></textarea>} }
14
+ it { should be_false }
15
+ end
16
+
17
+ context "with an input type" do
18
+ let(:markup) { %{<input type="text" name="name" />} }
19
+ it { should be_false }
20
+ end
21
+ end
22
+
23
+ describe "validations" do
24
+ subject(:input) { Formulary::HtmlForm::Fields::Select.new(element) }
25
+ let(:markup) do
26
+ <<-EOS
27
+ <select name="name">
28
+ <option>First Text</option>
29
+ <option value="second_value">Second Text</option>
30
+ </select>
31
+ EOS
32
+ end
33
+
34
+ context "passed the text from a valueless option" do
35
+ before { input.set_value("First Text") }
36
+
37
+ it { should be_valid }
38
+ its(:error) { should be_blank }
39
+ end
40
+
41
+ context "passed the value from an option with a value" do
42
+ before { input.set_value("second_value") }
43
+
44
+ it { should be_valid }
45
+ its(:error) { should be_blank }
46
+ end
47
+
48
+ context "passed the text from a value with an option" do
49
+ before { input.set_value("Second Text") }
50
+
51
+ it { should_not be_valid }
52
+ its(:error) { should include("choose") }
53
+ end
54
+
55
+ context "passed a value that is not in the options" do
56
+ before { input.set_value("unknown") }
57
+
58
+ it { should_not be_valid }
59
+ its(:error) { should include("choose") }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::TelInput do
4
+ describe ".compatible_with?" do
5
+ subject { Formulary::HtmlForm::Fields::TelInput.compatible_with?(element) }
6
+ let(:markup) { %{<input type="#{type}" name="name" />} }
7
+
8
+ context "text type" do
9
+ let(:type) { "text" }
10
+ it { should be_false }
11
+ end
12
+
13
+ context "tel type" do
14
+ let(:type) { "tel" }
15
+ it { should be_true }
16
+ end
17
+ end
18
+
19
+ it_should_behave_like "a field that allows the required attribute" do
20
+ let(:markup_with_required) { %{<input type="tel" name="field" required />} }
21
+ let(:markup_without_required) { %{<input type="tel" name="field" />} }
22
+ let(:valid_value) { "123-123-1234" }
23
+ end
24
+
25
+ it_should_behave_like "a field that allows the pattern attribute" do
26
+ let(:markup) { '<input type="tel" name="field" pattern="\d{3}-\d{3}-\d{4}" />' }
27
+ let(:valid_value) { "123-123-1234" }
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::TextInput do
4
+ describe ".compatible_with?" do
5
+ subject { Formulary::HtmlForm::Fields::TextInput.compatible_with?(element) }
6
+ let(:markup) { %{<input type="#{type}" name="name" />} }
7
+
8
+ context "text type" do
9
+ let(:type) { "text" }
10
+ it { should be_true }
11
+ end
12
+
13
+ context "email type" do
14
+ let(:type) { "email" }
15
+ it { should be_false }
16
+ end
17
+ end
18
+
19
+ it_should_behave_like "a field that allows the required attribute" do
20
+ let(:markup_with_required) { %{<input type="text" name="field" required />} }
21
+ let(:markup_without_required) { %{<input type="text" name="field" />} }
22
+ let(:valid_value) { "test" }
23
+ end
24
+
25
+ it_should_behave_like "a field that allows the pattern attribute" do
26
+ let(:markup) { %{<input type="text" name="field" pattern="^test$" />} }
27
+ let(:valid_value) { "test" }
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::Textarea do
4
+ describe ".compatible_with?" do
5
+ subject { Formulary::HtmlForm::Fields::Textarea.compatible_with?(element) }
6
+
7
+ context "with a textarea" do
8
+ let(:markup) { %{<textarea name="name"></textarea>} }
9
+ it { should be_true }
10
+ end
11
+
12
+ context "with a select" do
13
+ let(:markup) { %{<select name="name"></select>} }
14
+ it { should be_false }
15
+ end
16
+
17
+ context "with an input type" do
18
+ let(:markup) { %{<input type="text" name="name" />} }
19
+ it { should be_false }
20
+ end
21
+ end
22
+
23
+ it_should_behave_like "a field that allows the required attribute" do
24
+ let(:markup_with_required) { %{<textarea name="field" required></textarea>} }
25
+ let(:markup_without_required) { %{<textarea name="field"></textarea>} }
26
+ let(:valid_value) { "test" }
27
+ end
28
+ end
@@ -35,152 +35,96 @@ describe Formulary::HtmlForm do
35
35
  </div>
36
36
 
37
37
  <div class="field">
38
- <label for="street_address">Street Address</label>
39
- <input type="text" name="street_address" />
38
+ <label for="message">Message</label>
39
+ <textarea name="message"></textarea>
40
40
  </div>
41
41
 
42
42
  <div class="field">
43
- <label for="city">City</label>
44
- <input type="text" name="city" pattern="^[A-Za-z]*$" />
43
+ <label for="unit">Unit</label>
44
+ <select name="unit">
45
+ <option>5x5</option>
46
+ <option value="5x10">Five By Ten</option>
47
+ </select>
45
48
  </div>
46
49
 
47
- <div class="field">
48
- <label for="state">State</label>
49
- <input type="text" name="state" />
50
- </div>
50
+ <input type="radio" name="foods" value="bacon">Bacon<br>
51
+ <input type="radio" name="foods" value="butter" checked>Butter<br>
51
52
 
52
- <div class="field">
53
- <label for="zip">Zip</label>
54
- <input type="text" name="zip" />
55
- </div>
53
+ <input type="radio" name="beverages" value="water">Water<br>
56
54
 
57
- <div class="field">
58
- <label for="message">Message</label>
59
- <textarea name="message"></textarea>
60
- </div>
55
+ <input type="hidden" name="syndication_url" value="example.com" />
61
56
 
62
- <input type="submit" value="Apply" />
57
+ <input type="checkbox" name="terms">
63
58
 
59
+ <input type="submit" value="Apply" />
64
60
  </form>
65
61
  </div>
66
62
  EOS
67
63
  end
68
64
 
69
- def valid_params(hash = {})
70
- {
71
- first_name: "First",
72
- last_name: "Last",
73
- email: "test@test.com",
74
- g5_email: "test@getg5.com"
75
- }.merge(hash)
76
- end
77
-
78
65
  let(:html_form) { Formulary::HtmlForm.new(markup) }
79
66
 
80
- describe "#fields" do
81
- let(:fields) { html_form.fields }
82
- subject { fields }
83
-
84
- its(:length) { should eq(10) }
85
-
86
- describe "a required text input" do
87
- subject { fields.first }
88
-
89
- its(:name) { should eq("first_name") }
90
- its(:type) { should eq("text") }
91
- its(:required) { should be_true }
92
- end
93
-
94
- describe "a textarea that is not required" do
95
- subject { fields[9] }
96
-
97
- its(:name) { should eq("message") }
98
- its(:type) { should eq("textarea") }
99
- its(:required) { should be_false }
100
- end
101
-
102
- describe "an input with a pattern attribute" do
103
- subject { fields[6] }
67
+ context "unsupported field type" do
68
+ let(:markup) { %{<input type="bacon" name="delicious" />} }
104
69
 
105
- its(:pattern) { should eq("^[A-Za-z]*$") }
70
+ it "explodes" do
71
+ expect { html_form }.to raise_error(Formulary::UnsupportedFieldType, /bacon/)
106
72
  end
107
73
  end
108
74
 
109
- describe "#valid?" do
110
- subject(:valid) { html_form.valid?(params) }
111
- #before { subject }
112
-
113
- context "with valid parameters" do
114
- let(:params) { valid_params }
115
- it { should be_true }
75
+ describe "validations" do
76
+ let(:valid_params) do
77
+ {
78
+ first_name: "First",
79
+ last_name: "Last",
80
+ email: "test@test.com",
81
+ g5_email: "test@getg5.com",
82
+ unit: "5x5"
83
+ }
116
84
  end
117
85
 
118
- context "with valid parameters" do
119
- context "phone number" do
120
- let(:params) { valid_params(phone: "+1 456 123987") }
86
+ describe "#valid?" do
87
+ subject(:valid) { html_form.valid?(params) }
88
+
89
+ context "with a valid submission" do
90
+ let(:params) { valid_params }
121
91
  it { should be_true }
122
92
  end
123
- end
124
-
125
- context "with invalid parameters" do
126
- context "due to missing parameters" do
127
- let(:params) { { last_name: "" } }
128
93
 
94
+ context "with an invalid submission" do
95
+ let(:params) { {} }
129
96
  it { should be_false }
130
-
131
- it "has an error for an omitted field" do
132
- html_form.errors.keys.should include("first_name")
133
- html_form.errors["first_name"].should include("required")
134
- end
135
-
136
- it "has an error for a blank field" do
137
- html_form.errors.keys.should include("last_name")
138
- html_form.errors["last_name"].should include("required")
139
- end
140
97
  end
141
98
 
142
- context "due to failing regex check" do
143
- let(:params) { valid_params(city: "New York") }
144
-
145
- it { should be_false }
99
+ context "due to unexpected parameters" do
100
+ let(:params) { valid_params.merge(extra: "test") }
146
101
 
147
- it "has an error for the field that doesn't match the pattern" do
148
- valid
149
- html_form.errors.keys.should include("city")
150
- html_form.errors["city"].should include("format")
102
+ it "raises a Formulary::UnexpectedParameter exception" do
103
+ expect { valid }.to raise_error(Formulary::UnexpectedParameter, /extra/)
151
104
  end
152
105
  end
106
+ end
153
107
 
154
- context "due to invalid email address" do
155
- let(:params) { valid_params(email: "testing") }
156
-
157
- it { should be_false }
108
+ describe "#errors" do
109
+ before { html_form.valid?(params) }
110
+ subject { html_form.errors }
158
111
 
159
- it "has an error for the email field" do
160
- valid
161
- html_form.errors.keys.should include("email")
162
- html_form.errors["email"].should include("not a valid email")
163
- end
112
+ context "with a valid submission" do
113
+ let(:params) { valid_params }
114
+ it { should be_empty }
164
115
  end
165
116
 
166
- context "due to invalid g5 email address" do
167
- let(:params) { valid_params(g5_email: "test@test.com") }
168
-
169
- it { should be_false }
170
-
171
- it "has an error for the g5 email field" do
172
- valid
173
- html_form.errors.keys.should include("g5_email")
174
- html_form.errors["g5_email"].should include("format")
117
+ context "with an invalid submission" do
118
+ let(:params) do
119
+ {
120
+ email: "invalid", g5_email: "test@example.com", foods: "water"
121
+ }
175
122
  end
176
- end
177
-
178
- context "due to unexpected parameters" do
179
- let(:params) { valid_params(extra: "test") }
180
123
 
181
- it "raises a Formulary::UnexpectedParameter exception" do
182
- expect { valid }.to raise_error(Formulary::UnexpectedParameter, /extra/)
183
- end
124
+ its(["first_name"]) { should include("required") }
125
+ its(["email"]) { should include("email") }
126
+ its(["g5_email"]) { should include("format") }
127
+ its(["foods"]) { should include("choose") }
184
128
  end
185
129
  end
186
130
  end
data/spec/spec_helper.rb CHANGED
@@ -1,18 +1,13 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
1
+ require 'pry'
2
+ require 'support/shared_examples_for_pattern'
3
+ require 'support/shared_examples_for_required'
4
+ require 'support/element_helper'
5
+
7
6
  RSpec.configure do |config|
8
7
  config.treat_symbols_as_metadata_keys_with_true_values = true
9
8
  config.run_all_when_everything_filtered = true
10
9
  config.filter_run :focus
11
10
 
12
- # Run specs in random order to surface order dependencies. If you find an
13
- # order dependency and want to debug it, you can fix the order by providing
14
- # the seed, which is printed after each run.
15
- # --seed 1234
16
11
  config.order = 'random'
17
12
  end
18
13
 
@@ -0,0 +1,15 @@
1
+ module ElementHelper
2
+ def element
3
+ @element ||= elements.first
4
+ end
5
+
6
+ def elements
7
+ @elements ||= \
8
+ Nokogiri::HTML("<html><body>#{markup}</body></html>").
9
+ css("body > *")
10
+ end
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.include ElementHelper
15
+ end
@@ -0,0 +1,15 @@
1
+ shared_examples_for "a field that allows the pattern attribute" do
2
+ subject(:input) { described_class.new(element) }
3
+
4
+ context "with a matching value" do
5
+ before { input.set_value(valid_value) }
6
+ it { should be_valid }
7
+ its(:error) { should be_blank }
8
+ end
9
+
10
+ context "without a matching value" do
11
+ before { input.set_value("z") }
12
+ it { should_not be_valid }
13
+ its(:error) { should include("format") }
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ shared_examples_for "a field that allows the required attribute" do
2
+ subject(:input) { described_class.new(element) }
3
+
4
+ context "required" do
5
+ let(:markup) { markup_with_required }
6
+
7
+ context "with a submitted value" do
8
+ before { input.set_value(valid_value) }
9
+ it { should be_valid }
10
+ its(:error) { should be_blank }
11
+ end
12
+
13
+ context "submitted a blank value" do
14
+ before { input.set_value("") }
15
+ it { should_not be_valid }
16
+ its(:error) { should include("required") }
17
+ end
18
+
19
+ context "submitted a nil value" do
20
+ before { input.set_value(nil) }
21
+ it { should_not be_valid }
22
+ its(:error) { should include("required") }
23
+ end
24
+ end
25
+
26
+ context "not required" do
27
+ let(:markup) { markup_without_required }
28
+
29
+ context "without a submitted value" do
30
+ before { input.set_value("") }
31
+ it { should be_valid }
32
+ its(:error) { should be_blank }
33
+ end
34
+ end
35
+ end