|
1 | 1 | <?php
|
2 | 2 | namespace ChromeDevtoolsProtocol;
|
3 | 3 |
|
| 4 | +use ChromeDevtoolsProtocol\Exception\DeadlineException; |
4 | 5 | use ChromeDevtoolsProtocol\Exception\ErrorException;
|
5 | 6 | use ChromeDevtoolsProtocol\Instance\Launcher;
|
6 | 7 | use ChromeDevtoolsProtocol\Model\Network\EnableRequest;
|
| 8 | +use ChromeDevtoolsProtocol\Model\Network\GetResponseBodyRequest; |
| 9 | +use ChromeDevtoolsProtocol\Model\Network\LoadingFinishedEvent; |
7 | 10 | use ChromeDevtoolsProtocol\Model\Page\NavigateRequest;
|
8 | 11 | use ChromeDevtoolsProtocol\Model\Security\CertificateErrorActionEnum;
|
9 | 12 | use ChromeDevtoolsProtocol\Model\Security\CertificateErrorEvent;
|
10 | 13 | use ChromeDevtoolsProtocol\Model\Security\HandleCertificateErrorRequest;
|
11 | 14 | use ChromeDevtoolsProtocol\Model\Security\SetOverrideCertificateErrorsRequest;
|
| 15 | +use ChromeDevtoolsProtocol\WebSocket\WebSocketClient; |
12 | 16 | use PHPUnit\Framework\TestCase;
|
| 17 | +use Wrench\Payload\Payload; |
13 | 18 |
|
14 | 19 | class DevtoolsClientTest extends TestCase
|
15 | 20 | {
|
@@ -80,4 +85,83 @@ public function testErrorHandling()
|
80 | 85 | }
|
81 | 86 | }
|
82 | 87 |
|
| 88 | + /* |
| 89 | + * https://github.com/jakubkulhan/chrome-devtools-protocol/issues/14 |
| 90 | + */ |
| 91 | + public function testConflictBetweenAwaitAndExecute() |
| 92 | + { |
| 93 | + // websocket messages |
| 94 | + $responses = []; |
| 95 | + $addResponse = function($payload) use(&$responses) { |
| 96 | + $payloadStub = $this->getMockBuilder(Payload::class) |
| 97 | + ->disableOriginalConstructor() |
| 98 | + ->getMock(); |
| 99 | + $payloadStub->method('getPayload')->willReturn($payload); |
| 100 | + $responses[] = $payloadStub; |
| 101 | + }; |
| 102 | + |
| 103 | + // create a stub websocket client, which returns predefined messages |
| 104 | + $wsClient = $this->getMockBuilder(WebSocketClient::class) |
| 105 | + ->disableOriginalConstructor() |
| 106 | + ->getMock(); |
| 107 | + $wsClient->method('receive')->willReturnCallback(function() use(&$responses) { |
| 108 | + if (!empty($responses)) { |
| 109 | + return [array_shift($responses)]; |
| 110 | + } |
| 111 | + return null; |
| 112 | + }); |
| 113 | + $wsClient->method('setDeadline')->willReturnCallback(function($deadline) { |
| 114 | + $timeout = floatval($deadline->format("U.u")) - microtime(true); |
| 115 | + if ($timeout < 0.0) { |
| 116 | + throw new DeadlineException("Socket deadline reached."); |
| 117 | + } |
| 118 | + }); |
| 119 | + |
| 120 | + // create the failing scenario |
| 121 | + $ctx = Context::withTimeout(Context::background(), 3); |
| 122 | + |
| 123 | + $client = new DevtoolsClient($wsClient); |
| 124 | + register_shutdown_function(function () use ($client) { $client->close(); }); |
| 125 | + |
| 126 | + $addResponse('{"id":1,"result":{}}'); |
| 127 | + $client->page()->enable($ctx); |
| 128 | + |
| 129 | + $addResponse('{"id":2,"result":{}}'); |
| 130 | + $client->network()->enable($ctx, EnableRequest::make()); |
| 131 | + |
| 132 | + $client->network()->addLoadingFinishedListener(function (LoadingFinishedEvent $event) use($ctx, $client, $addResponse) { // <- 2 |
| 133 | + |
| 134 | + // The order of these responses matters, if the loadEventFired comes first, awaitLoadEventFired (1) would wait forever |
| 135 | + $addResponse('{"method":"Page.loadEventFired","params":{"timestamp":6758.846787}}'); |
| 136 | + $addResponse('{"id":4,"result":{"body":"..."}}'); |
| 137 | + |
| 138 | + $client->network()->getResponseBody($ctx, GetResponseBodyRequest::builder() // <- 3 |
| 139 | + ->setRequestId($event->requestId) |
| 140 | + ->build() |
| 141 | + ); |
| 142 | + }); |
| 143 | + |
| 144 | + $url = 'https://www.google.com'; |
| 145 | + |
| 146 | + $addResponse('{"id":3,"result":{"frameId":"1E56ACDD9B3B7F678F972C0EF0782649","loaderId":"AAF889CAE5B10663CA8D383A6125AC1B"}}'); |
| 147 | + $client->page()->navigate($ctx, NavigateRequest::builder()->setUrl($url)->build()); |
| 148 | + |
| 149 | + $addResponse('{"method":"Network.loadingFinished","params":{"requestId":"AAF889CAE5B10663CA8D383A6125AC1B","timestamp":6758.623335,"encodedDataLength":67174,"shouldReportCorbBlocking":false}}'); |
| 150 | + $client->page()->awaitLoadEventFired($ctx); // <- 1 |
| 151 | + |
| 152 | + $this->assertTrue((bool)'No conflict'); |
| 153 | + |
| 154 | + /* |
| 155 | +
|
| 156 | + 1) we are waiting for the LoadEvent, but in the meanwhile the LoadingFinishedEvent arrives -> |
| 157 | + 2) so the listener is called |
| 158 | + 3) getResponseBody command is executed |
| 159 | + - if the response is received before LoadEvent, everything is fine |
| 160 | + - but if the LoadEvent is happening before the response is received, |
| 161 | + the LoadEvent is dropped (not buffered) and awaitLoadEventFired could never return |
| 162 | +
|
| 163 | + */ |
| 164 | + } |
| 165 | + |
| 166 | + |
83 | 167 | }
|
0 commit comments