WebDriverTestBase.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <?php
  2. namespace Drupal\FunctionalJavascriptTests;
  3. use Behat\Mink\Exception\DriverException;
  4. use Drupal\Tests\BrowserTestBase;
  5. use Zumba\GastonJS\Exception\DeadClient;
  6. use Zumba\Mink\Driver\PhantomJSDriver;
  7. /**
  8. * Runs a browser test using a driver that supports Javascript.
  9. *
  10. * Base class for testing browser interaction implemented in JavaScript.
  11. */
  12. abstract class WebDriverTestBase extends BrowserTestBase {
  13. /**
  14. * {@inheritdoc}
  15. *
  16. * To use a legacy phantomjs based approach, please use PhantomJSDriver::class.
  17. */
  18. protected $minkDefaultDriverClass = DrupalSelenium2Driver::class;
  19. /**
  20. * {@inheritdoc}
  21. */
  22. protected function initMink() {
  23. if ($this->minkDefaultDriverClass === DrupalSelenium2Driver::class) {
  24. $this->minkDefaultDriverArgs = ['chrome', NULL, 'http://localhost:4444'];
  25. }
  26. elseif ($this->minkDefaultDriverClass === PhantomJSDriver::class) {
  27. // Set up the template cache used by the PhantomJS mink driver.
  28. $path = $this->tempFilesDirectory . DIRECTORY_SEPARATOR . 'browsertestbase-templatecache';
  29. $this->minkDefaultDriverArgs = [
  30. 'http://127.0.0.1:8510',
  31. $path,
  32. ];
  33. if (!file_exists($path)) {
  34. mkdir($path);
  35. }
  36. }
  37. try {
  38. return parent::initMink();
  39. }
  40. catch (DeadClient $e) {
  41. $this->markTestSkipped('PhantomJS is either not installed or not running. Start it via phantomjs --ssl-protocol=any --ignore-ssl-errors=true vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768&');
  42. }
  43. catch (DriverException $e) {
  44. if ($this->minkDefaultDriverClass === DrupalSelenium2Driver::class) {
  45. $this->markTestSkipped("The test wasn't able to connect to your webdriver instance. For more information read core/tests/README.md.\n\nThe original message while starting Mink: {$e->getMessage()}");
  46. }
  47. else {
  48. throw $e;
  49. }
  50. }
  51. catch (\Exception $e) {
  52. $this->markTestSkipped('An unexpected error occurred while starting Mink: ' . $e->getMessage());
  53. }
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. protected function tearDown() {
  59. if ($this->mink) {
  60. // Wait for all requests to finish. It is possible that an AJAX request is
  61. // still on-going.
  62. $result = $this->getSession()->wait(5000, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
  63. if (!$result) {
  64. // If the wait is unsuccessful, there may still be an AJAX request in
  65. // progress. If we tear down now, then this AJAX request may fail with
  66. // missing database tables, because tear down will have removed them.
  67. // Rather than allow it to fail, throw an explicit exception now
  68. // explaining what the problem is.
  69. throw new \RuntimeException('Unfinished AJAX requests while tearing down a test');
  70. }
  71. }
  72. parent::tearDown();
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. protected function getMinkDriverArgs() {
  78. if ($this->minkDefaultDriverClass === DrupalSelenium2Driver::class) {
  79. return getenv('MINK_DRIVER_ARGS_WEBDRIVER') ?: getenv('MINK_DRIVER_ARGS_PHANTOMJS') ?: parent::getMinkDriverArgs();
  80. }
  81. elseif ($this->minkDefaultDriverClass === PhantomJSDriver::class) {
  82. return getenv('MINK_DRIVER_ARGS_PHANTOMJS') ?: parent::getMinkDriverArgs();
  83. }
  84. return parent::getMinkDriverArgs();
  85. }
  86. /**
  87. * Asserts that the element with the given CSS selector is visible.
  88. *
  89. * @param string $css_selector
  90. * The CSS selector identifying the element to check.
  91. * @param string $message
  92. * Optional message to show alongside the assertion.
  93. *
  94. * @deprecated in Drupal 8.1.0, will be removed before Drupal 9.0.0. Use
  95. * \Behat\Mink\Element\NodeElement::isVisible() instead.
  96. */
  97. protected function assertElementVisible($css_selector, $message = '') {
  98. $this->assertTrue($this->getSession()->getDriver()->isVisible($this->cssSelectToXpath($css_selector)), $message);
  99. @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 8.1.0 and will be removed in 9.0.0. Use \Behat\Mink\Element\NodeElement::isVisible() instead.', E_USER_DEPRECATED);
  100. }
  101. /**
  102. * Asserts that the element with the given CSS selector is not visible.
  103. *
  104. * @param string $css_selector
  105. * The CSS selector identifying the element to check.
  106. * @param string $message
  107. * Optional message to show alongside the assertion.
  108. *
  109. * @deprecated in Drupal 8.1.0, will be removed before Drupal 9.0.0. Use
  110. * \Behat\Mink\Element\NodeElement::isVisible() instead.
  111. */
  112. protected function assertElementNotVisible($css_selector, $message = '') {
  113. $this->assertFalse($this->getSession()->getDriver()->isVisible($this->cssSelectToXpath($css_selector)), $message);
  114. @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 8.1.0 and will be removed in 9.0.0. Use \Behat\Mink\Element\NodeElement::isVisible() instead.', E_USER_DEPRECATED);
  115. }
  116. /**
  117. * Waits for the given time or until the given JS condition becomes TRUE.
  118. *
  119. * @param string $condition
  120. * JS condition to wait until it becomes TRUE.
  121. * @param int $timeout
  122. * (Optional) Timeout in milliseconds, defaults to 10000.
  123. * @param string $message
  124. * (optional) A message to display with the assertion. If left blank, a
  125. * default message will be displayed.
  126. *
  127. * @throws \PHPUnit_Framework_AssertionFailedError
  128. *
  129. * @see \Behat\Mink\Driver\DriverInterface::evaluateScript()
  130. */
  131. protected function assertJsCondition($condition, $timeout = 10000, $message = '') {
  132. $message = $message ?: "Javascript condition met:\n" . $condition;
  133. $result = $this->getSession()->getDriver()->wait($timeout, $condition);
  134. $this->assertTrue($result, $message);
  135. }
  136. /**
  137. * Creates a screenshot.
  138. *
  139. * @param string $filename
  140. * The file name of the resulting screenshot. If using the default phantomjs
  141. * driver then this should be a JPG filename.
  142. * @param bool $set_background_color
  143. * (optional) By default this method will set the background color to white.
  144. * Set to FALSE to override this behaviour.
  145. *
  146. * @throws \Behat\Mink\Exception\UnsupportedDriverActionException
  147. * When operation not supported by the driver.
  148. * @throws \Behat\Mink\Exception\DriverException
  149. * When the operation cannot be done.
  150. */
  151. protected function createScreenshot($filename, $set_background_color = TRUE) {
  152. $session = $this->getSession();
  153. if ($set_background_color) {
  154. $session->executeScript("document.body.style.backgroundColor = 'white';");
  155. }
  156. $image = $session->getScreenshot();
  157. file_put_contents($filename, $image);
  158. }
  159. /**
  160. * {@inheritdoc}
  161. */
  162. public function assertSession($name = NULL) {
  163. return new WebDriverWebAssert($this->getSession($name), $this->baseUrl);
  164. }
  165. /**
  166. * Gets the current Drupal javascript settings and parses into an array.
  167. *
  168. * Unlike BrowserTestBase::getDrupalSettings(), this implementation reads the
  169. * current values of drupalSettings, capturing all changes made via javascript
  170. * after the page was loaded.
  171. *
  172. * @return array
  173. * The Drupal javascript settings array.
  174. *
  175. * @see \Drupal\Tests\BrowserTestBase::getDrupalSettings()
  176. */
  177. protected function getDrupalSettings() {
  178. $script = <<<EndOfScript
  179. (function () {
  180. if (typeof drupalSettings !== 'undefined') {
  181. return drupalSettings;
  182. }
  183. })();
  184. EndOfScript;
  185. return $this->getSession()->evaluateScript($script) ?: [];
  186. }
  187. /**
  188. * {@inheritdoc}
  189. */
  190. protected function getHtmlOutputHeaders() {
  191. // The webdriver API does not support fetching headers.
  192. return '';
  193. }
  194. }