You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

data_table_spec.rb 20 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. # frozen_string_literal: true
  2. require 'spec_helper'
  3. require 'cucumber/multiline_argument/data_table'
  4. module Cucumber
  5. module MultilineArgument
  6. describe DataTable do
  7. before do
  8. @table = DataTable.from([
  9. %w[one four seven],
  10. %w[4444 55555 666666]
  11. ])
  12. end
  13. it 'should have rows' do
  14. expect(@table.cells_rows[0].map(&:value)).to eq %w[one four seven]
  15. end
  16. it 'should have columns' do
  17. expect(@table.columns[1].map(&:value)).to eq %w[four 55555]
  18. end
  19. it 'should have same cell objects in rows and columns' do
  20. # 666666
  21. expect(@table.cells_rows[1][2]).to equal(@table.columns[2][1])
  22. end
  23. it 'should be convertible to an array of hashes' do
  24. expect(@table.hashes).to eq [
  25. { 'one' => '4444', 'four' => '55555', 'seven' => '666666' }
  26. ]
  27. end
  28. it 'should accept symbols as keys for the hashes' do
  29. expect(@table.hashes.first[:one]).to eq '4444'
  30. end
  31. it 'should return the row values in order' do
  32. expect(@table.rows.first).to eq %w[4444 55555 666666]
  33. end
  34. describe '#symbolic_hashes' do
  35. it 'should covert data table to an array of hashes with symbols as keys' do
  36. ast_table = Cucumber::Core::Test::DataTable.new([['foo', 'Bar', 'Foo Bar'], %w[1 22 333]])
  37. data_table = DataTable.new(ast_table)
  38. expect(data_table.symbolic_hashes).to eq([{ foo: '1', bar: '22', foo_bar: '333' }])
  39. end
  40. it 'should be able to be accessed multiple times' do
  41. @table.symbolic_hashes
  42. expect { @table.symbolic_hashes }.to_not raise_error
  43. end
  44. it 'should not interfere with use of #hashes' do
  45. @table.symbolic_hashes
  46. expect { @table.hashes }.to_not raise_error
  47. end
  48. end
  49. describe '#map_column' do
  50. it 'should allow mapping columns' do
  51. new_table = @table.map_column('one', &:to_i)
  52. expect(new_table.hashes.first['one']).to eq 4444
  53. end
  54. it 'applies the block once to each value' do
  55. headers = ['header']
  56. rows = ['value']
  57. table = DataTable.from [headers, rows]
  58. count = 0
  59. new_table = table.map_column('header') { |_value| count += 1 }
  60. new_table.rows
  61. expect(count).to eq rows.size
  62. end
  63. it 'should allow mapping columns and take a symbol as the column name' do
  64. new_table = @table.map_column(:one, &:to_i)
  65. expect(new_table.hashes.first['one']).to eq 4444
  66. end
  67. it 'should allow mapping columns and modify the rows as well' do
  68. new_table = @table.map_column(:one, &:to_i)
  69. expect(new_table.rows.first).to include(4444)
  70. expect(new_table.rows.first).to_not include('4444')
  71. end
  72. it 'should pass silently if a mapped column does not exist in non-strict mode' do
  73. expect do
  74. new_table = @table.map_column('two', strict: false, &:to_i)
  75. new_table.hashes
  76. end.not_to raise_error
  77. end
  78. it 'should fail if a mapped column does not exist in strict mode' do
  79. expect do
  80. new_table = @table.map_column('two', strict: true, &:to_i)
  81. new_table.hashes
  82. end.to raise_error('The column named "two" does not exist')
  83. end
  84. it 'should return a new table' do
  85. expect(@table.map_column(:one, &:to_i)).to_not eq @table
  86. end
  87. end
  88. describe '#match' do
  89. before(:each) do
  90. @table = DataTable.from([
  91. %w[one four seven],
  92. %w[4444 55555 666666]
  93. ])
  94. end
  95. it 'returns nil if headers do not match' do
  96. expect(@table.match('does,not,match')).to be_nil
  97. end
  98. it 'requires a table: prefix on match' do
  99. expect(@table.match('table:one,four,seven')).to_not be_nil
  100. end
  101. it 'does not match if no table: prefix on match' do
  102. expect(@table.match('one,four,seven')).to be_nil
  103. end
  104. end
  105. describe '#transpose' do
  106. before(:each) do
  107. @table = DataTable.from([
  108. %w[one 1111],
  109. %w[two 22222]
  110. ])
  111. end
  112. it 'should be convertible in to an array where each row is a hash' do
  113. expect(@table.transpose.hashes[0]).to eq('one' => '1111', 'two' => '22222')
  114. end
  115. end
  116. describe '#rows_hash' do
  117. it 'should return a hash of the rows' do
  118. table = DataTable.from([
  119. %w[one 1111],
  120. %w[two 22222]
  121. ])
  122. expect(table.rows_hash).to eq('one' => '1111', 'two' => '22222')
  123. end
  124. it "should fail if the table doesn't have two columns" do
  125. faulty_table = DataTable.from([
  126. %w[one 1111 abc],
  127. %w[two 22222 def]
  128. ])
  129. expect do
  130. faulty_table.rows_hash
  131. end.to raise_error('The table must have exactly 2 columns')
  132. end
  133. it 'should support header and column mapping' do
  134. table = DataTable.from([
  135. %w[one 1111],
  136. %w[two 22222]
  137. ])
  138. t2 = table.map_headers({ 'two' => 'Two' }, &:upcase)
  139. .map_column('two', strict: false, &:to_i)
  140. expect(t2.rows_hash).to eq('ONE' => '1111', 'Two' => 22_222)
  141. end
  142. end
  143. describe '#map_headers' do
  144. let(:table) do
  145. DataTable.from([
  146. %w[ANT ANTEATER],
  147. %w[4444 55555]
  148. ])
  149. end
  150. it 'renames the columns to the specified values in the provided hash' do
  151. table2 = @table.map_headers('one' => :three)
  152. expect(table2.hashes.first[:three]).to eq '4444'
  153. end
  154. it 'allows renaming columns using regexp' do
  155. table2 = @table.map_headers(/one|uno/ => :three)
  156. expect(table2.hashes.first[:three]).to eq '4444'
  157. end
  158. it 'copies column mappings' do
  159. table = @table.map_column('one', &:to_i)
  160. table2 = table.map_headers('one' => 'three')
  161. expect(table2.hashes.first['three']).to eq 4444
  162. end
  163. it 'takes a block and operates on all the headers with it' do
  164. table2 = table.map_headers(&:downcase)
  165. expect(table2.hashes.first.keys).to match %w[ant anteater]
  166. end
  167. it 'treats the mappings in the provided hash as overrides when used with a block' do
  168. table2 = table.map_headers('ANT' => 'foo', &:downcase)
  169. expect(table2.hashes.first.keys).to match %w[foo anteater]
  170. end
  171. end
  172. describe 'diff!' do
  173. it 'should detect a complex diff' do
  174. t1 = DataTable.from(%(
  175. | 1 | 22 | 333 | 4444 |
  176. | 55555 | 666666 | 7777777 | 88888888 |
  177. | 999999999 | 0000000000 | 01010101010 | 121212121212 |
  178. | 4000 | ABC | DEF | 50000 |
  179. ))
  180. t2 = DataTable.from(%(
  181. | a | 4444 | 1 |
  182. | bb | 88888888 | 55555 |
  183. | ccc | xxxxxxxx | 999999999 |
  184. | dddd | 4000 | 300 |
  185. | e | 50000 | 4000 |
  186. ))
  187. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  188. expect(error.table.to_s(indent: 14, color: false)).to eq %{
  189. | 1 | (-) 22 | (-) 333 | 4444 | (+) a |
  190. | 55555 | (-) 666666 | (-) 7777777 | 88888888 | (+) bb |
  191. | (-) 999999999 | (-) 0000000000 | (-) 01010101010 | (-) 121212121212 | (+) |
  192. | (+) 999999999 | (+) | (+) | (+) xxxxxxxx | (+) ccc |
  193. | (+) 300 | (+) | (+) | (+) 4000 | (+) dddd |
  194. | 4000 | (-) ABC | (-) DEF | 50000 | (+) e |
  195. }
  196. end
  197. end
  198. it 'should not change table when diffed with identical' do
  199. t = DataTable.from(%(
  200. |a|b|c|
  201. |d|e|f|
  202. |g|h|i|
  203. ))
  204. t.diff!(t.dup)
  205. expect(t.to_s(indent: 12, color: false)).to eq %(
  206. | a | b | c |
  207. | d | e | f |
  208. | g | h | i |
  209. )
  210. end
  211. context 'with empty tables' do
  212. it 'should allow diffing empty tables' do
  213. t1 = DataTable.from([[]])
  214. t2 = DataTable.from([[]])
  215. expect { t1.diff!(t2) }.not_to raise_error
  216. end
  217. it 'should be able to diff when the right table is empty' do
  218. t1 = DataTable.from(%(
  219. |a|b|c|
  220. |d|e|f|
  221. |g|h|i|
  222. ))
  223. t2 = DataTable.from([[]])
  224. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  225. expect(error.table.to_s(indent: 16, color: false)).to eq %{
  226. | (-) a | (-) b | (-) c |
  227. | (-) d | (-) e | (-) f |
  228. | (-) g | (-) h | (-) i |
  229. }
  230. end
  231. end
  232. it 'should be able to diff when the left table is empty' do
  233. t1 = DataTable.from([[]])
  234. t2 = DataTable.from(%(
  235. |a|b|c|
  236. |d|e|f|
  237. |g|h|i|
  238. ))
  239. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  240. expect(error.table.to_s(indent: 16, color: false)).to eq %{
  241. | (+) a | (+) b | (+) c |
  242. | (+) d | (+) e | (+) f |
  243. | (+) g | (+) h | (+) i |
  244. }
  245. end
  246. end
  247. end
  248. context 'in case of duplicate header values' do
  249. it 'raises no error for two identical tables' do
  250. t = DataTable.from(%(
  251. |a|a|c|
  252. |d|e|f|
  253. |g|h|i|
  254. ))
  255. t.diff!(t.dup)
  256. expect(t.to_s(indent: 12, color: false)).to eq %(
  257. | a | a | c |
  258. | d | e | f |
  259. | g | h | i |
  260. )
  261. end
  262. it 'detects a diff in one cell' do
  263. t1 = DataTable.from(%(
  264. |a|a|c|
  265. |d|e|f|
  266. |g|h|i|
  267. ))
  268. t2 = DataTable.from(%(
  269. |a|a|c|
  270. |d|oops|f|
  271. |g|h|i|
  272. ))
  273. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  274. expect(error.table.to_s(indent: 16, color: false)).to eq %{
  275. | a | a | c |
  276. | (-) d | (-) e | (-) f |
  277. | (+) d | (+) oops | (+) f |
  278. | g | h | i |
  279. }
  280. end
  281. end
  282. it 'detects missing columns' do
  283. t1 = DataTable.from(%(
  284. |a|a|b|c|
  285. |d|d|e|f|
  286. |g|g|h|i|
  287. ))
  288. t2 = DataTable.from(%(
  289. |a|b|c|
  290. |d|e|f|
  291. |g|h|i|
  292. ))
  293. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  294. expect(error.table.to_s(indent: 16, color: false)).to eq %{
  295. | a | (-) a | b | c |
  296. | d | (-) d | e | f |
  297. | g | (-) g | h | i |
  298. }
  299. end
  300. end
  301. it 'detects surplus columns' do
  302. t1 = DataTable.from(%(
  303. |a|b|c|
  304. |d|e|f|
  305. |g|h|i|
  306. ))
  307. t2 = DataTable.from(%(
  308. |a|b|a|c|
  309. |d|e|d|f|
  310. |g|h|g|i|
  311. ))
  312. expect { t1.diff!(t2, surplus_col: true) }.to raise_error(DataTable::Different) do |error|
  313. expect(error.table.to_s(indent: 16, color: false)).to eq %{
  314. | a | b | c | (+) a |
  315. | d | e | f | (+) d |
  316. | g | h | i | (+) g |
  317. }
  318. end
  319. end
  320. end
  321. it 'should inspect missing and surplus cells' do
  322. t1 = DataTable.from([
  323. %w[name male lastname swedish],
  324. %w[aslak true hellesøy false]
  325. ])
  326. t2 = DataTable.from([
  327. %w[name male lastname swedish],
  328. ['aslak', true, 'hellesøy', false]
  329. ])
  330. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  331. expect(error.table.to_s(indent: 14, color: false)).to eq %{
  332. | name | male | lastname | swedish |
  333. | (-) aslak | (-) (i) "true" | (-) hellesøy | (-) (i) "false" |
  334. | (+) aslak | (+) (i) true | (+) hellesøy | (+) (i) false |
  335. }
  336. end
  337. end
  338. it 'should allow column mapping of target before diffing' do
  339. t1 = DataTable.from([
  340. %w[name male],
  341. %w[aslak true]
  342. ])
  343. t2 = DataTable.from([
  344. %w[name male],
  345. ['aslak', true]
  346. ])
  347. t1.map_column('male') { |m| m == 'true' }.diff!(t2)
  348. expect(t1.to_s(indent: 12, color: false)).to eq %(
  349. | name | male |
  350. | aslak | true |
  351. )
  352. end
  353. it 'should allow column mapping of argument before diffing' do
  354. t1 = DataTable.from([
  355. %w[name male],
  356. ['aslak', true]
  357. ])
  358. t2 = DataTable.from([
  359. %w[name male],
  360. %w[aslak true]
  361. ])
  362. t2.diff!(t1.map_column('male') { 'true' })
  363. expect(t1.to_s(indent: 12, color: false)).to eq %(
  364. | name | male |
  365. | aslak | true |
  366. )
  367. end
  368. it 'should allow header mapping before diffing' do
  369. t1 = DataTable.from([
  370. %w[Name Male],
  371. %w[aslak true]
  372. ])
  373. t1 = t1.map_headers('Name' => 'name', 'Male' => 'male')
  374. t1 = t1.map_column('male') { |m| m == 'true' }
  375. t2 = DataTable.from([
  376. %w[name male],
  377. ['aslak', true]
  378. ])
  379. t1.diff!(t2)
  380. expect(t1.to_s(indent: 12, color: false)).to eq %(
  381. | name | male |
  382. | aslak | true |
  383. )
  384. end
  385. it 'should detect seemingly identical tables as different' do
  386. t1 = DataTable.from([
  387. %w[X Y],
  388. %w[2 1]
  389. ])
  390. t2 = DataTable.from([
  391. %w[X Y],
  392. [2, 1]
  393. ])
  394. expect { t1.diff!(t2) }.to raise_error(DataTable::Different) do |error|
  395. expect(error.table.to_s(indent: 14, color: false)).to eq %{
  396. | X | Y |
  397. | (-) (i) "2" | (-) (i) "1" |
  398. | (+) (i) 2 | (+) (i) 1 |
  399. }
  400. end
  401. end
  402. it 'should not allow mappings that match more than 1 column' do
  403. t1 = DataTable.from([
  404. %w[Cuke Duke],
  405. %w[Foo Bar]
  406. ])
  407. expect do
  408. t1 = t1.map_headers(/uk/ => 'u')
  409. t1.hashes
  410. end.to raise_error(%(2 headers matched /uk/: ["Cuke", "Duke"]))
  411. end
  412. describe 'raising' do
  413. before do
  414. @t = DataTable.from(%(
  415. | a | b |
  416. | c | d |
  417. ))
  418. expect(@t).not_to eq nil
  419. end
  420. it 'should raise on missing rows' do
  421. t = DataTable.from(%(
  422. | a | b |
  423. ))
  424. expect { @t.dup.diff!(t) }.to raise_error(DataTable::Different)
  425. expect { @t.dup.diff!(t, missing_row: false) }.not_to raise_error
  426. end
  427. it 'should not raise on surplus rows when surplus is at the end' do
  428. t = DataTable.from(%(
  429. | a | b |
  430. | c | d |
  431. | e | f |
  432. ))
  433. expect { @t.dup.diff!(t) }.to raise_error(DataTable::Different)
  434. expect { @t.dup.diff!(t, surplus_row: false) }.not_to raise_error
  435. end
  436. it 'should not raise on surplus rows when surplus is interleaved' do
  437. t1 = DataTable.from(%(
  438. | row_1 | row_2 |
  439. | four | 4 |
  440. ))
  441. t2 = DataTable.from(%(
  442. | row_1 | row_2 |
  443. | one | 1 |
  444. | two | 2 |
  445. | three | 3 |
  446. | four | 4 |
  447. | five | 5 |
  448. ))
  449. expect { t1.dup.diff!(t2) }.to raise_error(DataTable::Different)
  450. expect { t1.dup.diff!(t2, surplus_row: false) }.not_to raise_error
  451. end
  452. it 'should raise on missing columns' do
  453. t = DataTable.from(%(
  454. | a |
  455. | c |
  456. ))
  457. expect { @t.dup.diff!(t) }.to raise_error(DataTable::Different)
  458. expect { @t.dup.diff!(t, missing_col: false) }.not_to raise_error
  459. end
  460. it 'should not raise on surplus columns' do
  461. t = DataTable.from(%(
  462. | a | b | x |
  463. | c | d | y |
  464. ))
  465. expect { @t.dup.diff!(t) }.not_to raise_error
  466. expect { @t.dup.diff!(t, surplus_col: true) }.to raise_error(DataTable::Different)
  467. end
  468. it 'should not raise on misplaced columns' do
  469. t = DataTable.from(%(
  470. | b | a |
  471. | d | c |
  472. ))
  473. expect { @t.dup.diff!(t) }.not_to raise_error
  474. expect { @t.dup.diff!(t, misplaced_col: true) }.to raise_error(DataTable::Different)
  475. end
  476. end
  477. it 'can compare to an Array' do
  478. t = DataTable.from(%(
  479. | b | a |
  480. | d | c |
  481. ))
  482. other = [%w[b a], %w[d c]]
  483. expect { t.diff!(other) }.not_to raise_error
  484. end
  485. end
  486. describe '#from' do
  487. it 'should allow Array of Hash' do
  488. t1 = DataTable.from([{ 'name' => 'aslak', 'male' => 'true' }])
  489. expect(t1.to_s(indent: 12, color: false)).to eq %(
  490. | male | name |
  491. | true | aslak |
  492. )
  493. end
  494. end
  495. end
  496. end
  497. end

No Description

Contributors (1)