formulary 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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +65 -34
  3. data/formulary.gemspec +0 -1
  4. data/lib/formulary.rb +1 -5
  5. data/lib/formulary/html_form.rb +31 -13
  6. data/lib/formulary/html_form/fields.rb +16 -0
  7. data/lib/formulary/html_form/fields/checkbox_group.rb +1 -1
  8. data/lib/formulary/html_form/fields/color_input.rb +24 -0
  9. data/lib/formulary/html_form/fields/date_input.rb +59 -0
  10. data/lib/formulary/html_form/fields/email_input.rb +11 -2
  11. data/lib/formulary/html_form/fields/field.rb +15 -3
  12. data/lib/formulary/html_form/fields/field_group.rb +3 -3
  13. data/lib/formulary/html_form/fields/input.rb +1 -1
  14. data/lib/formulary/html_form/fields/month_input.rb +15 -0
  15. data/lib/formulary/html_form/fields/number_input.rb +63 -0
  16. data/lib/formulary/html_form/fields/password_input.rb +7 -0
  17. data/lib/formulary/html_form/fields/range_input.rb +7 -0
  18. data/lib/formulary/html_form/fields/search_input.rb +7 -0
  19. data/lib/formulary/html_form/fields/select.rb +1 -1
  20. data/lib/formulary/html_form/fields/week_input.rb +15 -0
  21. data/lib/formulary/html_form/labels.rb +39 -0
  22. data/lib/formulary/version.rb +1 -1
  23. data/spec/html_form/fields/checkbox_group_spec.rb +23 -10
  24. data/spec/html_form/fields/color_input_spec.rb +58 -0
  25. data/spec/html_form/fields/date_input_spec.rb +60 -0
  26. data/spec/html_form/fields/email_input_spec.rb +16 -6
  27. data/spec/html_form/fields/field_spec.rb +40 -1
  28. data/spec/html_form/fields/hidden_input_spec.rb +3 -1
  29. data/spec/html_form/fields/month_input_spec.rb +60 -0
  30. data/spec/html_form/fields/number_input_spec.rb +67 -0
  31. data/spec/html_form/fields/password_input_spec.rb +29 -0
  32. data/spec/html_form/fields/radio_button_group_spec.rb +27 -8
  33. data/spec/html_form/fields/range_input_spec.rb +20 -0
  34. data/spec/html_form/fields/search_input_spec.rb +18 -0
  35. data/spec/html_form/fields/select_spec.rb +10 -7
  36. data/spec/html_form/fields/tel_input_spec.rb +3 -3
  37. data/spec/html_form/fields/text_input_spec.rb +3 -3
  38. data/spec/html_form/fields/textarea_spec.rb +2 -2
  39. data/spec/html_form/fields/week_input_spec.rb +60 -0
  40. data/spec/html_form_spec.rb +234 -100
  41. data/spec/support/element_helper.rb +1 -1
  42. data/spec/support/shared_examples_for_pattern.rb +4 -2
  43. data/spec/support/shared_examples_for_required.rb +4 -2
  44. metadata +114 -106
@@ -6,8 +6,8 @@ module Formulary::HtmlForm::Fields
6
6
  end
7
7
  end
8
8
 
9
- def initialize(group_name, elements)
10
- @group_name, @elements = group_name, elements
9
+ def initialize(html_form, group_name, elements)
10
+ @html_form, @group_name, @elements = html_form, group_name, elements
11
11
  end
12
12
 
13
13
  def name
@@ -20,7 +20,7 @@ module Formulary::HtmlForm::Fields
20
20
 
21
21
  def error
22
22
  return super if super.present?
23
- return "choose" if !value_in_list?
23
+ return "'#{label}' must be chosen from the available options" if !value_in_list?
24
24
  end
25
25
  end
26
26
  end
@@ -15,7 +15,7 @@ module Formulary::HtmlForm::Fields
15
15
 
16
16
  def error
17
17
  return super if super.present?
18
- return "format" unless pattern_correct?
18
+ return "'#{label}' does not match the expected format" unless pattern_correct?
19
19
  end
20
20
 
21
21
  protected
