Issue #2469713 by dawehner, pfrenssen, elachlan, Mixologic, larowlan, alexpott, hussainweb: Step 2: Create a JavaScriptTestBase using PhantomJs Driver/Binary
parent
eeab24a3ce
commit
e6f491d8f9
|
@ -620,9 +620,9 @@ function drupal_valid_test_ua($new_prefix = NULL) {
|
|||
// string.
|
||||
$http_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL;
|
||||
$user_agent = isset($_COOKIE['SIMPLETEST_USER_AGENT']) ? $_COOKIE['SIMPLETEST_USER_AGENT'] : $http_user_agent;
|
||||
if (isset($user_agent) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $user_agent, $matches)) {
|
||||
if (isset($user_agent) && preg_match("/^(simpletest\d+):(.+):(.+):(.+)$/", $user_agent, $matches)) {
|
||||
list(, $prefix, $time, $salt, $hmac) = $matches;
|
||||
$check_string = $prefix . ';' . $time . ';' . $salt;
|
||||
$check_string = $prefix . ':' . $time . ':' . $salt;
|
||||
// Read the hash salt prepared by drupal_generate_test_ua().
|
||||
// This function is called before settings.php is read and Drupal's error
|
||||
// handlers are set up. While Drupal's error handling may be properly
|
||||
|
@ -679,8 +679,8 @@ function drupal_generate_test_ua($prefix) {
|
|||
}
|
||||
// Generate a moderately secure HMAC based on the database credentials.
|
||||
$salt = uniqid('', TRUE);
|
||||
$check_string = $prefix . ';' . time() . ';' . $salt;
|
||||
return $check_string . ';' . Crypt::hmacBase64($check_string, $key);
|
||||
$check_string = $prefix . ':' . time() . ':' . $salt;
|
||||
return $check_string . ':' . Crypt::hmacBase64($check_string, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,6 +27,7 @@ use Drupal\Core\Url;
|
|||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\UserInterface;
|
||||
use Symfony\Component\CssSelector\CssSelector;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
|
@ -225,6 +226,13 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $preserveGlobalState = FALSE;
|
||||
|
||||
/**
|
||||
* The base URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl;
|
||||
|
||||
/**
|
||||
* Initializes Mink sessions.
|
||||
*/
|
||||
|
@ -241,6 +249,17 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$this->mink->setDefaultSessionName('default');
|
||||
$this->registerSessions();
|
||||
|
||||
// According to the W3C WebDriver specification a cookie can only be set if
|
||||
// the cookie domain is equal to the domain of the active document. When the
|
||||
// browser starts up the active document is not our domain but 'about:blank'
|
||||
// or similar. To be able to set our User-Agent and Xdebug cookies at the
|
||||
// start of the test we now do a request to the front page so the active
|
||||
// document matches the domain.
|
||||
// @see https://w3c.github.io/webdriver/webdriver-spec.html#add-cookie
|
||||
// @see https://www.w3.org/Bugs/Public/show_bug.cgi?id=20975
|
||||
$session = $this->getSession();
|
||||
$session->visit($this->baseUrl);
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
|
@ -271,7 +290,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
|
||||
if (is_array($this->minkDefaultDriverArgs)) {
|
||||
// Use ReflectionClass to instantiate class with received params.
|
||||
$reflector = new ReflectionClass($this->minkDefaultDriverClass);
|
||||
$reflector = new \ReflectionClass($this->minkDefaultDriverClass);
|
||||
$driver = $reflector->newInstanceArgs($this->minkDefaultDriverArgs);
|
||||
}
|
||||
else {
|
||||
|
@ -317,6 +336,8 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$path = isset($parsed_url['path']) ? rtrim(rtrim($parsed_url['path']), '/') : '';
|
||||
$port = isset($parsed_url['port']) ? $parsed_url['port'] : 80;
|
||||
|
||||
$this->baseUrl = $base_url;
|
||||
|
||||
// If the passed URL schema is 'https' then setup the $_SERVER variables
|
||||
// properly so that testing will run under HTTPS.
|
||||
if ($parsed_url['scheme'] === 'https') {
|
||||
|
@ -1335,6 +1356,40 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
return $logged_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is present.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementPresent($css_selector, $message = '') {
|
||||
$this->assertNotEmpty($this->getSession()->getDriver()->find(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is not present.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementNotPresent($css_selector, $message = '') {
|
||||
$this->assertEmpty($this->getSession()->getDriver()->find(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the element with the given CSS selector.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to click.
|
||||
*/
|
||||
protected function click($css_selector) {
|
||||
$this->getSession()->getDriver()->click(CssSelector::toXPath($css_selector));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents serializing any properties.
|
||||
*
|
||||
|
|
|
@ -123,6 +123,7 @@ class TestDiscovery {
|
|||
$this->testNamespaces["Drupal\\Tests\\$name\\Unit\\"][] = "$base_path/tests/src/Unit";
|
||||
$this->testNamespaces["Drupal\\Tests\\$name\\Kernel\\"][] = "$base_path/tests/src/Kernel";
|
||||
$this->testNamespaces["Drupal\\Tests\\$name\\Functional\\"][] = "$base_path/tests/src/Functional";
|
||||
$this->testNamespaces["Drupal\\Tests\\$name\\FunctionalJavascript\\"][] = "$base_path/tests/src/FunctionalJavascript";
|
||||
}
|
||||
|
||||
foreach ($this->testNamespaces as $prefix => $paths) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\simpletest\FunctionalJavascript\BrowserWithJavascriptTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\simpletest\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Tests if we can execute JavaScript in the browser.
|
||||
*
|
||||
* @group javascript
|
||||
*/
|
||||
class BrowserWithJavascriptTest extends JavascriptTestBase {
|
||||
|
||||
public function testJavascript() {
|
||||
$this->drupalGet('<front>');
|
||||
$session = $this->getSession();
|
||||
|
||||
$session->resizeWindow(400, 300);
|
||||
$javascript = <<<JS
|
||||
(function(){
|
||||
var w = window,
|
||||
d = document,
|
||||
e = d.documentElement,
|
||||
g = d.getElementsByTagName('body')[0],
|
||||
x = w.innerWidth || e.clientWidth || g.clientWidth,
|
||||
y = w.innerHeight || e.clientHeight|| g.clientHeight;
|
||||
return x == 400 && y == 300;
|
||||
}());
|
||||
JS;
|
||||
$result = $session->wait(1000, $javascript);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\toolbar\FunctionalJavascript\ToolbarIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\toolbar\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Tests the JavaScript functionality of the toolbar.
|
||||
*
|
||||
* @group toolbar
|
||||
*/
|
||||
class ToolbarIntegrationTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['toolbar', 'node'];
|
||||
|
||||
/**
|
||||
* Tests if the toolbar can be toggled with JavaScript.
|
||||
*/
|
||||
public function testToolbarToggling() {
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access toolbar',
|
||||
'administer site configuration',
|
||||
'access content overview'
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
// Test that it is possible to toggle the toolbar tray.
|
||||
$this->assertElementVisible('#toolbar-link-system-admin_content', 'Toolbar tray is open by default.');
|
||||
$this->click('#toolbar-item-administration');
|
||||
$this->assertElementNotVisible('#toolbar-link-system-admin_content', 'Toolbar tray is closed after clicking the "Manage" button.');
|
||||
$this->click('#toolbar-item-administration');
|
||||
$this->assertElementVisible('#toolbar-link-system-admin_content', 'Toolbar tray is visible again after clicking the "Manage" button a second time.');
|
||||
|
||||
// Test toggling the toolbar tray between horizontal and vertical.
|
||||
$this->assertElementVisible('#toolbar-item-administration-tray.toolbar-tray-horizontal', 'Toolbar tray is horizontally oriented by default.');
|
||||
$this->assertElementNotPresent('#toolbar-item-administration-tray.toolbar-tray-vertical', 'Toolbar tray is not vertically oriented by default.');
|
||||
|
||||
$this->click('#toolbar-item-administration-tray button.toolbar-icon-toggle-vertical');
|
||||
$this->assertTrue($this->wait(1000, 'jQuery("#toolbar-item-administration-tray").hasClass("toolbar-tray-vertical")'));
|
||||
$this->assertElementVisible('#toolbar-item-administration-tray.toolbar-tray-vertical', 'After toggling the orientation the toolbar tray is now displayed vertically.');
|
||||
|
||||
$this->click('#toolbar-item-administration-tray button.toolbar-icon-toggle-horizontal');
|
||||
$this->assertTrue($this->wait(1000, 'jQuery("#toolbar-item-administration-tray").hasClass("toolbar-tray-horizontal")'));
|
||||
$this->assertElementVisible('#toolbar-item-administration-tray.toolbar-tray-horizontal', 'After toggling the orientation a second time the toolbar tray is displayed horizontally again.');
|
||||
}
|
||||
|
||||
}
|
|
@ -50,6 +50,17 @@
|
|||
<!-- Exclude Drush tests. -->
|
||||
<exclude>./drush/tests</exclude>
|
||||
</testsuite>
|
||||
<testsuite name="functional-javascript">
|
||||
<directory>./tests/Drupal/FunctionalJavascriptTests</directory>
|
||||
<directory>./modules/*/tests/src/FunctionalJavascript</directory>
|
||||
<directory>../modules/*/tests/src/FunctionalJavascript</directory>
|
||||
<directory>../profiles/*/tests/src/FunctionalJavascript</directory>
|
||||
<directory>../sites/*/modules/*/tests/src/FunctionalJavascript</directory>
|
||||
<!-- Exclude Composer's vendor directory so we don't run tests there. -->
|
||||
<exclude>./vendor</exclude>
|
||||
<!-- Exclude Drush tests. -->
|
||||
<exclude>./drush/tests</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<listeners>
|
||||
<listener class="\Drupal\Tests\Listeners\DrupalStandardsListener">
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\FunctionalJavascriptTests\JavascriptTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\FunctionalJavascriptTests;
|
||||
|
||||
use Drupal\simpletest\BrowserTestBase;
|
||||
use Symfony\Component\CssSelector\CssSelector;
|
||||
use Zumba\Mink\Driver\PhantomJSDriver;
|
||||
|
||||
/**
|
||||
* Runs a browser test using PhantomJS.
|
||||
*
|
||||
* Base class for testing browser interaction implemented in JavaScript.
|
||||
*/
|
||||
abstract class JavascriptTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $minkDefaultDriverClass = PhantomJSDriver::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initMink() {
|
||||
// Set up the template cache used by the PhantomJS mink driver.
|
||||
$path = $this->tempFilesDirectory . DIRECTORY_SEPARATOR . 'browsertestbase-templatecache';
|
||||
$this->minkDefaultDriverArgs = [
|
||||
'http://127.0.0.1:8510',
|
||||
$path,
|
||||
];
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
parent::initMink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is visible.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementVisible($css_selector, $message = '') {
|
||||
$this->assertTrue($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is not visible.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementNotVisible($css_selector, $message = '') {
|
||||
$this->assertFalse($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given time or until the given JS condition becomes TRUE.
|
||||
*
|
||||
* @param int $timeout
|
||||
* Timeout in milliseconds.
|
||||
* @param string|bool $condition
|
||||
* JS condition, or FALSE to wait for the full duration of the timeout.
|
||||
*
|
||||
* @return bool
|
||||
* The result of the JS condition.
|
||||
*/
|
||||
protected function wait($timeout, $condition = FALSE) {
|
||||
return $this->getSession()->getDriver()->wait($timeout, $condition);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Running tests
|
||||
|
||||
## Functional tests
|
||||
|
||||
* Start PhantomJS:
|
||||
```
|
||||
phantomjs --ssl-protocol=any --ignore-ssl-errors=true ./vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768 2>&1 >> /dev/null &
|
||||
```
|
||||
* Run the functional tests:
|
||||
```
|
||||
export SIMPLETEST_DB='mysql://root@localhost/dev_d8'
|
||||
export SIMPLETEST_BASE_URL='http://d8.dev'
|
||||
./vendor/bin/phpunit -c core --testsuite functional
|
||||
./vendor/bin/phpunit -c core --testsuite functional-javascript
|
||||
```
|
|
@ -104,6 +104,8 @@ function drupal_phpunit_populate_class_loader() {
|
|||
// Start with classes in known locations.
|
||||
$loader->add('Drupal\\Tests', __DIR__);
|
||||
$loader->add('Drupal\\KernelTests', __DIR__);
|
||||
$loader->add('Drupal\\FunctionalTests', __DIR__);
|
||||
$loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
|
||||
|
||||
if (!isset($GLOBALS['namespaces'])) {
|
||||
// Scan for arbitrary extension namespaces from core and contrib.
|
||||
|
|
Loading…
Reference in New Issue