match_table 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1647f8131554af5dbc383c249d74e02aee0a266c0c4e9fc4240b756c8be66f80
4
+ data.tar.gz: 203a90a3cad73ad4daae163e3fafcc8bf0f6026f407da4d7171cde4c819a34d5
5
+ SHA512:
6
+ metadata.gz: 161f0b34b357ab60005624b8f713959e280176da89ab9c261a41670aed9aae731657e5ca712b6ba9ad5800fce300970c2f3ac03e118e34aac361d08e6f8e85ee
7
+ data.tar.gz: cbd4fed84e7e0171bdfe7eef2b585651a9f483b61660b154496b864cf5209d992761d6e28b36e9d831b44a22e187ef09849597d15136532d779eb60c41dc86df
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [1.0.0] - 2025-04-15
4
+
5
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 deTASO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # MatchTable
2
+
3
+ Adds a `match_table` matcher for your Capybara system specs.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```bash
10
+ bundle add match_table
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ In your system specs:
16
+
17
+ ```ruby
18
+ # Matches rows but doesn't care about the order or extra rows
19
+ expect(page).to match_table(:foo_table).with_rows(
20
+ {
21
+ "Name" => "John Smith",
22
+ "Status" => "Active",
23
+ }
24
+ )
25
+
26
+ # Matches rows in the order they are defined and fails if there are extra rows
27
+ expect(page).to match_table(:foo_table).with_exact_rows(
28
+ {
29
+ "Name" => "John Smith",
30
+ "Status" => "Active",
31
+ }
32
+ )
33
+ ```
34
+
35
+ ## Development
36
+
37
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
+
39
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/detaso/match_table>.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "standard/rake"
5
+
6
+ task default: :standard
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatchTable
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "match_table/version"
4
+ require "rspec/expectations"
5
+ require "capybara"
6
+
7
+ module MatchTable
8
+ end
9
+
10
+ RSpec::Matchers.define :match_table do |table|
11
+ match do |page|
12
+ @table = table
13
+
14
+ @found_table = false
15
+ expect(page).to have_table @table
16
+ @found_table = true
17
+
18
+ expected_headers = @expected_rows.first.keys
19
+
20
+ same_headers =
21
+ @expected_rows.all? do |row|
22
+ row.keys == expected_headers
23
+ end
24
+
25
+ raise ArgumentError, "all rows must have the same headers" unless same_headers
26
+
27
+ begin
28
+ # We'll rerun this block until our expectations are met or we time out.
29
+ synchronize do
30
+ @failures = []
31
+ find_elements_on_page
32
+
33
+ header_positions =
34
+ expected_headers.each_with_object({}) do |header, hash|
35
+ position = @actual_headers.find_index { |actual_header| actual_header.start_with?(header) }
36
+ unless position.nil?
37
+ hash[header] = position
38
+ end
39
+ end
40
+
41
+ matched_rows = []
42
+ @actual_rows.each do |actual_row|
43
+ matched_rows << match_row(header_positions:, actual_row:)
44
+ end
45
+
46
+ @actual = matched_rows
47
+ @expected_as_array = Array(@expected_rows)
48
+
49
+ # Grab the failures from these expectations so we can use their failure messages
50
+ RSpec::Support.with_failure_notifier(append_to_failures_array_notifier) do
51
+ case @mode
52
+ when :exact
53
+ expect(actual).to eq(expected_as_array)
54
+ when :include
55
+ expect(actual).to include(*expected_as_array)
56
+ end
57
+ end
58
+
59
+ raise Capybara::ExpectationNotMet unless @failures.empty?
60
+ end
61
+ rescue Capybara::ExpectationNotMet
62
+ false
63
+ end
64
+
65
+ @failures.empty?
66
+ end
67
+
68
+ # Match the table exactly with the provided rows in order.
69
+ chain :with_exact_rows do |*rows|
70
+ @mode = :exact
71
+ @expected_rows = rows
72
+ end
73
+
74
+ # Ensure the table includes the provided rows, but not necessarily in order.
75
+ chain :with_rows do |*rows|
76
+ @mode = :include
77
+ @expected_rows = rows
78
+ end
79
+
80
+ failure_message do |page|
81
+ if !@found_table
82
+ "unable to find table \"#{table}\" on page"
83
+ else
84
+ <<~MESSAGE
85
+ found table "#{table}" on page, with headers:
86
+ #{@actual_headers}
87
+ but rows did not match expected values:
88
+ #{@failures.map(&:message).join("\n")}
89
+ MESSAGE
90
+ end
91
+ end
92
+
93
+ def synchronize
94
+ Capybara.current_session.document.synchronize do
95
+ yield
96
+ end
97
+ end
98
+
99
+ def find_table(identifier)
100
+ if identifier.is_a?(Symbol)
101
+ find_by_id(identifier)
102
+ else
103
+ find(:table, identifier)
104
+ end
105
+ end
106
+
107
+ def find_elements_on_page
108
+ table = find_table(@table)
109
+
110
+ @actual_headers =
111
+ table.find("thead").all("th").map(&:text)
112
+
113
+ @actual_rows = []
114
+
115
+ rows = table.find("tbody:not(.contents)").all("tr[data-table-target='row']:not([data-accordion-content] table tr)").presence ||
116
+ table.find("tbody:not(.contents)").all("tr[data-table-target='row']")
117
+
118
+ rows.each do |row|
119
+ cells = row.all("td")
120
+ @actual_rows << cells.map(&:text)
121
+ end
122
+ end
123
+
124
+ def match_row(header_positions:, actual_row:)
125
+ header_positions.each_with_object({}) do |(header, position), matched_row|
126
+ matched_row[header] = actual_row[position]
127
+ end
128
+ end
129
+
130
+ def append_to_failures_array_notifier
131
+ lambda { |failure, _opts| @failures << failure }
132
+ end
133
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: match_table
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Schlesinger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-04-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-expectations
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: capybara
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ description: Capybara's built-in table matchers leave a lot to be desired. `match_table`
42
+ is a matcher that allows you to match tables in your system specs with a lot more
43
+ flexibility and much improved failure messages.
44
+ email:
45
+ - ryan@ryanschlesinger.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".standard.yml"
51
+ - CHANGELOG.md
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - lib/match_table.rb
56
+ - lib/match_table/version.rb
57
+ homepage: https://github.com/detaso/match_table
58
+ licenses: []
59
+ metadata:
60
+ allowed_push_host: https://rubygems.org
61
+ homepage_uri: https://github.com/detaso/match_table
62
+ source_code_uri: https://github.com/detaso/match_table
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 3.1.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.5.22
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Adds a `match_table` matcher for your Capybara system specs.
82
+ test_files: []