input_spec.rb 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. require 'spec_helper'
  2. RSpec.describe 'input tests' do
  3. describe 'input' do
  4. it_fails_firefox 'should upload the file', sinatra: true do
  5. page.goto("#{server_prefix}/input/fileupload.html")
  6. filepath = File.join('spec', 'assets', 'file-to-upload.txt')
  7. input = page.query_selector('input')
  8. js = <<~JAVASCRIPT
  9. (e) => {
  10. globalThis._inputEvents = [];
  11. e.addEventListener('change', (ev) =>
  12. globalThis._inputEvents.push(ev.type)
  13. );
  14. e.addEventListener('input', (ev) =>
  15. globalThis._inputEvents.push(ev.type)
  16. );
  17. }
  18. JAVASCRIPT
  19. page.evaluate(js, input)
  20. input.upload_file(filepath)
  21. expect(page.evaluate('(e) => e.files[0].name', input)).to eq('file-to-upload.txt')
  22. expect(page.evaluate('(e) => e.files[0].type', input)).to eq('text/plain')
  23. expect(page.evaluate('() => globalThis._inputEvents')).to eq(%w(input change))
  24. js = <<~JAVASCRIPT
  25. (e) => {
  26. const reader = new FileReader();
  27. const promise = new Promise((fulfill) => (reader.onload = fulfill));
  28. reader.readAsText(e.files[0]);
  29. return promise.then(() => reader.result);
  30. }
  31. JAVASCRIPT
  32. expect(page.evaluate(js, input)).to eq('contents of the file')
  33. end
  34. end
  35. describe 'Page#wait_for_file_chooser' do
  36. it_fails_firefox 'should work when file input is attached to DOM' do
  37. page.content = '<input type=file>'
  38. chooser = page.wait_for_file_chooser do
  39. page.click('input')
  40. end
  41. expect(chooser).to be_a(Puppeteer::FileChooser)
  42. end
  43. it_fails_firefox 'should work when file input is not attached to DOM' do
  44. js = <<~JAVASCRIPT
  45. () => {
  46. const el = document.createElement('input');
  47. el.type = 'file';
  48. el.click();
  49. }
  50. JAVASCRIPT
  51. chooser = page.wait_for_file_chooser do
  52. page.evaluate(js)
  53. end
  54. expect(chooser).to be_a(Puppeteer::FileChooser)
  55. end
  56. it 'should respect timeout' do
  57. expect { page.wait_for_file_chooser(timeout: 1) }.to raise_error(/waiting for filechooser failed: timeout 1ms exceeded/)
  58. end
  59. it 'should respect default timeout when there is no custom timeout' do
  60. page.default_timeout = 1
  61. expect { page.wait_for_file_chooser }.to raise_error(/waiting for filechooser failed: timeout 1ms exceeded/)
  62. end
  63. it 'should prioritize exact timeout over default timeout' do
  64. page.default_timeout = 5000
  65. expect { page.wait_for_file_chooser(timeout: 1) }.to raise_error(/waiting for filechooser failed: timeout 1ms exceeded/)
  66. end
  67. it_fails_firefox 'should work with no timeout' do
  68. js = <<~JAVASCRIPT
  69. () => {
  70. setTimeout(() => {
  71. const el = document.createElement('input');
  72. el.type = 'file';
  73. el.click();
  74. }, 50)
  75. }
  76. JAVASCRIPT
  77. chooser = page.wait_for_file_chooser do
  78. page.evaluate(js)
  79. end
  80. expect(chooser).to be_a(Puppeteer::FileChooser)
  81. end
  82. it_fails_firefox 'should return the same file chooser when there are many watchdogs simultaneously' do
  83. page.content = '<input type=file>'
  84. choosers = await_all(
  85. page.async_wait_for_file_chooser,
  86. page.async_wait_for_file_chooser,
  87. page.async_eval_on_selector('input', '(input) => input.click()'),
  88. ).first(2)
  89. expect(choosers.first).to eq(choosers.last)
  90. end
  91. end
  92. describe 'FileChooser#accept' do
  93. let(:filepath) { File.join('spec', 'assets', 'file-to-upload.txt') }
  94. it_fails_firefox 'should accept single file' do
  95. page.content = "<input type=file oninput='javascript:console.timeStamp()'>"
  96. chooser = page.wait_for_file_chooser do
  97. page.click('input')
  98. end
  99. chooser.accept(filepath)
  100. expect(page.eval_on_selector('input', "(input) => input.files.length")).to eq(1)
  101. expect(page.eval_on_selector('input', "(input) => input.files[0].name")).to eq("file-to-upload.txt")
  102. end
  103. it_fails_firefox 'should be able to read selected file' do
  104. page.content = '<input type=file>'
  105. future {
  106. chooser = page.wait_for_file_chooser
  107. chooser.accept(filepath)
  108. }
  109. js = <<~JAVASCRIPT
  110. async (picker) => {
  111. picker.click();
  112. await new Promise((x) => (picker.oninput = x));
  113. const reader = new FileReader();
  114. const promise = new Promise((fulfill) => (reader.onload = fulfill));
  115. reader.readAsText(picker.files[0]);
  116. return promise.then(() => reader.result);
  117. }
  118. JAVASCRIPT
  119. expect(page.eval_on_selector('input', js)).to eq('contents of the file')
  120. end
  121. it_fails_firefox 'should be able to reset selected files with empty file list' do
  122. page.content = '<input type=file>'
  123. future {
  124. chooser = page.wait_for_file_chooser
  125. chooser.accept(filepath)
  126. }
  127. js = <<~JAVASCRIPT
  128. async (picker) => {
  129. picker.click();
  130. await new Promise((x) => (picker.oninput = x));
  131. return picker.files.length;
  132. }
  133. JAVASCRIPT
  134. expect(page.eval_on_selector('input', js)).to eq(1)
  135. future {
  136. chooser = page.wait_for_file_chooser
  137. chooser.accept([])
  138. }
  139. js = <<~JAVASCRIPT
  140. async (picker) => {
  141. picker.click();
  142. await new Promise((x) => (picker.oninput = x));
  143. return picker.files.length;
  144. }
  145. JAVASCRIPT
  146. expect(page.eval_on_selector('input', js)).to eq(0)
  147. end
  148. it_fails_firefox 'should not accept multiple files for single-file input' do
  149. page.content = '<input type=file>'
  150. chooser = page.wait_for_file_chooser do
  151. page.click('input')
  152. end
  153. pprt_png = File.join('spec', 'assets', 'pptr.png')
  154. expect { chooser.accept([filepath, pprt_png]) }.to raise_error(/Multiple file uploads only work with <input type=file multiple>/)
  155. end
  156. it_fails_firefox 'should succeed even for non-existent files' do
  157. page.content = '<input type=file>'
  158. chooser = page.wait_for_file_chooser do
  159. page.click('input')
  160. end
  161. chooser.accept(['file-does-not-exist.txt'])
  162. end
  163. it_fails_firefox 'should error on read of non-existent files' do
  164. page.content = '<input type=file>'
  165. future {
  166. chooser = page.wait_for_file_chooser
  167. chooser.accept(['file-does-not-exist.txt'])
  168. }
  169. js = <<~JAVASCRIPT
  170. async (picker) => {
  171. picker.click();
  172. await new Promise((x) => (picker.oninput = x));
  173. const reader = new FileReader();
  174. const promise = new Promise((fulfill) => (reader.onerror = fulfill));
  175. reader.readAsText(picker.files[0]);
  176. return promise.then(() => false);
  177. }
  178. JAVASCRIPT
  179. expect(page.eval_on_selector('input', js)).to eq(false)
  180. end
  181. it_fails_firefox 'should fail when accepting file chooser twice' do
  182. page.content = '<input type=file>'
  183. chooser = page.wait_for_file_chooser do
  184. page.eval_on_selector('input', '(input) => input.click()')
  185. end
  186. chooser.accept([])
  187. expect { chooser.accept([]) }.to raise_error(/Cannot accept FileChooser which is already handled!/)
  188. end
  189. end
  190. describe 'FileChooser#cancel' do
  191. it_fails_firefox 'should cancel dialog' do
  192. # Consider file chooser canceled if we can summon another one.
  193. # There's no reliable way in WebPlatform to see that FileChooser was
  194. # canceled.
  195. page.content = '<input type=file>'
  196. chooser = page.wait_for_file_chooser do
  197. page.eval_on_selector('input', '(input) => input.click()')
  198. end
  199. chooser.cancel
  200. # If this resolves, than we successfully canceled file chooser.
  201. Timeout.timeout(2) do
  202. chooser = page.wait_for_file_chooser do
  203. page.eval_on_selector('input', '(input) => input.click()')
  204. end
  205. end
  206. end
  207. it_fails_firefox 'should fail when canceling file chooser twice' do
  208. page.content = '<input type=file>'
  209. chooser = page.wait_for_file_chooser do
  210. page.eval_on_selector('input', '(input) => input.click()')
  211. end
  212. chooser.cancel
  213. expect { chooser.cancel }.to raise_error(/Cannot cancel FileChooser which is already handled!/)
  214. end
  215. end
  216. describe 'FileChooser#multiple?' do
  217. it_fails_firefox 'should work for single file pick' do
  218. page.content = '<input type=file>'
  219. chooser = page.wait_for_file_chooser do
  220. page.click('input')
  221. end
  222. expect(chooser).not_to be_multiple
  223. end
  224. it_fails_firefox 'should work for "multiple"' do
  225. page.content = '<input multiple type=file>'
  226. chooser = page.wait_for_file_chooser do
  227. page.click('input')
  228. end
  229. expect(chooser).to be_multiple
  230. end
  231. it_fails_firefox 'should work for "webkitdirectory"' do
  232. page.content = '<input multiple webkitdirectory type=file>'
  233. chooser = page.wait_for_file_chooser do
  234. page.click('input')
  235. end
  236. expect(chooser).to be_multiple
  237. end
  238. end
  239. end