@@ -0,0 +1,15 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class MonthInput < DateInput
3
+ def self.compatible_type
4
+ "month"
5
+ end
6
+
7
+ def display_format
8
+ "YYYY-MM"
9
+ end
10
+
11
+ def datetime_format
12
+ "%Y-%m"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class NumberInput < Input
3
+ def self.compatible_type
4
+ "number"
5
+ end
6
+
7
+ def initialize(html_form, element)
8
+ @html_form, @element = html_form, element
9
+ @min = @element.attributes['min'].try('value')
10
+ @max = @element.attributes['max'].try('value')
11
+ @step = @element.attributes['step'].try('value')
12
+ end
13
+
14
+ def valid?
15
+ super && number_correct? && min_correct? && max_correct? && step_correct?
16
+ end
17
+
18
+ def error
19
+ return super if super.present?
20
+ return "'#{label}' must be a valid number" unless number_correct?
21
+ return "'#{label}' must be greater than or equal to #{@min}" unless min_correct?
22
+ return "'#{label}' must be less than or equal to #{@max}" unless max_correct?
23
+ return "'#{label}' must be a step of #{@step}, the nearest valid values are #{@lower_step} and #{@higher_step}" unless step_correct?
24
+ end
25
+
26
+ protected
27
+
28
+ def number_correct?
29
+ return true if @value.blank?
30
+ return true if @value.match(/^\d*$/)
31
+ false
32
+ end
33
+
34
+ def min_correct?
35
+ return true if @value.blank?
36
+ return true if @min.blank?
37
+ return true if @value.to_i >= @min.to_i
38
+ false
39
+ end
40
+
41
+ def max_correct?
42
+ return true if @value.blank?
43
+ return true if @max.blank?
44
+ return true if @value.to_i <= @max.to_i
45
+ false
46
+ end
47
+
48
+ def step_correct?
49
+ return true if @value.blank?
50
+ return true if @step.blank?
51
+ start = @min.to_i || 0
52
+ step = @step.to_i
53
+ value = @value.to_i
54
+ match_mod = start % step
55
+ return true if value % step == match_mod
56
+
57
+ @lower_step = value - (value % step)
58
+ @higher_step = value + (step - (value % step))
59
+
60
+ false
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,7 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class PasswordInput < Input
3
+ def self.compatible_type
4
+ "password"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class RangeInput < NumberInput
3
+ def self.compatible_type
4
+ "range"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class SearchInput < Input
3
+ def self.compatible_type
4
+ "search"
5
+ end
6
+ end
7
+ end
@@ -9,7 +9,7 @@ module Formulary::HtmlForm::Fields
9
9
  end
10
10
 
11
11
  def error
12
- return "choose" unless valid?
12
+ return "'#{label}' must be chosen from the available options" unless valid?
13
13
  end
14
14
 
15
15
  protected
@@ -0,0 +1,15 @@
1
+ module Formulary::HtmlForm::Fields
2
+ class WeekInput < DateInput
3
+ def self.compatible_type
4
+ "week"
5
+ end
6
+
7
+ def display_format
8
+ "YYYY-W##"
9
+ end
10
+
11
+ def datetime_format
12
+ "%Y-W%U"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ module Formulary::HtmlForm::Labels
2
+ def label_for_field(field_name)
3
+ fields_for_name = document.css("*[name='#{field_name}']")
4
+
5
+ if fields_for_name.length > 1
6
+ labels_for_fieldset(fields_for_name)
7
+ else
8
+ label_for_single_field(fields_for_name.first)
9
+ end
10
+ end
11
+
12
+ protected
13
+
14
+ def labels_for_fieldset(fields_for_name)
15
+ labels = fields_for_name.each_with_object({}) do |n, h|
16
+ h[n["value"]] = n.ancestors("label").first.text
17
+ end
18
+
19
+ fieldset_legend = fields_for_name.first.ancestors("fieldset").css("legend")
20
+ labels["fieldset"] = fieldset_legend.text
21
+ labels
22
+ end
23
+
24
+ def label_for_single_field(field)
25
+ input_id = field["id"]
26
+ label = document.css("label[for='#{input_id}']")
27
+
28
+ if label.empty?
29
+ parent_label = field.ancestors("label").first
30
+ if parent_label.nil?
31
+ nil
32
+ else
33
+ parent_label.text.strip
34
+ end
35
+ else
36
+ label.text
37
+ end
38
+ end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module Formulary
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,6 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Formulary::HtmlForm::Fields::CheckboxGroup do
4
+ let(:markup_with_label) do
5
+ "<fieldset><legend>Field</legend>#{markup}</fieldset>"
6
+ end
7
+ let(:html_form) { Formulary::HtmlForm.new(markup_with_label) }
8
+
4
9
  describe ".compatible_with?" do
5
10
  subject { Formulary::HtmlForm::Fields::CheckboxGroup.compatible_with?(elements) }
6
11
 
@@ -40,14 +45,18 @@ describe Formulary::HtmlForm::Fields::CheckboxGroup do
40
45
 
41
46
  describe "validations" do
42
47
  subject(:checkbox_group) do
43
- Formulary::HtmlForm::Fields::CheckboxGroup.new("food", elements)
48
+ Formulary::HtmlForm::Fields::CheckboxGroup.new(html_form, "field", elements)
44
49
  end
45
50
 
46
51
  context "when one of the items in the group is required" do
47
52
  let(:markup) do
