with_form 0.1.0

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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a9e706ed3dfe301d30053e531b040f1588ae35ca430f4c1de408bd01bd777e8e
4
+ data.tar.gz: dfbc4ba02b11b52f69a852e1b66b226ce10ef86a7e6bf075264b70ed4e808cbb
5
+ SHA512:
6
+ metadata.gz: 2fc1f5efb683e554791d22d0ec68ef467fffb768a9b9c1c96100545bd882715147757f91715d534be2eb9fa9dbc18ad3c8f9fe8785cd891e722d5a80450d3a91
7
+ data.tar.gz: 21d93934220c9b60dfa9aa1814b8f72121001f2e11f43b236634fd722e8c967cc03214f7208458244897b81845362b68bf8fe02823f9c19a48e7390b65174b40
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Sean Doyle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,191 @@
1
+ # WithForm
2
+
3
+ Your System Test's counterpart to `form_with`
4
+
5
+ ## Usage
6
+
7
+ Leverage Rails-generated `<label>` values to submit `<form>` elements in System
8
+ Tests.
9
+
10
+ To add coverage to a form's fields that are generated by ActionView's
11
+ `form_with` helper, fill them using `with_form`:
12
+
13
+ ```ruby
14
+ class UserInteractsWithFormsTest < ApplicationSystemTestCase
15
+ include WithForm::TestHelpers
16
+
17
+ test "user signs in" do
18
+ visit new_session_path
19
+ with_form(scope: :session) do |form|
20
+ form.fill_in :email, with: "user@example.com"
21
+ form.fill_in :password, with: "secr3t"
22
+ form.check :remember_me
23
+ form.click_button
24
+ end
25
+
26
+ assert_text "Welcome back, user@example.com."
27
+ end
28
+
29
+ test "user makes a post" do
30
+ post = Post.new(title: "My First Post", tags: ["ruby", "testing"])
31
+
32
+ visit new_post_path
33
+ with_form(model: post) do |form|
34
+ form.fill_in :title
35
+ form.check :tags
36
+ form.click_button
37
+ end
38
+
39
+ assert_text "Created Post: My First Post."
40
+ end
41
+
42
+ test "user updates their profile" do
43
+ profile = Profile.create!
44
+
45
+ visit profile_path
46
+ with_form(model: profile) do |form|
47
+ form.fill_in :email, with: "updated.user@example.com"
48
+ form.select "NY", from: :state
49
+ form.click_button :update
50
+ end
51
+
52
+ assert_text "Your profile has been updated."
53
+ end
54
+ end
55
+ ```
56
+
57
+ ### `with_form` Options
58
+
59
+ The `with_form` helper method accepts two styles of options:
60
+
61
+ * `scope:` - the internationalization scope key to use when translating Capybara's
62
+ locator values
63
+ * `model:` - an instance of an `ActiveModel::Model` or `ActiveRecord::Base` to
64
+ be used to translate Capybara's locator values, and to populate the fields
65
+ with an attribute's value.
66
+
67
+ For example, assuming that a `Post` record has a `title` attribute:
68
+
69
+ ```ruby
70
+ post = Post.new(title: "The Title")
71
+
72
+ form_with(model: post) do |form|
73
+ form.fill_in :title
74
+ end
75
+ ```
76
+
77
+ The call to `form.fill_in` will search for an [`<input>` element][input] or a
78
+ [`<textarea>` element][textarea] that is labelled by a
79
+ [`<label>` element][label] whose value is translated from the
80
+ `helpers.label.post.title` internationalization key. If that element exists,
81
+ set its value to the value of `post.title` (in this case, `"The Title"`).
82
+
83
+ An attribute's value can be overridden by providing
84
+ a different value. For instance, assuming that a `Post` record has a `title`
85
+ attribute:
86
+
87
+ ```ruby
88
+ post = Post.create!(title: "Old Title")
89
+
90
+ form_with(model: post) do |form|
91
+ form.fill_in :title, with: "New Title"
92
+ end
93
+ ```
94
+
95
+ The call to `form.fill_in` will work much like the example above, with the
96
+ exception that the provided `with:` option's value (in this case,
97
+ `"New Title"`) will take precedence over the `post.title` attribute's value
98
+ (in this case, `"Old Title"`).
99
+
100
+ [label]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
101
+ [input]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
102
+ [textarea]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
103
+
104
+ With the exception of `#click_link` and `#click_link_or_button`, the argument
105
+ yielded to `with_form` supports [all helper methods made available by
106
+ `Capybara::Node::Actions`][actions].
107
+
108
+ Those include:
109
+
110
+ * `attach_file(locator = nil, paths, make_visible: nil, **options)`
111
+ * `check(locator, **options)`
112
+ * `choose(locator, **options)`
113
+ * `click_button(locator, nil, **options)`
114
+ * `fill_in(locator, with: nil, **options)`
115
+ * `select(value = nil, from: nil, **options)`
116
+ * `uncheck(locator, **options)`
117
+ * `unselect(value = nil, from: nil, **options)`
118
+
119
+ [actions]: https://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions
120
+
121
+ ## Installation
122
+
123
+ Add this line to your application's Gemfile:
124
+
125
+ ```ruby
126
+ gem 'with_form'
127
+ ```
128
+
129
+ And then execute:
130
+
131
+ ```bash
132
+ $ bundle
133
+ ```
134
+
135
+ Then, include the `WithForm::TestHelpers` into your project testing framework.
136
+
137
+ ### MiniTest
138
+
139
+ ```ruby
140
+ # test/application_system_test_case.rb
141
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
142
+ include WithForm::TestHelpers
143
+ end
144
+ ```
145
+
146
+ ### RSpec
147
+
148
+ ```ruby
149
+ # spec/support/with_form.rb
150
+ RSpec.configure do |config|
151
+ config.include(WithForm::TestHelpers, type: :system)
152
+ end
153
+ ```
154
+
155
+ ## FAQ
156
+
157
+ I want to call a Capybara helper with that input's [`id` attribute][mdn-id]
158
+ or [`name` attribute][mdn-name]. How can I do that?
159
+
160
+ * You can mix the object that you invoke the helper methods on within the
161
+ `with_form` block. For instance:
162
+
163
+ ```ruby
164
+ with_form(scope: :post) do |form|
165
+ form.fill_in :title, with: "The Post's Title"
166
+ fill_in "another-field-id", with: "Another Value"
167
+ fill_in "post[special-field]", with: "Special Value"
168
+ end
169
+ ```
170
+
171
+ I've used the [`formulaic` gem][formulaic] before. How is this gem different?
172
+
173
+ * Formulaic's `fill_form` and `fill_form_and_submit` are very useful
174
+ abstractions over [`simple_form`-generated `<form>` elements][simple_form].
175
+ This gem's focus is at a different layer of abstraction. Instead of translating
176
+ a Hash of attribute-value pairs into `<form>` element interactions, this gem's
177
+ interface focussed on enhancing the experience of filling in
178
+ [`form_with`-generated `<form>` elements][form_with] that are labelled by the
179
+ [`ActionView`-provided internationalization tooling][rails-i18n].
180
+
181
+
182
+ [mdn-id]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id
183
+ [mdn-name]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#attr-name
184
+ [formulaic]: https://github.com/thoughtbot/formulaic
185
+ [simple_form]: https://github.com/heartcombo/simple_form
186
+ [form_with]: https://guides.rubyonrails.org/form_helpers.html#dealing-with-basic-forms
187
+ [rails-i18n]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label
188
+
189
+ ## License
190
+
191
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'WithForm'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,7 @@
1
+ class CreateWidgetRecords < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :widget_records do |t|
4
+ t.text :text_field
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :with_form do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,6 @@
1
+ require "with_form/engine"
2
+ require "with_form/test_helpers"
3
+
4
+ module WithForm
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,4 @@
1
+ module WithForm
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,98 @@
1
+ module WithForm
2
+ class ModelForm
3
+ include ActionView::Helpers::TranslationHelper
4
+
5
+ def initialize(model:, page:)
6
+ @page = page
7
+ @model = model
8
+ end
9
+
10
+ def fill_in(attribute, with: nil, **options)
11
+ scope_form.fill_in(
12
+ attribute,
13
+ with: with || @model.public_send(attribute),
14
+ **options,
15
+ )
16
+ end
17
+
18
+ def attach_file(attribute, path = nil, **options)
19
+ scope_form.attach_file(
20
+ attribute,
21
+ path || @model.public_send(attribute),
22
+ **options,
23
+ )
24
+ end
25
+
26
+ def choose(attribute, **options)
27
+ case attribute
28
+ when Symbol
29
+ value = @model.public_send(attribute)
30
+ else
31
+ value = attribute
32
+ end
33
+
34
+ scope_form.choose value, **options
35
+ end
36
+
37
+ def check(attribute, **options)
38
+ case attribute
39
+ when Symbol
40
+ values = Array(@model.public_send(attribute))
41
+ else
42
+ values = Array(attribute)
43
+ end
44
+
45
+ values.each { |value| scope_form.check(value, **options) }
46
+ end
47
+
48
+ def uncheck(attribute, **options)
49
+ case attribute
50
+ when Symbol
51
+ values = Array(@model.public_send(attribute))
52
+ else
53
+ values = Array(attribute)
54
+ end
55
+
56
+ values.each { |value| scope_form.uncheck(value, **options) }
57
+ end
58
+
59
+ def select(attribute, from: nil, **options)
60
+ case attribute
61
+ when Symbol
62
+ value = @model.public_send(attribute)
63
+ else
64
+ value = attribute
65
+ end
66
+
67
+ scope_form.select value, from: from || attribute, **options
68
+ end
69
+
70
+ def unselect(attribute, from: nil, **options)
71
+ case attribute
72
+ when Symbol
73
+ value = @model.public_send(attribute)
74
+ else
75
+ value = attribute
76
+ end
77
+
78
+ scope_form.unselect value, from: from || attribute, **options
79
+ end
80
+
81
+ def click_button(action = nil, **options)
82
+ if action.present?
83
+ elsif @model.persisted?
84
+ action = :update
85
+ else
86
+ action = :create
87
+ end
88
+
89
+ scope_form.click_button action, **options
90
+ end
91
+
92
+ private
93
+
94
+ def scope_form
95
+ WithForm::ScopeForm.new(scope: @model.model_name.i18n_key, page: @page)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,40 @@
1
+ module WithForm
2
+ class ScopeForm
3
+ include ActionView::Helpers::TranslationHelper
4
+
5
+ delegate :check, :uncheck, :choose, to: :@page
6
+
7
+ def initialize(scope:, page:)
8
+ @page = page
9
+ @scope = scope
10
+ end
11
+
12
+ def fill_in(attribute, with:, **options)
13
+ @page.fill_in label(attribute), with: with, **options
14
+ end
15
+
16
+ def select(value, from:, **options)
17
+ @page.select value, from: label(from), **options
18
+ end
19
+
20
+ def unselect(value, from:, **options)
21
+ @page.unselect value, from: label(from), **options
22
+ end
23
+
24
+ def attach_file(attribute, path, **options)
25
+ @page.attach_file label(attribute), path, **options
26
+ end
27
+
28
+ def click_button(action = nil, **options)
29
+ @page.click_button submit(action), **options
30
+ end
31
+
32
+ def submit(action = nil)
33
+ translate(action || :submit, scope: [:helpers, :submit, @scope])
34
+ end
35
+
36
+ def label(attribute)
37
+ translate(attribute, scope: [:helpers, :label, @scope])
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,16 @@
1
+ require "with_form/model_form"
2
+ require "with_form/scope_form"
3
+
4
+ module WithForm
5
+ module TestHelpers
6
+ def with_form(scope: nil, model: nil, &block)
7
+ if model.present?
8
+ WithForm::ModelForm.new(model: model, page: page).yield_self(&block)
9
+ elsif scope.present?
10
+ WithForm::ScopeForm.new(scope: scope, page: page).yield_self(&block)
11
+ else
12
+ raise "with_form must be invoked with either a scope: or model: option"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module WithForm
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: with_form
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Doyle
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.2.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 6.0.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.2.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: capybara
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.15'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '2.15'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: selenium-webdriver
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: webdrivers
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: Leverage Rails-generated <label> values to submit <form> elements in
90
+ System Tests
91
+ email:
92
+ - sean.p.doyle24@gmail.com
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - MIT-LICENSE
98
+ - README.md
99
+ - Rakefile
100
+ - db/migrate/20200310042146_create_widget_records.rb
101
+ - lib/tasks/with_form_tasks.rake
102
+ - lib/with_form.rb
103
+ - lib/with_form/engine.rb
104
+ - lib/with_form/model_form.rb
105
+ - lib/with_form/scope_form.rb
106
+ - lib/with_form/test_helpers.rb
107
+ - lib/with_form/version.rb
108
+ homepage: https://github.com/seanpdoyle/with_form
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.0.3
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Your System Test's counterpart to `form_with`
131
+ test_files: []