test_devtools.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # Copyright (c) 2017 crocoite contributors
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. # THE SOFTWARE.
  20. import asyncio
  21. import pytest
  22. from aiohttp import web
  23. import websockets
  24. from .devtools import Browser, Tab, MethodNotFound, Crashed, \
  25. InvalidParameter, Process, Passthrough
  26. @pytest.fixture
  27. async def browser ():
  28. async with Process () as url:
  29. yield Browser (url)
  30. @pytest.fixture
  31. async def tab (browser):
  32. async with browser as tab:
  33. yield tab
  34. # make sure there are no transactions left over (i.e. no unawaited requests)
  35. assert not tab.transactions
  36. docBody = "<html><body><p>Hello, world</p></body></html>"
  37. async def hello(request):
  38. return web.Response(text=docBody, content_type='text/html')
  39. @pytest.fixture
  40. async def server ():
  41. """ Simple HTTP server for testing notifications """
  42. app = web.Application()
  43. app.add_routes([web.get('/', hello)])
  44. runner = web.AppRunner(app)
  45. await runner.setup()
  46. site = web.TCPSite(runner, 'localhost', 8080)
  47. await site.start()
  48. yield app
  49. await runner.cleanup ()
  50. @pytest.mark.asyncio
  51. async def test_tab_create (tab):
  52. """ Creating tabs works """
  53. assert isinstance (tab, Tab)
  54. version = await tab.Browser.getVersion ()
  55. assert 'protocolVersion' in version
  56. assert tab.pending == 0
  57. @pytest.mark.asyncio
  58. async def test_tab_close (browser):
  59. """ Tabs are closed after using them """
  60. async with browser as tab:
  61. tid = tab.id
  62. # give the browser some time to close the tab
  63. await asyncio.sleep (0.5)
  64. tabs = [t['id'] async for t in browser]
  65. assert tid not in tabs
  66. @pytest.mark.asyncio
  67. async def test_tab_notify_enable_disable (tab):
  68. """ Make sure enabling/disabling notifications works for all known
  69. namespaces """
  70. for name in ('Debugger', 'DOM', 'Log', 'Network', 'Page', 'Performance',
  71. 'Profiler', 'Runtime', 'Security'):
  72. f = getattr (tab, name)
  73. await f.enable ()
  74. await f.disable ()
  75. @pytest.mark.asyncio
  76. async def test_tab_unknown_method (tab):
  77. with pytest.raises (MethodNotFound):
  78. await tab.Nonexistent.foobar ()
  79. @pytest.mark.asyncio
  80. async def test_tab_invalid_argument (tab):
  81. # should be string
  82. with pytest.raises (InvalidParameter):
  83. await tab.Page.captureScreenshot (format=123)
  84. with pytest.raises (InvalidParameter):
  85. await tab.Page.captureScreenshot (format=[123])
  86. with pytest.raises (InvalidParameter):
  87. await tab.Page.captureScreenshot (format={123: '456'})
  88. @pytest.mark.asyncio
  89. async def test_tab_crash (tab):
  90. with pytest.raises (Crashed):
  91. await tab.Page.crash ()
  92. # caling anything else now should fail as well
  93. with pytest.raises (Crashed):
  94. await tab.Browser.getVersion ()
  95. @pytest.mark.asyncio
  96. async def test_load (tab, server):
  97. await tab.Network.enable ()
  98. await tab.Page.navigate (url='http://localhost:8080')
  99. haveRequest = False
  100. haveResponse = False
  101. haveData = False
  102. haveFinished = False
  103. haveBody = False
  104. req = None
  105. resp = None
  106. while not haveBody:
  107. method, data = await tab.get ()
  108. # it can be either of those two in no specified order
  109. if method in (tab.Network.requestWillBeSent, tab.Network.requestWillBeSentExtraInfo) and not haveResponse:
  110. if req is None:
  111. req = data
  112. assert data['requestId'] == req['requestId']
  113. haveRequest = True
  114. elif method in (tab.Network.responseReceived, tab.Network.responseReceivedExtraInfo) and haveRequest:
  115. if resp is None:
  116. resp = data
  117. assert data['requestId'] == resp['requestId']
  118. haveResponse = True
  119. elif haveRequest and haveResponse and method == tab.Network.dataReceived:
  120. assert data['dataLength'] == len (docBody)
  121. assert data['requestId'] == req['requestId']
  122. haveData = True
  123. elif haveData:
  124. assert method == tab.Network.loadingFinished
  125. assert data['requestId'] == req['requestId']
  126. haveBody = True
  127. elif haveFinished:
  128. body = await tab.Network.getResponseBody (requestId=req['requestId'])
  129. assert body['body'] == docBody
  130. haveBody = True
  131. else:
  132. assert False, (method, req)
  133. await tab.Network.disable ()
  134. assert tab.pending == 0
  135. @pytest.mark.asyncio
  136. async def test_recv_failure(browser):
  137. """ Inject failure into receiver process and crash it """
  138. async with browser as tab:
  139. await tab.ws.close ()
  140. with pytest.raises (Crashed):
  141. await tab.Browser.getVersion ()
  142. async with browser as tab:
  143. await tab.ws.close ()
  144. with pytest.raises (Crashed):
  145. await tab.get ()
  146. async with browser as tab:
  147. handle = asyncio.ensure_future (tab.get ())
  148. await tab.ws.close ()
  149. with pytest.raises (Crashed):
  150. await handle
  151. @pytest.mark.asyncio
  152. async def test_tab_function (tab):
  153. assert tab.Network.enable.name == 'Network.enable'
  154. assert tab.Network.disable == tab.Network.disable
  155. assert tab.Network.enable != tab.Network.disable
  156. assert tab.Network != tab.Network.enable
  157. assert callable (tab.Network.enable)
  158. assert not callable (tab.Network.enable.name)
  159. assert 'Network.enable' in repr (tab.Network.enable)
  160. @pytest.mark.asyncio
  161. async def test_tab_function_hash (tab):
  162. d = {tab.Network.enable: 1, tab.Network.disable: 2, tab.Page: 3,
  163. tab.Page.enable: 4}
  164. assert len (d) == 4
  165. @pytest.mark.asyncio
  166. async def test_ws_ping(tab):
  167. """
  168. Chrome does not like websocket pings and closes the connection if it
  169. receives one. Not sure why.
  170. """
  171. with pytest.raises (Crashed):
  172. await tab.ws.ping ()
  173. await tab.Browser.getVersion ()
  174. @pytest.mark.asyncio
  175. async def test_passthrough ():
  176. """ Null service returns the url as is """
  177. url = 'http://localhost:12345'
  178. async with Passthrough (url) as u:
  179. assert str (u) == url