48
53
  %{
49
- <input type="checkbox" name="field" value="first" required>
50
- <input type="checkbox" name="field" value="second">
54
+ <label>
55
+ First<input type="checkbox" name="field" value="first" required>
56
+ </label>
57
+ <label>
58
+ Second<input type="checkbox" name="field" value="second">
59
+ </label>
51
60
  }
52
61
  end
53
62
 
@@ -69,27 +78,31 @@ describe Formulary::HtmlForm::Fields::CheckboxGroup do
69
78
  before { checkbox_group.set_value("second") }
70
79
 
71
80
  it { should_not be_valid }
72
- its(:error) { should include("required") }
81
+ its(:error) { should eql("'Field' is required") }
73
82
  end
74
83
 
75
84
  context "with a value that isn't expected" do
76
85
  before { checkbox_group.set_value([ "first", "invalid" ]) }
77
86
 
78
87
  it { should_not be_valid }
79
- its(:error) { should include("choose") }
88
+ its(:error) { should eql("'Field' must be chosen from the available options") }
80
89
  end
81
90
 
82
91
  context "with no value" do
83
92
  it { should_not be_valid }
84
- its(:error) { should include("required") }
93
+ its(:error) { should eql("'Field' is required") }
85
94
  end
86
95
  end
87
96
 
88
97
  context "when none are required" do
89
98
  let(:markup) do
90
99
  %{
91
- <input type="checkbox" name="field" value="first">
92
- <input type="checkbox" name="field" value="second">
100
+ <label>
101
+ <input type="checkbox" name="field" value="first">
102
+ </label>
103
+ <label>
104
+ <input type="checkbox" name="field" value="second">
105
+ </label>
93
106
  }
94
107
  end
95
108
 
@@ -97,7 +110,7 @@ describe Formulary::HtmlForm::Fields::CheckboxGroup do
97
110
  before { checkbox_group.set_value("invalid") }
98
111
 
99
112
  it { should_not be_valid }
100
- its(:error) { should include("choose") }
113
+ its(:error) { should eql("'Field' must be chosen from the available options") }
101
114
  end
102
115
 
103
116
  context "with no value" do
@@ -109,7 +122,7 @@ describe Formulary::HtmlForm::Fields::CheckboxGroup do
109
122
  before { checkbox_group.set_value("on") }
110
123
 
111
124
  it { should_not be_valid }
112
- its(:error) { should include("choose") }
125
+ its(:error) { should eql("'Field' must be chosen from the available options") }
113
126
  end
114
127
  end
