Commit ca48ed6d authored by alexpott's avatar alexpott

Issue #2469713 by dawehner, pfrenssen, alexpott, elachlan, Mixologic,...

Issue #2469713 by dawehner, pfrenssen, alexpott, elachlan, Mixologic, larowlan, hussainweb, benjy, jibran, Wim Leers, isntall: Step 2: Create a JavaScriptTestBase using PhantomJs Driver/Binary
parent d10dea12
......@@ -621,9 +621,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
......@@ -680,8 +680,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\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Symfony\Component\CssSelector\CssSelector;
use Symfony\Component\HttpFoundation\Request;
/**
......@@ -225,8 +226,18 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
*/
protected $preserveGlobalState = FALSE;
/**
* The base URL.
*
* @var string
*/
protected $baseUrl;
/**
* Initializes Mink sessions.
*
* @return \Behat\Mink\Session
* The mink session.
*/
protected function initMink() {
$driver = $this->getDefaultDriverInstance();
......@@ -241,6 +252,17 @@ protected function initMink() {
$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 +293,7 @@ protected function getDefaultDriverInstance() {
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 +339,8 @@ protected function setUp() {
$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 +1359,40 @@ protected function drupalUserIsLoggedIn(UserInterface $account) {
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 @@ public function registerTestNamespaces() {
$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) {
......
<?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;
$this->assertJsCondition($javascript);
}
public function testAssertJsCondition() {
$this->drupalGet('<front>');
$session = $this->getSession();
$session->resizeWindow(500, 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;
// We expected the following assertion to fail because the window has been
// re-sized to have a width of 500 not 400.
$this->setExpectedException(\PHPUnit_Framework_AssertionFailedError::class);
$this->assertJsCondition($javascript, 100);
}
}
<?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->assertJsCondition('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->assertJsCondition('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">
......
<?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);
}
return 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 string $condition
* JS condition to wait until it becomes TRUE.
* @param int $timeout
* (Optional) Timeout in milliseconds, defaults to 1000.
* @param string $message
* (optional) A message to display with the assertion. If left blank, a
* default message will be displayed.
*
* @throws \PHPUnit_Framework_AssertionFailedError
*
* @see \Behat\Mink\Driver\DriverInterface::evaluateScript()
*/
protected function assertJsCondition($condition, $timeout = 1000, $message = '') {
$message = $message ?: "Javascript condition met:\n" . $condition;
$result = $this->getSession()->getDriver()->wait($timeout, $condition);
$this->assertTrue($result, $message);
}
}
# 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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment