toothbrush 0.1.3 → 0.1.4

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
+ SHA1:
3
+ metadata.gz: c0236cdd0e3cd84517f2172af73e1eb430373dd8
4
+ data.tar.gz: 16bbf613fe9f1b22077041dbba1fc482043d2454
5
+ SHA512:
6
+ metadata.gz: 3e27a550cef69208428161cccb36ef51d6bac05e8fc7631c2efabdbe70cad113aadd9f0f0eaa69db69026552aa629ee737d291c2609e4b0d6601c53724fae6e5
7
+ data.tar.gz: b2ea8d28c73be7673841d675cc6e81de457178b85e8ebbe7ae8548d1f0b54a728afb0f19771e06e4256c8ff83fc7e3dd4be8ca8e9a65b794a7f9b2081597041a
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -1,37 +1,133 @@
1
- module Toothbrush
2
- module Helpers
3
- def ensure_table(table_selector, header_or_content, content = nil)
4
- if content.nil?
5
- return _ensure_table(table_selector, header_or_content)
1
+ require 'rspec/expectations'
2
+ require 'text-table'
3
+
4
+ RSpec::Matchers.define :include_table do |selector, *header_content|
5
+ match do |actual|
6
+ if header_content.count == 2
7
+ expected_header, expected_content = header_content.map(&:clone)
8
+ else
9
+ expected_header, expected_content = nil, header_content[0].clone
10
+ end
11
+
12
+ check_selector = lambda do
13
+ actual.has_selector?(selector)
14
+ end
15
+
16
+ has_thead = lambda do
17
+ actual.has_selector?("#{selector} thead")
18
+ end
19
+
20
+ has_tbody = lambda do
21
+ actual.has_selector?("#{selector} tbody")
22
+ end
23
+
24
+ has_header = lambda do
25
+ has_thead[] || actual.has_selector?("#{selector} tr th")
26
+ end
27
+
28
+ expected_has_header = lambda do
29
+ expected_header && !expected_header.empty?
30
+ end
31
+
32
+ html_table_to_array = lambda do
33
+ if has_thead[]
34
+ header = actual.all("#{selector} thead tr th").map(&:text)
35
+ header = actual.all("#{selector} thead tr td").map(&:text) if header.empty?
36
+ elsif has_header[]
37
+ header = actual.first("#{selector} tr").all('th').map(&:text)
38
+ header = actual.first("#{selector} tr").all('td').map(&:text) if header.empty?
39
+ end
40
+ body_rows = if has_tbody[]
41
+ actual.all("#{selector} tbody tr")
42
+ elsif has_header[]
43
+ actual.all("#{selector} tr")[1..-1]
6
44
  else
7
- header = header_or_content
45
+ actual.all("#{selector} tr")
46
+ end
47
+ body = body_rows.map {|tr| tr.all('td').map(&:text) }
48
+ if header && !header.empty?
49
+ body_size = body.first ? body.first.size : 0
50
+ header << '' while header.size < body_size
51
+ body.unshift(header)
8
52
  end
53
+ body.to_table(first_row_is_head: has_header[]).to_s
54
+ end
55
+
56
+ column_equivalence = {}
9
57
 
10
- content.each_with_index do |row, row_index|
11
- row.each_with_index do |cell, cell_index|
12
- begin
13
- within("#{table_selector} tbody tr:nth-child(#{row_index+1}) td:nth-child(#{cell_index+1})") do
14
- page.should have_content(cell)
15
- end
16
- rescue Capybara::ElementNotFound
17
- within("#{table_selector} tr:nth-child(#{row_index+2}) td:nth-child(#{cell_index+1})") do
18
- page.should have_content(cell)
19
- end
58
+ check_header = lambda do
59
+ return true unless expected_header
60
+
61
+ check_header_content = lambda do |*header_selector|
62
+ columns = {}
63
+ sub_selector = header_selector.empty? ? '' : header_selector[0]
64
+ first_row = actual.all("#{selector} #{sub_selector} tr")[0]
65
+ if first_row
66
+ first_row.
67
+ all("th, td").
68
+ each_with_index {|td, index| columns[td.text] = index }
69
+ expected_header.each_with_index do |title, index|
70
+ column_equivalence[index] = columns[title] if columns.has_key? title
20
71
  end
72
+ expected_header.all? {|title| columns.has_key? title }
73
+ else
74
+ expected_header.empty?
21
75
  end
22
76
  end
77
+
78
+ (has_thead[] && check_header_content.('thead')) ||
79
+ (!has_thead[] && check_header_content[])
23
80
  end
24
81
 
25
- private
82
+ check_content = lambda do
83
+ check_body_content = lambda do |sub_selector_or_flag|
84
+ if sub_selector_or_flag.is_a? Symbol
85
+ sub_selector, exclude_first_line = '', sub_selector_or_flag == :exclude_first_line
86
+ else
87
+ sub_selector, exclude_first_line = sub_selector_or_flag, false
88
+ end
89
+ trs = actual.all("#{selector} #{sub_selector} tr").to_a
90
+ trs.shift if exclude_first_line
91
+ expected_content.map.with_index {|row, row_index|
92
+ tds = trs[row_index].all('td')
93
+ row.map.with_index {|data, col_index|
94
+ tds[column_equivalence[col_index] || col_index].has_content? data
95
+ }.all?
96
+ }.compact.all?
97
+ end
26
98
 
27
- def _ensure_table(selector, content)
28
- content.each_with_index do |row, row_index|
29
- row.each_with_index do |cell_data, cell_index|
30
- within("#{selector} tr:nth-child(#{row_index + 1}) td:nth-child(#{cell_index + 1})") do
31
- page.should have_content cell_data
32
- end
99
+ if has_tbody[]
100
+ check_body_content.('tbody')
101
+ elsif has_header[]
102
+ check_body_content.(:exclude_first_line)
103
+ else
104
+ check_body_content.('')
105
+ end
106
+ end
107
+
108
+ result = check_selector[] && check_header[] && check_content[]
109
+ if !result
110
+ if !check_selector[]
111
+ @message = "expected to have selector '#{selector}'"
112
+ else
113
+ expected_table = expected_content.clone
114
+ if expected_header
115
+ expected_header << '' while expected_header.size < expected_table.first.size
116
+ expected_table.unshift(expected_header)
33
117
  end
118
+ @message = "expected to include table\n%sbut found\n%s" %
119
+ [expected_table.to_table(first_row_is_head: expected_has_header[]),
120
+ html_table_to_array[]]
34
121
  end
35
122
  end
123
+ result
124
+ end
125
+
126
+ class << self
127
+ alias :failure_message :failure_message_for_should
128
+ end unless respond_to? :failure_message
129
+
130
+ failure_message do |actual|
131
+ @message
36
132
  end
37
133
  end
@@ -96,4 +96,7 @@
96
96
  <td>You can't destroy this</td>
97
97
  </tr>
98
98
  </tbody>
99
- </table>
99
+ </table>
100
+
101
+ <table id='nothing-inside'>
102
+ </table>
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,8 @@ require 'rspec'
4
4
  require 'toothbrush'
5
5
  require 'capybara'
6
6
  require 'capybara/dsl'
7
+ require 'pry-rails'
8
+ require 'text-table'
7
9
 
8
10
  require File.join(File.dirname(__FILE__), 'dummy_app', 'app')
9
11
  Capybara.app = Sinatra::Application
@@ -14,5 +16,4 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
16
 
15
17
  RSpec.configure do |config|
16
18
  config.include Capybara::DSL
17
- config.include Toothbrush::Helpers
18
19
  end
@@ -4,12 +4,12 @@ describe "Toothbrush" do
4
4
  before(:each) { visit '/dummy' }
5
5
 
6
6
  it 'talks to the dummy app' do
7
- page.should have_content "Hey, ho, let's go!"
7
+ expect(page).to have_content "Hey, ho, let's go!"
8
8
  end
9
9
 
10
10
  describe 'ensures table content' do
11
11
  it 'correct table' do
12
- ensure_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
12
+ expect(page).to include_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
13
13
  [ 'Club', 'World', 'Libertadores', 'Brasileiro', 'Copa do Brasil', 'Carioca'],
14
14
  [%w( Flamengo 1 1 6 2 32 ),
15
15
  %w( Vasco 0 1 4 1 22 ),
@@ -19,37 +19,171 @@ describe "Toothbrush" do
19
19
 
20
20
  it 'vasco is not a world club champion' do
21
21
  expect {
22
- ensure_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
22
+ expect(page).to include_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
23
23
  [ 'Club', 'World', 'Libertadores', 'Brasileiro', 'Copa do Brasil', 'Carioca'],
24
24
  [%w( Flamengo 1 1 6 2 32 ),
25
25
  %w( Vasco 1 1 4 1 22 ),
26
26
  %w( Fluminense 0 0 3 1 30 ),
27
27
  %w( Botafogo 0 0 1 0 19 )]
28
28
  }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
29
- 'expected #has_content?("1") to return true, got false')
29
+ """expected to include table
30
+ +------------+-------+--------------+------------+----------------+---------+
31
+ | Club | World | Libertadores | Brasileiro | Copa do Brasil | Carioca |
32
+ +------------+-------+--------------+------------+----------------+---------+
33
+ | Flamengo | 1 | 1 | 6 | 2 | 32 |
34
+ | Vasco | 1 | 1 | 4 | 1 | 22 |
35
+ | Fluminense | 0 | 0 | 3 | 1 | 30 |
36
+ | Botafogo | 0 | 0 | 1 | 0 | 19 |
37
+ +------------+-------+--------------+------------+----------------+---------+
38
+ but found
39
+ +------------+-------+--------------+------------+----------------+---------+
40
+ | Club | World | Libertadores | Brasileiro | Copa do Brasil | Carioca |
41
+ +------------+-------+--------------+------------+----------------+---------+
42
+ | Flamengo | 1 | 1 | 6 | 2 | 32 |
43
+ | Vasco | 0 | 1 | 4 | 1 | 22 |
44
+ | Fluminense | 0 | 0 | 3 | 1 | 30 |
45
+ | Botafogo | 0 | 0 | 1 | 0 | 19 |
46
+ +------------+-------+--------------+------------+----------------+---------+
47
+ """)
30
48
  end
