with_form 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9e706ed3dfe301d30053e531b040f1588ae35ca430f4c1de408bd01bd777e8e
4
- data.tar.gz: dfbc4ba02b11b52f69a852e1b66b226ce10ef86a7e6bf075264b70ed4e808cbb
3
+ metadata.gz: 7a78f39980ba03659bd61b33c94e65e8987dc590647ba8870271141a879eafca
4
+ data.tar.gz: ad43310ff4fd4a42ddb3f154af5c9e799f1a25c82dafdd46c9d18a6f41f48304
5
5
  SHA512:
6
- metadata.gz: 2fc1f5efb683e554791d22d0ec68ef467fffb768a9b9c1c96100545bd882715147757f91715d534be2eb9fa9dbc18ad3c8f9fe8785cd891e722d5a80450d3a91
7
- data.tar.gz: 21d93934220c9b60dfa9aa1814b8f72121001f2e11f43b236634fd722e8c967cc03214f7208458244897b81845362b68bf8fe02823f9c19a48e7390b65174b40
6
+ metadata.gz: 2a300ce3b20953400496bc7b7cf09e0bb9206870526c8d63cfb6987db7537ffe702788cd1543ad9eb28f83b99ac44bb39063b628be3677eac73069874a0bdbd6
7
+ data.tar.gz: b596e4300be6ac53de36e9e81e85514722d1763f3c877ed82aa1b5b38a04adb8d02995d625d89eb800a07938f42343a76d565aa62c0523e338e16845aa675e95
data/README.md CHANGED
@@ -7,6 +7,8 @@ Your System Test's counterpart to `form_with`
7
7
  Leverage Rails-generated `<label>` values to submit `<form>` elements in System
8
8
  Tests.
9
9
 
10
+ ### The `with_form` test helper
11
+
10
12
  To add coverage to a form's fields that are generated by ActionView's
11
13
  `form_with` helper, fill them using `with_form`:
12
14
 
@@ -60,6 +62,29 @@ The `with_form` helper method accepts two styles of options:
60
62
 
61
63
  * `scope:` - the internationalization scope key to use when translating Capybara's
62
64
  locator values
65
+
66
+ When submitting a `<form>` through a call to `form.click_button`, you can pass
67
+ an `action` as the translation scope. A `with_form(scope:)` call will default
68
+ to the `submit` key when one is not specified. For instance:
69
+
70
+ ```ruby
71
+ form_with(scope: :post) do |form|
72
+ form.click_button
73
+ end
74
+ ```
75
+
76
+ This call will search for an `<input type="text">` or `<button>` whose `value`
77
+ or text content is the String translated by the `helpers.submit.post.submit`
78
+ key.
79
+
80
+ That action can be overridden:
81
+
82
+ ```ruby
83
+ form_with(scope: :post) do |form|
84
+ form.click_button :create
85
+ end
86
+ ```
87
+
63
88
  * `model:` - an instance of an `ActiveModel::Model` or `ActiveRecord::Base` to
64
89
  be used to translate Capybara's locator values, and to populate the fields
65
90
  with an attribute's value.
@@ -97,9 +122,65 @@ The `with_form` helper method accepts two styles of options:
97
122
  `"New Title"`) will take precedence over the `post.title` attribute's value
98
123
  (in this case, `"Old Title"`).
99
124
 
