PuphpeteerTest.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. namespace Nesk\Puphpeteer\Tests;
  3. use Nesk\Puphpeteer\Puppeteer;
  4. use Nesk\Rialto\Data\JsFunction;
  5. use PHPUnit\Framework\ExpectationFailedException;
  6. use Nesk\Puphpeteer\Resources\ElementHandle;
  7. use Nesk\Rialto\Data\BasicResource;
  8. use Psr\Log\LoggerInterface;
  9. class PuphpeteerTest extends TestCase
  10. {
  11. public function setUp(): void
  12. {
  13. parent::setUp();
  14. // Serve the content of the "/resources"-folder to test these.
  15. $this->serveResources();
  16. // Launch the browser to run tests on.
  17. $this->launchBrowser();
  18. }
  19. /** @test */
  20. public function can_browse_website()
  21. {
  22. $response = $this->browser->newPage()->goto($this->url);
  23. $this->assertTrue($response->ok(), 'Failed asserting that the response is successful.');
  24. }
  25. /**
  26. * @test
  27. */
  28. public function can_use_method_aliases()
  29. {
  30. $page = $this->browser->newPage();
  31. $page->goto($this->url);
  32. $select = function($resource) {
  33. $elements = [
  34. $resource->querySelector('h1'),
  35. $resource->querySelectorAll('h1')[0],
  36. $resource->querySelectorXPath('/html/body/h1')[0],
  37. ];
  38. $this->assertContainsOnlyInstancesOf(ElementHandle::class, $elements);
  39. };
  40. $evaluate = function($resource) {
  41. $strings = [
  42. $resource->querySelectorEval('h1', JsFunction::createWithBody('return "Hello World!";')),
  43. $resource->querySelectorAllEval('h1', JsFunction::createWithBody('return "Hello World!";')),
  44. ];
  45. foreach ($strings as $string) {
  46. $this->assertEquals('Hello World!', $string);
  47. }
  48. };
  49. // Test method aliases for Page, Frame and ElementHandle classes
  50. $resources = [$page, $page->mainFrame(), $page->querySelector('body')];
  51. foreach ($resources as $resource) {
  52. $select($resource);
  53. $evaluate($resource);
  54. }
  55. }
  56. /** @test */
  57. public function can_evaluate_a_selection()
  58. {
  59. $page = $this->browser->newPage();
  60. $page->goto($this->url);
  61. $title = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])
  62. ->body('return node.textContent;'));
  63. $titleCount = $page->querySelectorAllEval('h1', JsFunction::createWithParameters(['nodes'])
  64. ->body('return nodes.length;'));
  65. $this->assertEquals('Example Page', $title);
  66. $this->assertEquals(1, $titleCount);
  67. }
  68. /** @test */
  69. public function can_intercept_requests()
  70. {
  71. $page = $this->browser->newPage();
  72. $page->setRequestInterception(true);
  73. $page->on('request', JsFunction::createWithParameters(['request'])
  74. ->body('request.resourceType() === "stylesheet" ? request.abort() : request.continue()'));
  75. $page->goto($this->url);
  76. $backgroundColor = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])
  77. ->body('return getComputedStyle(node).textTransform'));
  78. $this->assertNotEquals('lowercase', $backgroundColor);
  79. }
  80. /**
  81. * @test
  82. * @dataProvider resourceProvider
  83. * @dontPopulateProperties browser
  84. */
  85. public function check_all_resources_are_supported(string $name)
  86. {
  87. $incompleteTest = false;
  88. $resourceInstantiator = new ResourceInstantiator($this->browserOptions, $this->url);
  89. $resource = $resourceInstantiator->{$name}(new Puppeteer, $this->browserOptions);
  90. if ($resource instanceof UntestableResource) {
  91. $incompleteTest = true;
  92. } else if ($resource instanceof RiskyResource) {
  93. if (!empty($resource->exception())) {
  94. $incompleteTest = true;
  95. } else {
  96. try {
  97. $this->assertInstanceOf("Nesk\\Puphpeteer\\Resources\\$name", $resource->value());
  98. } catch (ExpectationFailedException $exception) {
  99. $incompleteTest = true;
  100. }
  101. }
  102. } else {
  103. $this->assertInstanceOf("Nesk\\Puphpeteer\\Resources\\$name", $resource);
  104. }
  105. if (!$incompleteTest) return;
  106. $reason = "The \"$name\" resource has not been tested properly, probably"
  107. ." for a good reason but you might want to have a look: \n\n ";
  108. if ($resource instanceof UntestableResource) {
  109. $reason .= "\e[33mMarked as untestable.\e[0m";
  110. } else {
  111. if (!empty($exception = $resource->exception())) {
  112. $reason .= "\e[31mMarked as risky because of a Node error: {$exception->getMessage()}\e[0m";
  113. } else {
  114. $value = print_r($resource->value(), true);
  115. $reason .= "\e[31mMarked as risky because of an unexpected value: $value\e[0m";
  116. }
  117. }
  118. $this->markTestIncomplete($reason);
  119. }
  120. public function resourceProvider(): \Generator
  121. {
  122. $resourceNames = (new ResourceInstantiator([], ''))->getResourceNames();
  123. foreach ($resourceNames as $name) {
  124. yield [$name];
  125. }
  126. }
  127. private function createBrowserLogger(callable $onBrowserLog): LoggerInterface
  128. {
  129. $logger = $this->createMock(LoggerInterface::class);
  130. $logger->expects(self::atLeastOnce())
  131. ->method('log')
  132. ->willReturnCallback(function (string $level, string $message) use ($onBrowserLog) {
  133. if (\strpos($message, "Received a Browser log:") === 0) {
  134. $onBrowserLog();
  135. }
  136. return null;
  137. });
  138. return $logger;
  139. }
  140. /**
  141. * @test
  142. * @dontPopulateProperties browser
  143. */
  144. public function browser_console_calls_are_logged_if_enabled()
  145. {
  146. $browserLogOccured = false;
  147. $logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {
  148. $browserLogOccured = true;
  149. });
  150. $puppeteer = new Puppeteer([
  151. 'log_browser_console' => true,
  152. 'logger' => $logger,
  153. ]);
  154. $this->browser = $puppeteer->launch($this->browserOptions);
  155. $this->browser->pages()[0]->goto($this->url);
  156. static::assertTrue($browserLogOccured);
  157. }
  158. /**
  159. * @test
  160. * @dontPopulateProperties browser
  161. */
  162. public function browser_console_calls_are_not_logged_if_disabled()
  163. {
  164. $browserLogOccured = false;
  165. $logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {
  166. $browserLogOccured = true;
  167. });
  168. $puppeteer = new Puppeteer([
  169. 'log_browser_console' => false,
  170. 'logger' => $logger,
  171. ]);
  172. $this->browser = $puppeteer->launch($this->browserOptions);
  173. $this->browser->pages()[0]->goto($this->url);
  174. static::assertFalse($browserLogOccured);
  175. }
  176. }