(Translated by https://www.hiragana.jp/)
Split up tests (#214) · josefarias/hotwire_combobox@57dea45 · GitHub
Skip to content

Commit

Permalink
Split up tests (#214)
Browse files Browse the repository at this point in the history
* Clean up helper tests

* Pull out test helpers

* Split up tests
  • Loading branch information
josefarias authored Oct 13, 2024
1 parent 0fa9a7f commit 57dea45
Show file tree
Hide file tree
Showing 23 changed files with 1,466 additions and 1,434 deletions.
3 changes: 3 additions & 0 deletions test/application_system_test_case.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
include ComboboxActionsHelper
include ComboboxAssertionsHelper

driven_by :selenium, using: :headless_chrome

def states(...)
Expand Down
22 changes: 1 addition & 21 deletions test/application_view_test_case.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
class ApplicationViewTestCase < ActionView::TestCase
include ConfigurationHelper

# `#assert_attrs` expects attrs to appear in the order they are passed.
def assert_attrs(tag, tag_name: :input, **attrs)
assert_match(/<#{tag_name}.* #{escaped_attrs(attrs)}/, tag)
end

def assert_no_attrs(tag, tag_name: :input, **attrs)
assert_no_match(/<#{tag_name}.* #{escaped_attrs(attrs)}/, tag)
end

private
def escaped_attrs(attrs)
attrs.map do |k, v|
%Q(#{escape_specials(k)}="#{escape_specials(v)}".*)
end.join(" ").gsub("&", "&amp;")
end

def escape_specials(value)
special_characters = Regexp.union "[]?".chars
value.to_s.gsub(special_characters) { |char| "\\#{char}" }
end
include AttributeAssertionsHelper
end
48 changes: 13 additions & 35 deletions test/helpers/hotwire_combobox/helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,43 @@

class HotwireCombobox::HelperTest < ApplicationViewTestCase
test "passing an input type" do
tag = combobox_tag :foo, type: :search

assert_attrs tag, type: "search"
assert_attrs combobox_tag(:foo, type: :search), type: "search"
end

test "passing an id" do
tag = combobox_tag :foo, id: :foobar

assert_attrs tag, id: "foobar"
assert_attrs combobox_tag(:foo, id: :foobar), id: "foobar"
end

test "passing a name" do
tag = combobox_tag :foo

assert_attrs tag, name: "foo"
assert_attrs combobox_tag(:foo), name: "foo"
end

test "passing a value" do
tag = combobox_tag :foo, value: :bar

assert_attrs tag, value: "bar"
assert_attrs combobox_tag(:foo, value: :bar), value: "bar"
end

test "passing a form builder object" do
form = ActionView::Helpers::FormBuilder.new :foo, nil, self, {}
tag = combobox_tag :bar, form: form

assert_attrs tag, type: "hidden", name: "foo[bar]" # name is not "bar"
assert_attrs tag, id: "foo_bar", role: "combobox" # id is determined by the form builder
assert_attrs combobox_tag(:bar, form: form), type: "hidden", name: "foo[bar]" # name is not "bar"
assert_attrs combobox_tag(:bar, form: form), id: "foo_bar", role: "combobox" # id is determined by the form builder
end

test "passing a value overrides form builder object" do
form = ActionView::Helpers::FormBuilder.new :foo, OpenStruct.new(bar: "foobar"), self, {}
tag = combobox_tag :bar, value: :baz, form: form

assert_attrs tag, value: "baz" # not "foobar"
assert_attrs combobox_tag(:bar, value: :baz, form: form), value: "baz" # not "foobar"
end

test "passing an id overrides form builder" do
form = ActionView::Helpers::FormBuilder.new :foo, nil, self, {}
tag = combobox_tag :bar, form: form, id: :foobar

assert_attrs tag, id: "foobar" # not "foo_bar"
assert_attrs combobox_tag(:bar, form: form, id: :foobar), id: "foobar" # not "foo_bar"
end

test "passing open" do
tag = combobox_tag :foo, open: true

assert_attrs tag, tag_name: :fieldset, "data-hw-combobox-expanded-value": "true"
assert_attrs combobox_tag(:foo, open: true), tag_name: :fieldset, "data-hw-combobox-expanded-value": "true"
end

test "a11y attributes" do
tag = combobox_tag :foo, id: :foobar

assert_attrs tag, role: "combobox",
assert_attrs combobox_tag(:foo, id: :foobar), role: "combobox",
"aria-controls": "foobar-hw-listbox", "aria-owns": "foobar-hw-listbox",
"aria-haspopup": "listbox", "aria-autocomplete": "both"
end
Expand Down Expand Up @@ -89,22 +71,18 @@ class HotwireCombobox::HelperTest < ApplicationViewTestCase
{ alias: :async_combobox_options, method: :hw_async_combobox_options }
].each do |pair|
test "#{pair[:alias]} is an alias for #{pair[:method]}" do
assert_equal \
HotwireCombobox::Helper.instance_method(pair[:method]),
HotwireCombobox::Helper.instance_method(pair[:alias])
assert_equal HotwireCombobox::Helper.instance_method(pair[:method]), HotwireCombobox::Helper.instance_method(pair[:alias])
end

test "#{pair[:alias]} is not defined when bypassing convenience methods" do
swap_config bypass_convenience_methods: true do
assert_not HotwireCombobox::Helper.instance_methods.include?(pair[:alias]),
"#{pair[:alias]} is defined"
assert_not HotwireCombobox::Helper.instance_methods.include?(pair[:alias]), "#{pair[:alias]} is defined"
end
end
end

test "hw_combobox_style_tag" do
assert_attrs hw_combobox_style_tag, tag_name: :link,
rel: "stylesheet", href: "/stylesheets/hotwire_combobox.css"
assert_attrs hw_combobox_style_tag, tag_name: :link, rel: "stylesheet", href: "/stylesheets/hotwire_combobox.css"
end

test "hw_listbox_id returns the same as component#listbox_id" do
Expand Down
22 changes: 22 additions & 0 deletions test/lib/helpers/attribute_assertions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module AttributeAssertionsHelper
# `#assert_attrs` expects attrs to appear in the order they are passed.
def assert_attrs(tag, tag_name: :input, **attrs)
assert_match(/<#{tag_name}.* #{escaped_attrs(attrs)}/, tag)
end

def assert_no_attrs(tag, tag_name: :input, **attrs)
assert_no_match(/<#{tag_name}.* #{escaped_attrs(attrs)}/, tag)
end

private
def escaped_attrs(attrs)
attrs.map do |k, v|
%Q(#{escape_specials(k)}="#{escape_specials(v)}".*)
end.join(" ").gsub("&", "&amp;")
end

def escape_specials(value)
special_characters = Regexp.union "[]?".chars
value.to_s.gsub(special_characters) { |char| "\\#{char}" }
end
end
70 changes: 70 additions & 0 deletions test/lib/helpers/combobox_actions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module ComboboxActionsHelper
def open_combobox(selector)
find(selector).click
end

def type_in_combobox(selector, *text)
find(selector).send_keys(*text)
end

def delete_from_combobox(selector, text, original:)
find(selector).then do |input|
original.chars.each { input.send_keys(:arrow_right) }
text.chars.each { input.send_keys(:backspace) }
end
end

def click_on_option(text)
find("li[role=option]", exact_text: text).click
end

def clear_autocompleted_portion(selector)
type_in_combobox selector, :backspace
end

def remove_chip(text)
find("[aria-label='Remove #{text}']").click
end

def on_small_screen
@on_small_screen = true
original_size = page.current_window.size
page.current_window.resize_to 375, 667
yield
ensure
@on_small_screen = false
page.current_window.resize_to(*original_size)
end

def on_slow_device(delay:)
@on_slow_device = true
page.execute_script "window.HOTWIRE_COMBOBOX_STREAM_DELAY = #{delay * 1000}"
yield
ensure
@on_slow_device = false
page.execute_script "window.HOTWIRE_COMBOBOX_STREAM_DELAY = 0"
end

def tab_away
find("body").send_keys(:tab, :tab)
assert_closed_combobox
end

def click_away
if @on_small_screen
click_on_top_left_corner
else
find("#clickable").click
end

assert_closed_combobox
end

def click_on_top_left_corner
page.execute_script "document.elementFromPoint(0, 0).click()"
end

def current_selection_contents
page.evaluate_script "document.getSelection().toString()"
end
end
107 changes: 107 additions & 0 deletions test/lib/helpers/combobox_assertions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
module ComboboxAssertionsHelper
def assert_combobox
assert_selector "input[role=combobox]"
end

def assert_closed_combobox
assert_selector "input[aria-expanded=false]"
end

def assert_open_combobox
assert_selector "input[aria-expanded=true]"
end

def assert_no_listbox
assert_no_selector "ul[role=listbox]"
end

def assert_listbox_with(**kwargs)
assert_selector "ul[role=listbox]", **kwargs
end
alias_method :assert_listbox, :assert_listbox_with

def assert_no_visible_options_with(**kwargs)
assert_no_selector "li[role=option]", **kwargs
end
alias_method :assert_no_visible_options, :assert_no_visible_options_with

def assert_option_with(html_markup: "", **kwargs)
assert_selector "li[role=option]#{kwargs.delete(:class)} #{html_markup}".squish, **kwargs
end
alias_method :assert_options_with, :assert_option_with

def assert_chip_with(html_markup: "", **kwargs)
assert_selector "[data-hw-combobox-chip] #{html_markup}".squish, **kwargs
end

def assert_combobox_display(selector, text)
if text.is_a? Array
assert_selection_chips(*text)
else
assert_field locator_for(selector), with: text
end
end

def assert_combobox_value(selector, value)
value = value.join(",") if value.is_a? Array
assert_field "#{locator_for(selector)}-hw-hidden-field", type: "hidden", with: value
end

def assert_combobox_display_and_value(selector, text, value)
assert_combobox_display selector, text
assert_combobox_value selector, value
end

def assert_selected_option_with(selector: "", **kwargs)
assert_selector "li[role=option][aria-selected=true]#{selector}".squish, **kwargs
end

def assert_no_visible_selected_option
assert_no_selector "li[role=option][aria-selected=true]"
end

def assert_invalid_combobox
assert_selector "input[aria-invalid=true]"
assert_selector "dialog input[aria-invalid=true]", visible: :hidden
end

def assert_not_invalid_combobox
assert_no_selector "input[aria-invalid=true]"
assert_no_selector "dialog input[aria-invalid=true]", visible: :hidden
end

def assert_proper_combobox_name_choice(original:, new:, proper:)
if proper == :original
assert_selector "input[name='#{original}']", visible: :hidden
assert_no_selector "input[name='#{new}']", visible: :hidden
else
assert_no_selector "input[name='#{original}']", visible: :hidden
assert_selector "input[name='#{new}']", visible: :hidden
end
end

def assert_selection_chips(*texts)
texts.each do |text|
assert_selector "[data-hw-combobox-chip]", text: text
end
end

def assert_focused_combobox(selector)
page.evaluate_script("document.activeElement.id") == locator_for(selector)
end

def assert_group_with(**kwargs)
assert_selector "ul[role=group] li[role=presentation]", **kwargs
end

def assert_scrolled(selector)
sleep 0.5
assert page.evaluate_script("document.querySelector('#{selector}').scrollTop") > 0,
"Expected #{selector} to be scrolled."
end

def locator_for(selector)
# https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Matchers#has_field%3F-instance_method
selector.delete_prefix("#")
end
end
57 changes: 57 additions & 0 deletions test/system/async_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require "system_test_helper"

class AsyncTest < ApplicationSystemTestCase
[
{ path: :async_path, visible_options: 10 },
{ path: :async_html_path, visible_options: 5 }
].each do |test_case|
test "async combobox #{test_case[:path]}" do
visit send(test_case[:path])

open_combobox "#movie-field"

assert_text "12 Angry Men"
type_in_combobox "#movie-field", "wh"
assert_combobox_display_and_value "#movie-field", "Whiplash", movies(:whiplash).id
assert_options_with count: 2
clear_autocompleted_portion "#movie-field"
delete_from_combobox "#movie-field", "wh", original: "wh"
assert_combobox_display_and_value "#movie-field", "", nil
assert_text "12 Angry Men"

# pagination
assert_options_with count: test_case[:visible_options]
find("#movie-field-hw-listbox").scroll_to :bottom
assert_options_with count: test_case[:visible_options] + 5

type_in_combobox "#movie-field", "a"
assert_combobox_display_and_value "#movie-field", "A Beautiful Mind", movies(:a_beautiful_mind).id
find("#movie-field-hw-listbox").scroll_to :bottom
assert_options_with count: test_case[:visible_options] + 5
assert_scrolled "#movie-field-hw-listbox"
end
end

test "async autocomplete selections don't trample over each other" do
visit async_path

on_slow_device delay: 0.5 do
open_combobox "#movie-field"
type_in_combobox "#movie-field", "a"
sleep 0.3 # less than the delay, more than the debounce
type_in_combobox "#movie-field", "l"
sleep 0.7 # more than the delay

assert_equal "addin", current_selection_contents
end
end

test "substring matching in async free-text combobox" do
visit freetext_async_path

open_combobox "#movie-field"
type_in_combobox "#movie-field", "few"
click_on_option "A Few Good Men"
assert_combobox_display_and_value "#movie-field", "A Few Good Men", movies(:a_few_good_men).id
end
end
Loading

0 comments on commit 57dea45

Please sign in to comment.