31
49
 
32
- it 'supports tables without <thead> and <tbody>' do
33
- ensure_table '#without-thead-tbody',
34
- %w( 1 2),
35
- [%w(3 4),
36
- %w(5 6)]
50
+ describe 'supports tables without <thead> and <tbody>' do
51
+ it 'passing' do
52
+ expect(page).to include_table '#without-thead-tbody',
53
+ %w( 1 2),
54
+ [%w(3 4),
55
+ %w(5 6)]
56
+ end
57
+
58
+ it 'failing' do
59
+ expect {
60
+ expect(page).to include_table '#without-thead-tbody',
61
+ %w( 1 2),
62
+ [%w(3 4),
63
+ %w(5 7)]
64
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
65
+ """expected to include table
66
+ +---+---+
67
+ | 1 | 2 |
68
+ +---+---+
69
+ | 3 | 4 |
70
+ | 5 | 7 |
71
+ +---+---+
72
+ but found
73
+ +---+---+
74
+ | 1 | 2 |
75
+ +---+---+
76
+ | 3 | 4 |
77
+ | 5 | 6 |
78
+ +---+---+
79
+ """)
80
+ end
37
81
  end
38
82
 
39
- it 'supports tables without <th>' do
40
- ensure_table '#without-th',
41
- [%w(1 2),
42
- %w(3 4),
43
- %w(5 6)]
83
+ describe 'supports tables without header' do
84
+ it 'passing' do
85
+ expect(page).to include_table '#without-th',
86
+ [%w(1 2),
87
+ %w(3 4),
88
+ %w(5 6)]
89
+ end
90
+
91
+ it 'failing' do
92
+ expect {
93
+ expect(page).to include_table '#without-th',
94
+ [%w(1 2),
95
+ %w(3 4),
96
+ %w(6 6)]
97
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
98
+ """expected to include table
99
+ +---+---+
100
+ | 1 | 2 |
101
+ | 3 | 4 |
102
+ | 6 | 6 |
103
+ +---+---+
104
+ but found
105
+ +---+---+
106
+ | 1 | 2 |
107
+ | 3 | 4 |
108
+ | 5 | 6 |
109
+ +---+---+
110
+ """)
111
+ end
112
+ end
113
+
114
+ describe 'supports tables with different <th> and <td> number' do
115
+ it 'passing' do
116
+ expect(page).to include_table '#different-th-td-number',
117
+ ['Name', 'City'],
118
+ [
119
+ ['Americano', 'Campos', 'Destroy'],
120
+ ['Goytacaz', 'Campos', "You can't destroy this"]
121
+ ]
122
+ end
123
+
124
+ it 'failing' do
125
+ expect {
126
+ expect(page).to include_table '#different-th-td-number',
127
+ ['Name', 'Cidade'],
128
+ [
129
+ ['Americano', 'Campos', 'Destroy'],
130
+ ['Goytacaz', 'Campos', "You can't destroy this"]
131
+ ]
132
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
133
+ """expected to include table
134
+ +-----------+--------+------------------------+
135
+ | Name | Cidade | |
136
+ +-----------+--------+------------------------+
137
+ | Americano | Campos | Destroy |
138
+ | Goytacaz | Campos | You can't destroy this |
139
+ +-----------+--------+------------------------+
140
+ but found
141
+ +-----------+--------+------------------------+
142
+ | Name | City | |
143
+ +-----------+--------+------------------------+
144
+ | Americano | Campos | Destroy |
145
+ | Goytacaz | Campos | You can't destroy this |
146
+ +-----------+--------+------------------------+
147
+ """)
148
+ end
44
149
  end
45
150
 
46
- it 'supports tables with different <th> and <td> number' do
47
- ensure_table '#different-th-td-number',
48
- ['Name', 'City'],
49
- [
50
- ['Americano', 'Campos', 'Destroy'],
51
- ['Goytacaz', 'Campos', "You can't destroy this"]
52
- ]
151
+ it 'supports partial table' do
152
+ expect(page).to include_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
153
+ %w( Club World ),
154
+ [%w( Flamengo 1 ),
155
+ %w( Vasco 0 ),
156
+ %w( Fluminense 0 ),
157
+ %w( Botafogo 0 )]
53
158
  end