125
+ When submitting a `<form>` through a call to `form.click_button`, you can pass
126
+ an `action` as the translation scope. A `with_form(model:)` call will
127
+ determine the translation key based on [the `model`
128
+ argument's persistence state][persisted].
129
+
130
+ When a `model` instance is a new record, the key will use `create`. For
131
+ instance:
132
+
133
+ ```ruby
134
+ post = Post.new
135
+
136
+ form_with(model: post) do |form|
137
+ form.click_button
138
+ end
139
+ ```
140
+
141
+ This call will search for an `<input type="text">` or `<button>` whose `value`
142
+ or text content is the String translated by the `helpers.submit.post.create`
143
+ key.
144
+
145
+ That action can be overridden:
146
+
147
+ ```ruby
148
+ post = Post.new
149
+
150
+ form_with(model: post) do |form|
151
+ form.click_button :submit
152
+ end
153
+ ```
154
+
155
+ When a `model` instance is an existing persisted record, the key will use
156
+ `update`. For instance:
157
+
158
+ ```ruby
159
+ post = Post.last
160
+
161
+ form_with(model: post) do |form|
162
+ form.click_button
163
+ end
164
+ ```
165
+
166
+ This call will search for an `<input type="text">` or `<button>` whose `value`
167
+ or text content is the String translated by the `helpers.submit.post.update`
168
+ key.
169
+
170
+ That action can be overridden:
171
+
172
+ ```ruby
173
+ post = Post.last
174
+
175
+ form_with(model: post) do |form|
176
+ form.click_button :submit
177
+ end
178
+ ```
179
+
100
180
  [label]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
101
181
  [input]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
102
182
  [textarea]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
183
+ [persisted]: https://api.rubyonrails.org/classes/ActiveModel/Model.html#method-i-persisted-3F
103
184
 
104
185
  With the exception of `#click_link` and `#click_link_or_button`, the argument
105
186
  yielded to `with_form` supports [all helper methods made available by
@@ -118,6 +199,117 @@ Those include:
118
199
 
119
200
  [actions]: https://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions
120
201
 
202
+ #### ActionText `rich_text_area` support
203
+
204
+ When [`ActionText`][actiontext] is available, `with_form` provides a
205
+ `#fill_in_rich_text_area` helper method.
206
+
207
+ The current implementation is inspired by
208
+ `ActionText::SystemTestHelper#fill_in_rich_text_area` that is currently declared
209
+ on the current [`rails@master` branch][fill_in_rich_text_area].
210
+
211
+ ```ruby
212
+ class UserInteractsWithRichTextAreasTest < ApplicationSystemTestCase
213
+ include WithForm::TestHelpers
214
+
215
+ test "makes a post with a scope: argument" do
216
+ visit new_post_path
217
+ with_form(scope: :post) do |form|
218
+ form.fill_in_rich_text_area :body, with: "My First Post"
219
+ form.click_button
220
+ end
221
+
222
+ assert_text "My First Post"
223
+ end
224
+
225
+ test "user makes a post with a model: argument" do
226
+ post = Post.new(body: "My First Post")
227
+
228
+ visit new_post_path
229
+ with_form(model: post) do |form|
230
+ form.fill_in_rich_text_area :body
231
+ form.click_button
232
+ end
233
+
234
+ assert_text "My First Post"
235
+ end
236
+ end
237
+ ```
238
+
239
+ There is a current limitation in how the [`rails@master`-inspired
240
+ `#fill_in_rich_text_area` implementation][fill_in_rich_text_area] resolves the
241
+ `locator` argument. Since the underlying `<trix-editor>` element is not a
242
+ default field provided by the browser, focussing on its corresponding `<label>`
243
+ element won't focus the `<trix-editor>`. To resolve that shortcoming, the
244
+ `#fill_in_rich_text_area` uses the `<trix-editor aria-label="...">` attribute as
245
+ the label text.
246
+
247
+ This is a helpful, but incomplete solution to the problem. This requires that
248
+ instead of declaring a `<label for="my_rich_text_field">` element
249
+ referencing the `<trix-editor id="my_rich_text_field">` element, the `<label>`
250
+ element's text (or rather, the text that _would be_ in the `<label>` element)
251
+ must be passed to the `<trix-editor aria-label="...">` attribute.
252
+
253
+ For example:
254
+
255
+ ```html+erb
256
+ <%= form_with(model: Post.new) do |form %>
257
+ <%= form.label :my_rich_text_field %>
258
+ <%= form.rich_text_area :my_rich_text_field, "aria-label": translate(:my_rich_text_field, scope: "helpers.label.post") %>
259
+ <% end %>
260
+ ```
261
+
262
+ [actiontext]: https://edgeguides.rubyonrails.org/action_text_overview.html#examples
263
+ [fill_in_rich_text_area]: https://github.com/rails/rails/blob/339be65d669c83fd4c64541a9e82086dc5e64682/actiontext/lib/action_text/system_test_helper.rb
264
+
265
+ ### The `label` and `submit` test helpers
266
+
267
+ While `with_form` can simplify `<form>` element interactions with multiple
268
+ steps, there are times when a single line of instructions is more convenient.
269
+
270
+ Behind the scenes, `with_form` utilize the `#label` and `#submit` helper
271
+ methods to translate `<label>` and `<button>` text, along with `<input
272
+ type="submit">` values.
273
+
274
+ To put the same helpers to use within your test, include the
275
+ `WithForm::TranslationHelpers` module and invoke either:
276
+
277
+ * `label(model_name, attribute)`
278
+
279
+ * `submit(model_name, action = :submit)`
280
+
281
+ For example when clicking an `<input type="checkbox">` labelled by a translation
282
+ declared at `helpers.label.session.ready`:
283
+
284
+ ```ruby
285
+ class UserInteractsWithFormsTest < ApplicationSystemTestCase
286
+ include WithForm::TranslationHelpers
287
+
288
+ test "user ticks a box" do
289
+ visit new_session_path
290
+ check label(:session, :ready)
291
+
292
+ assert_text "We're glad you're ready, user@example.com."
293
+ end
294
+ end
295
+ ```
296
+
297
+ Or, to destroy a Post by clicking a button labelled by a translation declared at
298
+ `helpers.submit.post.destroy`:
299
+
300
+ ```ruby
301
+ class UserInteractsWithFormsTest < ApplicationSystemTestCase
302
+ include WithForm::TranslationHelpers
303
+
304
+ test "user deletes a post" do
305
+ visit new_post_path
306
+ click_on submit(:post, :destroy)
307
+
308
+ assert_text "Deleted Post."
309
+ end
310
+ end
311
+ ```
312
+
121
313
  ## Installation
122
314
 
123
315
  Add this line to your application's Gemfile:
@@ -186,6 +378,10 @@ I've used the [`formulaic` gem][formulaic] before. How is this gem different?
186
378
  [form_with]: https://guides.rubyonrails.org/form_helpers.html#dealing-with-basic-forms
187
379
  [rails-i18n]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label
188
380
 
381
+ ## Contributing
382
+
383
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
384
+
189
385
  ## License
190
386
 
191
387
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,4 +1,4 @@
1
- class CreateWidgetRecords < ActiveRecord::Migration[6.0]
1
+ class CreateWidgetRecords < ActiveRecord::Migration[Rails.version.to_f]
2
2
  def change
3
3
  create_table :widget_records do |t|
4
4
  t.text :text_field
@@ -1,4 +1,22 @@
1
1
  module WithForm
2
2
  class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load :action_dispatch_system_test_case do
4
+ Capybara.add_selector :rich_text_area do
5
+ label "rich-text area"
6
+ xpath do |locator|
7
+ if locator.nil?
8
+ XPath.descendant(:"trix-editor")
9
+ else
10
+ input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
11
+
12
+ XPath.descendant(:"trix-editor").where \
13
+ XPath.attr(:id).equals(locator) |
14
+ XPath.attr(:placeholder).equals(locator) |
15
+ XPath.attr(:"aria-label").equals(locator) |
16
+ XPath.attr(:input).equals(input_located_by_name)
17
+ end
18
+ end
19
+ end
20
+ end
3
21
  end
4
22
  end
@@ -15,6 +15,14 @@ module WithForm
15
15
  )
16
16
  end
17
17
 
18
+ def fill_in_rich_text_area(attribute, with: nil, **options)
19
+ scope_form.fill_in_rich_text_area(
20
+ attribute,
21
+ with: with || @model.public_send(attribute),
22
+ **options,
23
+ )
24
+ end
25
+
18
26
  def attach_file(attribute, path = nil, **options)
19
27
  scope_form.attach_file(
20
28
  attribute,
@@ -1,18 +1,54 @@
1
+ require "with_form/translation_helpers"
2
+
1
3
  module WithForm
2
4
  class ScopeForm
3
5
  include ActionView::Helpers::TranslationHelper
6
+ include WithForm::TranslationHelpers
4
7
 
5
- delegate :check, :uncheck, :choose, to: :@page
8
+ delegate :choose, to: :@page
6
9
 
7
10
  def initialize(scope:, page:)
8
11
  @page = page
9
12
  @scope = scope
10
13
  end
11
14
 
15
+ def check(attribute, **options)
16
+ case attribute
17
+ when Symbol
18
+ value = label(attribute)
19
+ else
20
+ value = attribute
21
+ end
22
+
23
+ @page.check value, **options
24
+ end
25
+
26
+ def uncheck(attribute, **options)
27
+ case attribute
28
+ when Symbol
29
+ value = label(attribute)
30
+ else
31
+ value = attribute
32
+ end
33
+
34
+ @page.uncheck value, **options
35
+ end
36
+
12
37
  def fill_in(attribute, with:, **options)
13
38
  @page.fill_in label(attribute), with: with, **options
14
39
  end
15
40
 
41
+ def fill_in_rich_text_area(locator = nil, with:)
42
+ fill_in_script = <<~JS
43
+ this.editor.loadHTML(arguments[0])
44
+ JS
45
+
46
+ @page.find(:rich_text_area, label(locator)).execute_script(
47
+ fill_in_script,
48
+ with.to_s,
49
+ )
50
+ end
51
+
16
52
  def select(value, from:, **options)
17
53
  @page.select value, from: label(from), **options
18
54
  end
@@ -30,11 +66,11 @@ module WithForm
30
66
  end
31
67
 
32
68
  def submit(action = nil)
33
- translate(action || :submit, scope: [:helpers, :submit, @scope])
69
+ super(@scope, action || :submit)
34
70
  end
35
71
 
36
72
  def label(attribute)
37
- translate(attribute, scope: [:helpers, :label, @scope])
73
+ super(@scope, attribute)
38
74
  end
39
75
  end
40
76
  end
@@ -0,0 +1,11 @@
1
+ module WithForm
2
+ module TranslationHelpers
3
+ def submit(model_name, action = :submit)
4
+ translate(action, scope: [:helpers, :submit, model_name])
5
+ end
6
+
7
+ def label(model_name, attribute)
8
+ translate(attribute, scope: [:helpers, :label, model_name])
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module WithForm
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Doyle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-10 00:00:00.000000000 Z
11
+ date: 2020-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: railties
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 6.0.2
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 6.0.2.1
19
+ version: 5.2.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 6.0.2
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 6.0.2.1
26
+ version: 5.2.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: capybara
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +52,20 @@ dependencies:
58
52
  - - ">="
59
53
  - !ruby/object:Gem::Version
60
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 5.2.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 5.2.0
61
69
  - !ruby/object:Gem::Dependency
62
70
  name: selenium-webdriver
63
71
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +112,7 @@ files:
104
112
  - lib/with_form/model_form.rb
105
113
  - lib/with_form/scope_form.rb
106
114
  - lib/with_form/test_helpers.rb
115
+ - lib/with_form/translation_helpers.rb
107
116
  - lib/with_form/version.rb
108
117
  homepage: https://github.com/seanpdoyle/with_form
109
118
  licenses: