JavascriptTestBase.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. namespace Drupal\FunctionalJavascriptTests;
  3. use Drupal\Tests\BrowserTestBase;
  4. use Zumba\GastonJS\Exception\DeadClient;
  5. use Zumba\Mink\Driver\PhantomJSDriver;
  6. /**
  7. * Runs a browser test using a driver that supports Javascript.
  8. *
  9. * Base class for testing browser interaction implemented in JavaScript.
  10. */
  11. abstract class JavascriptTestBase extends BrowserTestBase {
  12. /**
  13. * {@inheritdoc}
  14. *
  15. * To use a webdriver based approach, please use DrupalSelenium2Driver::class.
  16. * We will switch the default later.
  17. */
  18. protected $minkDefaultDriverClass = PhantomJSDriver::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 (\Exception $e) {
  44. $this->markTestSkipped('An unexpected error occurred while starting Mink: ' . $e->getMessage());
  45. }
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. protected function tearDown() {
  51. if ($this->mink) {
  52. // Wait for all requests to finish. It is possible that an AJAX request is
  53. // still on-going.
  54. $result = $this->getSession()->wait(5000, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
  55. if (!$result) {
  56. // If the wait is unsuccessful, there may still be an AJAX request in
  57. // progress. If we tear down now, then this AJAX request may fail with
  58. // missing database tables, because tear down will have removed them.
  59. // Rather than allow it to fail, throw an explicit exception now
  60. // explaining what the problem is.
  61. throw new \RuntimeException('Unfinished AJAX requests while tearing down a test');
  62. }
  63. }
  64. parent::tearDown();
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. protected function getMinkDriverArgs() {
  70. if ($this->minkDefaultDriverClass === DrupalSelenium2Driver::class) {
  71. return getenv('MINK_DRIVER_ARGS_WEBDRIVER') ?: getenv('MINK_DRIVER_ARGS_PHANTOMJS') ?: parent::getMinkDriverArgs();
  72. }
  73. elseif ($this->minkDefaultDriverClass === PhantomJSDriver::class) {
  74. return getenv('MINK_DRIVER_ARGS_PHANTOMJS') ?: parent::getMinkDriverArgs();
  75. }
  76. return parent::getMinkDriverArgs();
  77. }
  78. /**
  79. * Asserts that the element with the given CSS selector is visible.
  80. *
  81. * @param string $css_selector
  82. * The CSS selector identifying the element to check.
  83. * @param string $message
  84. * Optional message to show alongside the assertion.
  85. *
  86. * @deprecated in Drupal 8.1.x, will be removed before Drupal 8.3.x. Use
  87. * \Behat\Mink\Element\NodeElement::isVisible() instead.
  88. */
  89. protected function assertElementVisible($css_selector, $message = '') {
  90. $this->assertTrue($this->getSession()->getDriver()->isVisible($this->cssSelectToXpath($css_selector)), $message);
  91. }
  92. /**
  93. * Asserts that the element with the given CSS selector is not visible.
  94. *
  95. * @param string $css_selector
  96. * The CSS selector identifying the element to check.
  97. * @param string $message
  98. * Optional message to show alongside the assertion.
  99. *
  100. * @deprecated in Drupal 8.1.x, will be removed before Drupal 8.3.x. Use
  101. * \Behat\Mink\Element\NodeElement::isVisible() instead.
  102. */
  103. protected function assertElementNotVisible($css_selector, $message = '') {
  104. $this->assertFalse($this->getSession()->getDriver()->isVisible($this->cssSelectToXpath($css_selector)), $message);
  105. }
  106. /**
  107. * Waits for the given time or until the given JS condition becomes TRUE.
  108. *
  109. * @param string $condition
  110. * JS condition to wait until it becomes TRUE.
  111. * @param int $timeout
  112. * (Optional) Timeout in milliseconds, defaults to 10000.
  113. * @param string $message
  114. * (optional) A message to display with the assertion. If left blank, a
  115. * default message will be displayed.
  116. *
  117. * @throws \PHPUnit_Framework_AssertionFailedError
  118. *
  119. * @see \Behat\Mink\Driver\DriverInterface::evaluateScript()
  120. */
  121. protected function assertJsCondition($condition, $timeout = 10000, $message = '') {
  122. $message = $message ?: "Javascript condition met:\n" . $condition;
  123. $result = $this->getSession()->getDriver()->wait($timeout, $condition);
  124. $this->assertTrue($result, $message);
  125. }
  126. /**
  127. * Creates a screenshot.
  128. *
  129. * @param string $filename
  130. * The file name of the resulting screenshot. If using the default phantomjs
  131. * driver then this should be a JPG filename.
  132. * @param bool $set_background_color
  133. * (optional) By default this method will set the background color to white.
  134. * Set to FALSE to override this behaviour.
  135. *
  136. * @throws \Behat\Mink\Exception\UnsupportedDriverActionException
  137. * When operation not supported by the driver.
  138. * @throws \Behat\Mink\Exception\DriverException
  139. * When the operation cannot be done.
  140. */
  141. protected function createScreenshot($filename, $set_background_color = TRUE) {
  142. $session = $this->getSession();
  143. if ($set_background_color) {
  144. $session->executeScript("document.body.style.backgroundColor = 'white';");
  145. }
  146. $image = $session->getScreenshot();
  147. file_put_contents($filename, $image);
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function assertSession($name = NULL) {
  153. return new WebDriverWebAssert($this->getSession($name), $this->baseUrl);
  154. }
  155. /**
  156. * Gets the current Drupal javascript settings and parses into an array.
  157. *
  158. * Unlike BrowserTestBase::getDrupalSettings(), this implementation reads the
  159. * current values of drupalSettings, capturing all changes made via javascript
  160. * after the page was loaded.
  161. *
  162. * @return array
  163. * The Drupal javascript settings array.
  164. *
  165. * @see \Drupal\Tests\BrowserTestBase::getDrupalSettings()
  166. */
  167. protected function getDrupalSettings() {
  168. $script = <<<EndOfScript
  169. (function () {
  170. if (typeof drupalSettings !== 'undefined') {
  171. return drupalSettings;
  172. }
  173. })();
  174. EndOfScript;
  175. return $this->getSession()->evaluateScript($script) ?: [];
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. protected function getHtmlOutputHeaders() {
  181. // The webdriver API does not support fetching headers.
  182. return '';
  183. }
  184. }