159
+
160
+ it 'does not care about column ordering' do
161
+ expect(page).to include_table '#football-clubs-from-rio-de-janeiro-and-their-honors',
162
+ %w( World Club ),
163
+ [%w( 1 Flamengo ),
164
+ %w( 0 Vasco ),
165
+ %w( 0 Fluminense ),
166
+ %w( 0 Botafogo )]
167
+ end
168
+ end
169
+
170
+ it 'supports table having nothing inside' do
171
+ expect {
172
+ expect(page).to include_table '#nothing-inside',
173
+ ['a'],
174
+ [['b'],
175
+ ['c']]
176
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
177
+ """expected to include table
178
+ +---+
179
+ | a |
180
+ +---+
181
+ | b |
182
+ | c |
183
+ +---+
184
+ but found
185
+ ++
186
+ ++
187
+ """)
54
188
  end
55
189
  end
data/toothbrush.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "toothbrush"
8
- s.version = "0.1.3"
8
+ s.version = "0.1.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Rodrigo Manh\u{e3}es"]
@@ -37,7 +37,9 @@ Gem::Specification.new do |s|
37
37
  s.require_paths = ["lib"]
38
38
  s.summary = "Useful stuff for testing with Capybara"
39
39
 
40
- s.add_development_dependency('rspec', '~> 2.0')
40
+ s.add_dependency('text-table')
41
+ s.add_development_dependency('rspec', '>= 2.11.0')
41
42
  s.add_development_dependency('sinatra', '>= 0')
42
43
  s.add_development_dependency('capybara', '>= 1.0')
44
+ s.add_development_dependency('pry-rails')
43
45
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toothbrush
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
5
- prerelease:
4
+ version: 0.1.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Rodrigo Manhães
@@ -11,54 +10,76 @@ bindir: bin
11
10
  cert_chain: []
12
11
  date: 2013-02-23 00:00:00.000000000 Z
13
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: text-table
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
14
27
  - !ruby/object:Gem::Dependency
15
28
  name: rspec
16
29
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
30
  requirements:
19
- - - ~>
31
+ - - ">="
20
32
  - !ruby/object:Gem::Version
21
- version: '2.0'
33
+ version: 2.11.0
22
34
  type: :development
23
35
  prerelease: false
24
36
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
37
  requirements:
27
- - - ~>
38
+ - - ">="
28
39
  - !ruby/object:Gem::Version
29
- version: '2.0'
40
+ version: 2.11.0
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: sinatra
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - ">="
36
46
  - !ruby/object:Gem::Version
37
47
  version: '0'
38
48
  type: :development
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ! '>='
52
+ - - ">="
44
53
  - !ruby/object:Gem::Version
45
54
  version: '0'
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: capybara
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ! '>='
59
+ - - ">="
52
60
  - !ruby/object:Gem::Version
53
61
  version: '1.0'
54
62
  type: :development
55
63
  prerelease: false
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
65
  requirements:
59
- - - ! '>='
66
+ - - ">="
60
67
  - !ruby/object:Gem::Version
61
68
  version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
62
83
  description: Useful stuff for testing with Capybara
63
84
  email: rmanhaes@gmail.com
64
85
  executables: []
@@ -67,8 +88,8 @@ extra_rdoc_files:
67
88
  - LICENSE.txt
68
89
  - README.rdoc
69
90
  files:
70
- - .document
71
- - .rspec
91
+ - ".document"
92
+ - ".rspec"
72
93
  - Gemfile
73
94
  - LICENSE.txt
74
95
  - README.rdoc
@@ -84,26 +105,25 @@ files:
84
105
  homepage: http://github.com/rodrigomanhaes/toothbrush
85
106
  licenses:
86
107
  - MIT
108
+ metadata: {}
87
109
  post_install_message:
88
110
  rdoc_options: []
89
111
  require_paths:
90
112
  - lib
91
113
  required_ruby_version: !ruby/object:Gem::Requirement
92
- none: false
93
114
  requirements:
94
- - - ! '>='
115
+ - - ">="
95
116
  - !ruby/object:Gem::Version
96
117
  version: '0'
97
118
  required_rubygems_version: !ruby/object:Gem::Requirement
98
- none: false
99
119
  requirements:
100
- - - ! '>='
120
+ - - ">="
101
121
  - !ruby/object:Gem::Version
102
122
  version: '0'
103
123
  requirements: []
104
124
  rubyforge_project:
105
- rubygems_version: 1.8.23
125
+ rubygems_version: 2.2.2
106
126
  signing_key:
107
- specification_version: 3
127
+ specification_version: 4
108
128
  summary: Useful stuff for testing with Capybara
109
129
  test_files: []