oopif_spec.rb 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. require 'spec_helper'
  2. metadata = {
  3. skip: Puppeteer.env.firefox?,
  4. enable_site_per_process_flag: true,
  5. browser_context: :incognito,
  6. sinatra: true,
  7. }
  8. RSpec.describe 'OOPIF', **metadata do
  9. include Utils::AttachFrame
  10. include Utils::DetachFrame
  11. include Utils::NavigateFrame
  12. def oopifs(context)
  13. context.targets.select do |target|
  14. target.raw_type == 'iframe'
  15. end
  16. end
  17. it 'should treat OOP iframes and normal iframes the same' do
  18. page.goto(server_empty_page)
  19. predicate = ->(frame) { frame.url&.end_with?('/empty.html') }
  20. page.wait_for_frame(predicate: predicate) do
  21. attach_frame(page, 'frame1', server_empty_page)
  22. attach_frame(page, 'frame2', "#{server_cross_process_prefix}/empty.html")
  23. end
  24. expect(page.main_frame.child_frames.size).to eq(2)
  25. end
  26. it 'should track navigations within OOP iframes' do
  27. page.goto(server_empty_page)
  28. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  29. frame = page.wait_for_frame(predicate: predicate) do
  30. attach_frame(page, 'frame1', "#{server_cross_process_prefix}/empty.html")
  31. end
  32. expect(frame.url).to end_with('/empty.html')
  33. navigate_frame(page, 'frame1', "#{server_cross_process_prefix}/frames/frame.html")
  34. expect(frame.url).to end_with('/frames/frame.html')
  35. end
  36. it 'should support OOP iframes becoming normal iframes again' do
  37. page.goto(server_empty_page)
  38. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  39. frame = page.wait_for_frame(predicate: predicate) do
  40. attach_frame(page, 'frame1', server_empty_page)
  41. end
  42. expect(frame).not_to be_oop_frame
  43. navigate_frame(page, 'frame1', "#{server_cross_process_prefix}/empty.html")
  44. expect(frame).to be_oop_frame
  45. navigate_frame(page, 'frame1', server_empty_page)
  46. expect(frame).not_to be_oop_frame
  47. expect(page.frames.size).to eq(2)
  48. end
  49. it 'should support frames within OOP frames' do
  50. page.goto(server_empty_page)
  51. frame1_promise = page.async_wait_for_frame(predicate: -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 })
  52. frame2_promise = page.async_wait_for_frame(predicate: -> (frame) { page.frames.index { |_frame| _frame == frame } == 2 })
  53. attach_frame(page, 'frame1', "#{server_cross_process_prefix}/frames/one-frame.html")
  54. frame1, frame2 = await_all(frame1_promise, frame2_promise)
  55. expect(frame1.url).to end_with('/one-frame.html')
  56. expect(frame2.url).to end_with('/frames/frame.html')
  57. end
  58. it 'should support OOP iframes getting detached' do
  59. page.goto(server_empty_page)
  60. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  61. frame = page.wait_for_frame(predicate: predicate) do
  62. attach_frame(page, 'frame1', server_empty_page)
  63. navigate_frame(page, 'frame1', "#{server_cross_process_prefix}/empty.html")
  64. end
  65. expect(frame).to be_oop_frame
  66. detach_frame(page, 'frame1')
  67. expect(page.frames.size).to eq(1)
  68. end
  69. it 'should support wait for navigation for transitions from local to OOPIF' do
  70. page.goto(server_empty_page)
  71. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  72. frame = page.wait_for_frame(predicate: predicate) do
  73. attach_frame(page, 'frame1', server_empty_page)
  74. end
  75. expect(frame).not_to be_oop_frame
  76. frame.wait_for_navigation do
  77. navigate_frame(page, 'frame1', "#{server_cross_process_prefix}/empty.html")
  78. end
  79. expect(frame).to be_oop_frame
  80. detach_frame(page, 'frame1')
  81. expect(page.frames.count).to eq(1)
  82. end
  83. it 'should keep track of a frames OOP state' do
  84. page.goto(server_empty_page)
  85. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  86. frame = page.wait_for_frame(predicate: predicate) do
  87. attach_frame(page, 'frame1', server_empty_page)
  88. end
  89. expect(frame.url).to include('/empty.html')
  90. navigate_frame(page, 'frame1', server_empty_page)
  91. expect(frame.url).to eq(server_empty_page)
  92. end
  93. it 'should support evaluating in oop iframes' do
  94. page.goto(server_empty_page)
  95. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  96. frame = page.wait_for_frame(predicate: predicate) do
  97. attach_frame(page, 'frame1', server_empty_page)
  98. end
  99. frame.evaluate("() => { _test = 'Test 123'; }")
  100. result = frame.evaluate('() => window._test')
  101. expect(result).to eq('Test 123')
  102. end
  103. it 'should provide access to elements' do
  104. page.goto(server_empty_page)
  105. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  106. frame = page.wait_for_frame(predicate: predicate) do
  107. attach_frame(page, 'frame1', server_empty_page)
  108. end
  109. frame.evaluate(<<~JAVASCRIPT)
  110. () => {
  111. const button = document.createElement('button');
  112. button.id = 'test-button';
  113. document.body.appendChild(button);
  114. }
  115. JAVASCRIPT
  116. frame.click('#test-button')
  117. end
  118. it 'should report oopif frames' do
  119. predicate = -> (frame) { frame.url&.end_with?('/oopif.html') }
  120. frame = page.wait_for_frame(predicate: predicate) do
  121. page.goto("#{server_prefix}/dynamic-oopif.html")
  122. end
  123. expect(frame).to be_oop_frame
  124. expect(oopifs(browser_context).size).to eq(1)
  125. expect(page.frames.size).to eq(2)
  126. end
  127. it 'should wait for inner OOPIFs' do
  128. predicate = -> (frame) { frame.url&.end_with?('/inner-frame2.html') }
  129. frame2 = page.wait_for_frame(predicate: predicate) do
  130. page.goto("http://mainframe:#{server_port}/main-frame.html")
  131. end
  132. expect(oopifs(browser_context).size).to eq(2)
  133. expect(page.frames.count { |frame| frame.oop_frame? }).to eq(2)
  134. expect(frame2.evaluate('() => document.querySelectorAll("button").length')).to eq(1)
  135. end
  136. it 'should load oopif iframes with subresources and request interception' do
  137. predicate = -> (frame) { frame.url&.end_with?('/oopif.html') }
  138. frame_promise = page.async_wait_for_frame(predicate: predicate)
  139. page.request_interception = true
  140. page.on('request') { |req| req.continue }
  141. page.goto("#{server_prefix}/dynamic-oopif.html")
  142. await frame_promise
  143. expect(oopifs(browser_context).size).to eq(1)
  144. end
  145. it 'should support frames within OOP iframes' do
  146. predicate = -> (frame) { frame.url&.end_with?('/oopif.html') }
  147. oop_iframe = page.wait_for_frame(predicate: predicate) do
  148. page.goto("#{server_prefix}/dynamic-oopif.html")
  149. end
  150. attach_frame(oop_iframe, 'frame1', "#{server_cross_process_prefix}/empty.html")
  151. frame1 = oop_iframe.child_frames.first
  152. expect(frame1.url).to end_with('/empty.html')
  153. navigate_frame(oop_iframe, 'frame1', "#{server_cross_process_prefix}/oopif.html")
  154. expect(frame1.url).to end_with('/oopif.html')
  155. detach_frame(oop_iframe, 'frame1')
  156. expect(oop_iframe.child_frames).to be_empty
  157. end
  158. it 'clickablePoint, boundingBox, boxModel should work for elements inside OOPIFs' do
  159. page.goto(server_empty_page)
  160. predicate = -> (frame) { page.frames.index { |_frame| _frame == frame } == 1 }
  161. frame = page.wait_for_frame(predicate: predicate) do
  162. attach_frame(page, 'frame1', "#{server_cross_process_prefix}/empty.html")
  163. end
  164. page.evaluate(<<~JAVASCRIPT)
  165. () => {
  166. document.body.style.border = '50px solid black';
  167. document.body.style.margin = '50px';
  168. document.body.style.padding = '50px';
  169. }
  170. JAVASCRIPT
  171. frame.evaluate(<<~JAVASCRIPT)
  172. () => {
  173. const button = document.createElement('button');
  174. button.id = 'test-button';
  175. button.innerText = 'click';
  176. document.body.appendChild(button);
  177. }
  178. JAVASCRIPT
  179. button = frame.wait_for_selector('#test-button', visible: true)
  180. result = button.clickable_point
  181. expect(result.x).to be > 150 # padding + margin + border left
  182. expect(result.y).to be > 150 # padding + margin + border top
  183. result_box_model = button.box_model
  184. %i(content border margin padding).each do |attr_name|
  185. result_box_model.send(attr_name).each do |part|
  186. expect(part.x).to be > 150 # padding + margin + border left
  187. expect(part.y).to be > 150 # padding + margin + border top
  188. end
  189. end
  190. result_bounding_box = button.bounding_box
  191. expect(result_bounding_box.x).to be > 150 # padding + margin + border left
  192. expect(result_bounding_box.y).to be > 150 # padding + margin + border top
  193. end
  194. it 'should support lazy OOP frames' do
  195. page.goto("#{server_prefix}/lazy-oopif-frame.html")
  196. page.viewport = Puppeteer::Viewport.new(width: 1000, height: 1000)
  197. expect(page.frames.map { |frame| frame.has_started_loading? }).to eq([true, true, false])
  198. end
  199. it 'should resolve immediately if the frame already exists' do
  200. page.goto(server_empty_page)
  201. attach_frame(page, 'frame2', "#{server_cross_process_prefix}/empty.html")
  202. predicate = ->(frame) { frame.url&.end_with?('/empty.html') }
  203. frame = page.wait_for_frame(predicate: predicate)
  204. expect(frame.url).to end_with("/empty.html")
  205. end
  206. end