115
128
 
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::ColorInput do
4
+ let(:markup_with_label) { "<label for='field'>Field</label>#{markup}" }
5
+ let(:html_form) { Formulary::HtmlForm.new(markup_with_label) }
6
+
7
+ describe ".compatible_with?" do
8
+ subject { Formulary::HtmlForm::Fields::ColorInput.compatible_with?(element) }
9
+ let(:markup) { %{<input type="#{type}" name="name" />} }
10
+
11
+ context "color type" do
12
+ let(:type) { "color" }
13
+ it { should be_true }
14
+ end
15
+
16
+ context "text type" do
17
+ let(:type) { "text" }
18
+ it { should be_false }
19
+ end
20
+ end
21
+
22
+ describe "validations" do
23
+ subject(:input) { Formulary::HtmlForm::Fields::ColorInput.new(html_form, element) }
24
+ let(:markup) { %{<input type="color" id="field" name="name" />} }
25
+
26
+ context "with a valid color" do
27
+ before { input.set_value("#123DEF") }
28
+
29
+ it { should be_valid }
30
+ its(:error) { should be_blank }
31
+ end
32
+
33
+ context "with an invalid color address" do
34
+ before { input.set_value("invalid!") }
35
+
36
+ it { should_not be_valid }
37
+ its(:error) { should eql("'Field' is not a valid color hex value") }
38
+ end
39
+
40
+ context "with a nil color" do
41
+ before { input.set_value(nil) }
42
+
43
+ it { should be_valid }
44
+ its(:error) { should be_blank }
45
+ end
46
+ end
47
+
48
+ it_should_behave_like "a field that allows the required attribute" do
49
+ let(:markup_with_required) { %{<input type="color" id="field" name="field" required />} }
50
+ let(:markup_without_required) { %{<input type="color" id="field" name="field" />} }
51
+ let(:valid_value) { "#123456" }
52
+ end
53
+
54
+ it_should_behave_like "a field that allows the pattern attribute" do
55
+ let(:markup) { %{<input type="color" id="field" name="field" pattern="^#[A-F]*$" />} }
56
+ let(:valid_value) { "#ABCDEF" }
57
+ end
58
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Formulary::HtmlForm::Fields::DateInput do
4
+ let(:markup_with_label) { "<label for='field'>Field</label>#{markup}" }
5
+ let(:html_form) { Formulary::HtmlForm.new(markup_with_label) }
6
+
7
+ describe ".compatible_with?" do
8
+ subject { Formulary::HtmlForm::Fields::DateInput.compatible_with?(element) }
9
+ let(:markup) { %{<input type="#{type}" name="name" />} }
10
+
11
+ context "text type" do
12
+ let(:type) { "text" }
13
+ it { should be_false }
14
+ end
15
+
16
+ context "date type" do
17
+ let(:type) { "date" }
18
+ it { should be_true }
19
+ end
20
+ end
21
+
22
+ it_should_behave_like "a field that allows the required attribute" do
23
+ let(:markup_with_required) { %{<input type="date" id="field" name="field" required />} }
24
+ let(:markup_without_required) { %{<input type="date" id="field" name="field" />} }
25
+ let(:valid_value) { "2014-01-01" }
26
+ end
27
+
28
+ describe "validations" do
29
+ subject(:input) { Formulary::HtmlForm::Fields::DateInput.new(html_form, element) }
30
+ let(:markup) { %{<input type="date" id="field" name="test" min="2013-12-31" max="2014-02-01" />} }
31
+
32
+ context "with a valid date" do
33
+ before { input.set_value("2014-01-01") }
34
+
35
+ it { should be_valid }
36
+ its(:error) { should be_blank }
37
+ end
38
+
39
+ context "with a invalid date" do
40
+ before { input.set_value("1/5/14") }
41
+
42
+ it { should_not be_valid }
43
+ its(:error) { should eql("'Field' is not a properly formatted date, please use YYYY-MM-DD") }
44
+ end
45
+
46
+ context "with a date less than the min" do
47
+ before { input.set_value("2013-01-01") }
48
+
49
+ it { should_not be_valid }
50
+ its(:error) { should eql("'Field' must be a date after 2013-12-31") }
51
+ end
52
+
53
+ context "with a date greater than the max" do
54
+ before { input.set_value("2015-01-01") }
55
+
56
+ it { should_not be_valid }
57
+ its(:error) { should eql("'Field' must be a date before 2014-02-01") }
58
+ end
59
+ end
60
+ end
@@ -1,6 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Formulary::HtmlForm::Fields::EmailInput do
4
+ let(:markup_with_label) { "<label for='field'>Field</label>#{markup}" }
5
+ let(:html_form) { Formulary::HtmlForm.new(markup_with_label) }
6
+
4
7
  describe ".compatible_with?" do
5
8
  subject { Formulary::HtmlForm::Fields::EmailInput.compatible_with?(element) }
6
9
  let(:markup) { %{<input type="#{type}" name="name" />} }
@@ -17,19 +20,19 @@ describe Formulary::HtmlForm::Fields::EmailInput do
17
20
  end
18
21
 
19
22
  it_should_behave_like "a field that allows the required attribute" do
20
- let(:markup_with_required) { %{<input type="email" name="field" required />} }
21
- let(:markup_without_required) { %{<input type="email" name="field" />} }
23
+ let(:markup_with_required) { %{<input type="email" id="field" name="field" required />} }
24
+ let(:markup_without_required) { %{<input type="email" id="field" name="field" />} }
22
25
  let(:valid_value) { "test@example.com" }
23
26
  end
24
27
 
25
28
  it_should_behave_like "a field that allows the pattern attribute" do
26
- let(:markup) { '<input type="email" name="field" pattern="@example\.com$" />' }
29
+ let(:markup) { '<input type="email" id="field" name="field" pattern="@example\.com$" />' }
27
30
  let(:valid_value) { "test@example.com" }
28
31
  end
29
32
 
30
33
  describe "validations" do
31
- subject(:input) { Formulary::HtmlForm::Fields::EmailInput.new(element) }
32
- let(:markup) { %{<input type="email" name="name" />} }
34
+ subject(:input) { Formulary::HtmlForm::Fields::EmailInput.new(html_form, element) }
35
+ let(:markup) { %{<input type="email" id="field" name="name" />} }
33
36
 
34
37
  context "with a valid email address" do
35
38
  before { input.set_value("test@example.com") }
@@ -42,7 +45,14 @@ describe Formulary::HtmlForm::Fields::EmailInput do
42
45
  before { input.set_value("invalid!") }
43
46
 
44
47
  it { should_not be_valid }
45
- its(:error) { should include("email") }
48
+ its(:error) { should eql("'Field' is not a valid email address") }
49
+ end
50
+
51
+ context "with an email address that is somewhat invalid but passes the client-side spec" do
52
+ before { input.set_value("e@e.e") }
53
+
54
+ it { should be_valid }
55
+ its(:error) { should be_blank }
46
56
  end
47
57
 
48
58
  context "with a nil email address" do