Browse Source

Create javascript tests (#86)

* Introduce functional js testing
* Reorganize travis configuration
tags/3.3.0-beta1
Christian Fritsch GitHub 3 years ago
parent
commit
f2dc2bcf30
33 changed files with 1166 additions and 703 deletions
  1. +2
    -0
      .eslintignore
  2. +0
    -1
      .gitignore
  3. +22
    -49
      .travis.yml
  4. +1
    -4
      composer.json
  5. +1
    -1
      modules/thunder_media/js/media_entity_browser.view.js
  6. +9
    -9
      modules/thunder_media/js/thunder_gallery.js
  7. +39
    -0
      scripts/travis/before-install.sh
  8. +36
    -0
      scripts/travis/install-requirements.sh
  9. +42
    -24
      scripts/travis/install-thunder.sh
  10. +1
    -1
      scripts/travis/keep-travis-running.sh
  11. +7
    -0
      scripts/travis/run-tests.sh
  12. +37
    -0
      scripts/travis/setup-environment.sh
  13. +21
    -0
      scripts/travis/setup-tests.sh
  14. +0
    -10
      tests/behat/behat.local.yml.example
  15. +0
    -33
      tests/behat/behat.travis.yml
  16. +0
    -33
      tests/behat/behat.yml
  17. +0
    -38
      tests/behat/features/article/article.javascript.feature
  18. +0
    -39
      tests/behat/features/article/paragraphs/paragraph.media.feature
  19. +0
    -34
      tests/behat/features/article/paragraphs/paragraph.quote.feature
  20. +0
    -34
      tests/behat/features/article/paragraphs/paragraph.text.feature
  21. +0
    -393
      tests/behat/features/bootstrap/Thunder/behat/FeatureContext.php
  22. BIN
      tests/behat/files/thunder-main-1.png
  23. BIN
      tests/fixtures/reference.jpg
  24. +331
    -0
      tests/modules/thunder_test/js/jquery.simulate.js
  25. +6
    -0
      tests/modules/thunder_test/thunder_test.info.yml
  26. +7
    -0
      tests/modules/thunder_test/thunder_test.libraries.yml
  27. +13
    -0
      tests/modules/thunder_test/thunder_test.module
  28. +67
    -0
      tests/src/FunctionalJavascript/ArticleCreationTest.php
  29. +63
    -0
      tests/src/FunctionalJavascript/MediaGalleryModifyTest.php
  30. +60
    -0
      tests/src/FunctionalJavascript/MediaImageModifyTest.php
  31. +205
    -0
      tests/src/FunctionalJavascript/ThunderJavascriptTestBase.php
  32. +79
    -0
      tests/src/FunctionalJavascript/ThunderMediaTestTrait.php
  33. +117
    -0
      tests/src/FunctionalJavascript/ThunderParagraphsTestTrait.php

+ 2
- 0
.eslintignore View File

@@ -6,3 +6,5 @@ libraries/**/*
sites/**/libraries/**/*
profiles/**/libraries/**/*
**/js_test_files/**/*
docroot/**/*
tests/modules/thunder_test/js/jquery.simulate.js

+ 0
- 1
.gitignore View File

@@ -5,5 +5,4 @@
/libraries
/themes/infinite/
.DS_Store
tests/behat/behat.local.yml
composer.lock

+ 22
- 49
.travis.yml View File

@@ -5,6 +5,9 @@ cache:
- "$HOME/.composer/cache"
- "$HOME/.drush/cache"
- "vendor"
- "travis_phantomjs"
- "travis_selenium"

php:
- 5.6
- 7.0
@@ -28,58 +31,28 @@ mysql:
encoding: utf8

before_install:
- phpenv config-rm xdebug.ini
- composer self-update
- composer --version
# Clear drush release history cache, to pick up new releases.
- rm -f ~/.drush/cache/download/*---updates.drupal.org-release-history-*
- source ./scripts/travis/setup-environment.sh
- bash -x -e ./scripts/travis/before-install.sh

install:
# Set global environment variables
- export THUNDER_DIST_DIR=`echo $(pwd)`
- export TEST_DIR=`echo ${THUNDER_DIST_DIR}"/../test-dir"`
- bash -e ${THUNDER_DIST_DIR}/scripts/keep-travis-running.sh &

# add composer's global bin directory to the path
# see: https://github.com/drush-ops/drush#install---composer
- export PATH="$HOME/.composer/vendor/bin:$PATH"

# Install Drush
- composer global require drush/drush:~8 --prefer-source
- phpenv rehash
- drush dl drupalorg_drush-7.x
- drush verify-makefile

# MySQL Options
- mysql -e 'SET GLOBAL wait_timeout = 5400;'
- mysql -e "SHOW VARIABLES LIKE 'wait_timeout'"

# PHP conf tweaks
- echo 'max_execution_time = 120' >> drupal.php.ini;
- echo 'sendmail_path = /bin/true' >> drupal.php.ini;
- phpenv config-add drupal.php.ini
- phpenv rehash

- mkdir -p ${TEST_DIR}

# Install Test requirements and set global environment variables
- composer global require "drupal/drupal-extension:^3.2" "devinci/devinci-behat-extension:dev-master"
- BEHAT_PARAMS='{"extensions":{"Drupal\\DrupalExtension":{"drupal":{"drupal_root":"TEST_DIR_MACRO/docroot"}},"Behat\\MinkExtension":{"base_url":"http://localhost:8080"}}}'
- BEHAT_PARAMS=`echo $BEHAT_PARAMS | sed -e s#TEST_DIR_MACRO#$TEST_DIR#g`
- export BEHAT_PARAMS

# Build docroot and install thunder
- bash -x -e ${THUNDER_DIST_DIR}/scripts/build-docroot.sh
- bash -x -e ./scripts/travis/install-requirements.sh
- bash -x -e ./scripts/travis/install-thunder.sh

before_script:
- cd ${TEST_DIR}/docroot
- drush cr
- drush runserver --default-server=builtin 8080 &>/dev/null &
- phantomjs --webdriver=4444 > /dev/null &
- bash -x -e ./scripts/travis/setup-tests.sh

script:
# Run Drupal tests (@group Thunder)
- php ${TEST_DIR}/docroot/core/scripts/run-tests.sh --php `which php` --die-on-fail --verbose --url http://localhost:8080 Thunder

# Run Behat tests
- behat --config ${THUNDER_DIST_DIR}/tests/behat/behat.travis.yml
- bash -x -e ./scripts/travis/run-tests.sh

deploy:
provider: s3
access_key_id: AKIAJULZG77ZX2XFLPOA
secret_access_key:
secure: AtsY08zKG01jR6ZQLzIrH6MAF52heHB1fdvWLUs+S9eJJ4h31Z4MJqRtlQQn8Ur1JzFWWS4Gmt3biKILDRm7uCBFz3z3b8kuEt4dPDHzBRdhe6TN4QvTyUz6pL5Q6AR8pVMKc0jNNR2tDCMWqjCMpBCHbdS+28iyobdtx8tQn/HP6Udapl2FHByF1TqCtpMQ9ISbpcjf0AC9WUADL1BnyHoqybAvwUpyoO3Iuym6EhOzCJ3aZULt64IamwpWy3fikVwOtzxwQHS7Jp4M7k0QhgGM0hvlEKnKwS7X6CRysBuxH/exY11UdDUgTzPMw5AsnORkFbyKIh2WW2mvJ9aQJuKbSxAmnr4zQKSS9/7MmDM3FEkeQF8DL5SKb/Q0ap3zCgdcbSVfS5UElS4YrKHSGioudfyEpIQUtMOkW1Lz7SDdLCRD5PaubR8BvifbxRvKNLJX2RjVJ2HyPzKNdkn6mviAteNJrh2YwMgLvC5aFRwCHRFgsJwoek43LL4w3M5jZS8Vz4PrjJkBsLwhVzZbeR0S4CpeewttM5hoC19CllygpbRjjJrChfdre1RsfDRYKsty4WpqZD4jZoxU0sNfyNcn0KnCvspRV40bZqzy0uxY3mVPw76OnZUvWBLVi89PvJ56/AXuHhNalkQbgRxyd0SXAjK34EBMLOyLCGBi3NM=
bucket: thunder-travis-ci
region: eu-central-1
local-dir: /tmp/thunder-travis-ci
upload-dir: thunder-distribution
acl: private
on:
repo: BurdaMagazinOrg/thunder-distribution

+ 1
- 4
composer.json View File

@@ -94,9 +94,6 @@
"valiton/harbourmaster": "~8.1"
},
"require-dev": {
"burdamagazinorg/thunder-dev-tools": "dev-master",
"behat/behat": "~3.0",
"drupal/drupal-extension": "^3.2",
"devinci/devinci-behat-extension": "dev-master"
"burdamagazinorg/thunder-dev-tools": "dev-master"
}
}

+ 1
- 1
modules/thunder_media/js/media_entity_browser.view.js View File

@@ -5,7 +5,7 @@

(function ($) {

"use strict";
'use strict';

/**
* Attaches the behavior of the media entity browser view.


+ 9
- 9
modules/thunder_media/js/thunder_gallery.js View File

@@ -4,16 +4,16 @@
* Media related javascripts.
*/
(function ($) {
'use strict';
'use strict';

/**
* Registers behaviours related to thunder media.
*/
Drupal.behaviors.fixGallery = {
attach: function (context) {
$('.media-gallery img').height($('.media-gallery img').attr('height'));
}
};
/**
* Registers behaviours related to thunder media.
*/
Drupal.behaviors.fixGallery = {
attach: function (context) {
$('.media-gallery img').height($('.media-gallery img').attr('height'));
}
};

}(jQuery, Drupal, drupalSettings));



+ 39
- 0
scripts/travis/before-install.sh View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash

# Download thunder from drupal.org with drush
drush_download_thunder() {
DOWNLOAD_PATH=$1

mkdir -p ${DOWNLOAD_PATH}
cd ${DOWNLOAD_PATH}

drush dl thunder --drupal-project-rename="docroot" -y
}

# remove xdebug to make php execute faster
phpenv config-rm xdebug.ini

# Set MySQL Options
mysql -e 'SET GLOBAL wait_timeout = 5400;'
mysql -e "SHOW VARIABLES LIKE 'wait_timeout'"

# PHP conf tweaks
echo 'max_execution_time = 120' >> drupal.php.ini;
echo 'sendmail_path = /bin/true' >> drupal.php.ini;
phpenv config-add drupal.php.ini
phpenv rehash

# Prepare test directory
mkdir -p ${TEST_DIR}

# Clear drush release history cache, to pick up new releases.
rm -f ~/.drush/cache/download/*---updates.drupal.org-release-history-*

# keep travis running without output
bash -e ${THUNDER_DIST_DIR}/scripts/travis/keep-travis-running.sh &

# If we test update, we also need the previous version of thunder downloaded
if [[ ${TEST_UPDATE} == "true" ]]; then
# Download latest release from drupal.org
drush_download_thunder {$UPDATE_BASE_PATH}
fi

+ 36
- 0
scripts/travis/install-requirements.sh View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash

# update composer
composer self-update

# install required phantomjs Version, if its not already build from travis cache
if [ $(phantomjs --version) != ${PHANTOMJS_VERSION} ]; then
rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs;
fi
if [ $(phantomjs --version) != ${PHANTOMJS_VERSION} ]; then
wget https://assets.membergetmember.co/software/phantomjs-${PHANTOMJS_VERSION}-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-${PHANTOMJS_VERSION}-linux-x86_64.tar.bz2;
fi
if [ $(phantomjs --version) != ${PHANTOMJS_VERSION} ]; then
tar -xf $PWD/travis_phantomjs/phantomjs-${PHANTOMJS_VERSION}-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs;
fi
phantomjs --version

# download + install Selenium2
if [ ! -d "$SELENIUM_PATH" ]; then
mkdir -p $SELENIUM_PATH;
fi

if [ ! -f "$SELENIUM_PATH/selenium-server-standalone-2.53.1.jar" ]; then
wget http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar -O "$SELENIUM_PATH/selenium-server-standalone-2.53.1.jar"
fi

# Install Drush and drupalorg_drush module
composer global require drush/drush:~8
phpenv rehash
drush dl drupalorg_drush-7.x

# verify, that makefile is accepted by drupal.org, otherwise we do not need to go any further
drush verify-makefile

# install image magick
printf "\n" | pecl install imagick

scripts/build-docroot.sh → scripts/travis/install-thunder.sh View File

@@ -1,4 +1,5 @@
#!/bin/bash
#!/usr/bin/env bash


# Install thunder and enable Test module
# in provided folder
@@ -9,19 +10,19 @@ install_thunder() {
drush en simpletest -y
}

# For daily cron runs, current version from Drupal will be installed
# and after that update will be executed and tested
if [[ ${TRAVIS_EVENT_TYPE} == "cron" ]]; then
# Install last version released on Drupal
mkdir -p ${TEST_DIR}-cron-base
cd ${TEST_DIR}-cron-base
# Update thunder to current test version
update_thunder() {
# Link sites folder from initial installation
mv ${TEST_DIR}/docroot/sites ${TEST_DIR}/docroot/_sites
ln -s ${UPDATE_BASE_PATH}/docroot/sites ${TEST_DIR}/docroot/sites

drush dl thunder --drupal-project-rename="docroot" -y
install_thunder ${TEST_DIR}-cron-base/docroot
fi
cd ${TEST_DIR}/docroot

# Install thunder from repository
if [[ ${INSTALL_METHOD} == "drush_make" ]]; then
# Execute all required updates
drush updatedb -y
}

drush_make_thunder() {
cd ${THUNDER_DIST_DIR}

# Build drupal + thunder from makefile
@@ -31,25 +32,42 @@ if [[ ${INSTALL_METHOD} == "drush_make" ]]; then
rsync -a . ${TEST_DIR}/docroot/profiles/thunder --exclude docroot

drush make -y --no-core ${TEST_DIR}/docroot/profiles/thunder/drupal-org.make ${TEST_DIR}/docroot/profiles/thunder
elif [[ ${INSTALL_METHOD} == "composer" ]]; then
# Build thunder by composer
}

composer_create_thunder() {
cd ${THUNDER_DIST_DIR}
composer create-project burdamagazinorg/thunder-infrastructure ${TEST_DIR} --stability dev --no-interaction --no-install

cd ${TEST_DIR}
composer config repositories.thunder path ${THUNDER_DIST_DIR}
composer require "burdamagazinorg/thunder:*" --no-progress
fi

# Post install part
if [[ ${TRAVIS_EVENT_TYPE} == "cron" ]]; then
# Link sites folder from initial installation
mv ${TEST_DIR}/docroot/sites ${TEST_DIR}/docroot/_sites
ln -s ${TEST_DIR}-cron-base/docroot/sites ${TEST_DIR}/docroot/sites
}

apply_patches() {
cd ${TEST_DIR}/docroot

# Execute all required updates
drush updatedb -y
# apply cookie expire patch for javascript tests
wget https://www.drupal.org/files/issues/test-session-expire-2771547-64.patch
patch -p1 < test-session-expire-2771547-64.patch

# return correct error code from run-tests.php script
wget https://www.drupal.org/files/issues/2776071-25.patch
patch -p1 < 2776071-25.patch
}
# Build current revision of thunder
if [[ ${INSTALL_METHOD} == "drush_make" ]]; then
drush_make_thunder
elif [[ ${INSTALL_METHOD} == "composer" ]]; then
composer_create_thunder
fi

# Install Thunder
if [[ ${TEST_UPDATE} == "true" ]]; then
# Install last drupal org version and update to currently tested version
install_thunder ${UPDATE_BASE_PATH}/docroot
update_thunder
else
install_thunder ${TEST_DIR}/docroot
fi
fi

apply_patches

scripts/keep-travis-running.sh → scripts/travis/keep-travis-running.sh View File

@@ -3,5 +3,5 @@
while true
do
echo "Still testing...."
sleep 60
sleep 300
done

+ 7
- 0
scripts/travis/run-tests.sh View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash

# Run Drupal tests (@group Thunder)
cd ${TEST_DIR}/docroot

# execute Drupal tests
php ${TEST_DIR}/docroot/core/scripts/run-tests.sh --php `which php` --die-on-fail --verbose --color --url http://localhost:8080 Thunder

+ 37
- 0
scripts/travis/setup-environment.sh View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash

## Setup environment
export PHANTOMJS_VERSION=2.1.1
# export PATH=$PWD/travis_phantomjs/phantomjs-${PHANTOMJS_VERSION}-linux-x86_64/bin:$PATH

# add composer's global bin directory to the path
# see: https://github.com/drush-ops/drush#install---composer
export PATH="$HOME/.composer/vendor/bin:$PATH"
export THUNDER_DIST_DIR=`echo $(pwd)`
export TEST_DIR=`echo ${THUNDER_DIST_DIR}"/../test-dir"`

# depending on install method, the composer vendor dirrectory is in different places
if [[ ${INSTALL_METHOD} == "drush_make" ]]; then
LOCAL_COMPOSER_VENDOR_DIR=${TEST_DIR}/docroot/vendor
elif [[ ${INSTALL_METHOD} == "composer" ]]; then
LOCAL_COMPOSER_VENDOR_DIR=${TEST_DIR}/vendor
fi
export LOCAL_COMPOSER_VENDOR_DIR

# For daily cron runs, current version from Drupal will be installed
# and after that update will be executed and tested
if [[ ${TRAVIS_EVENT_TYPE} == "cron" ]]; then
TEST_UPDATE="true"
else
TEST_UPDATE=""
fi
export TEST_UPDATE;

# base path for update tests
export UPDATE_BASE_PATH=${TEST_DIR}-update-base

# Setup Selenium2 parameters
export DISPLAY=:99.0

SELENIUM_PATH="$PWD/travis_selenium"
export SELENIUM_PATH

+ 21
- 0
scripts/travis/setup-tests.sh View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash

# Rebuild caches and start servers
cd ${TEST_DIR}/docroot

# require Selenium2 Driver
composer require "behat/mink-selenium2-driver"

# Final cache rebuild, to make sure every code change is respected
drush cr

# Run the webserver
drush runserver --default-server=builtin 8080 &>/dev/null &

# Run phantomjs for javascript tests
#phantomjs --ssl-protocol=any --ignore-ssl-errors=true ${LOCAL_COMPOSER_VENDOR_DIR}/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768 false &>/dev/null &

# Run Selenium2 Server
bash -e /etc/init.d/xvfb start
sleep 3
java -jar "${SELENIUM_PATH}/selenium-server-standalone-2.53.1.jar" > /dev/null 2>&1 &

+ 0
- 10
tests/behat/behat.local.yml.example View File

@@ -1,10 +0,0 @@
default:
extensions:
Behat\MinkExtension:
# Adjust "base_url" to Local installed Thunder site.
base_url: 'http://localhost:8080'
Drupal\DrupalExtension:
drupal:
# Adjust "drupal_root" to point on local installation docroot.
# It can be full path or relative path from location of Behat YAML configuration file.
drupal_root: '../..'

+ 0
- 33
tests/behat/behat.travis.yml View File

@@ -1,33 +0,0 @@
default:
suites:
default:
paths:
features: '%paths.base%/features'
contexts:
- Thunder\behat\FeatureContext
- Devinci\DevinciExtension\Context\JavascriptContext:
maximum_wait: 120
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\DrushContext
- Drupal\DrupalExtension\Context\MessageContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Behat\MinkExtension:
files_path: "%paths.base%/files/"
goutte: ~
selenium2: ~
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drupal'
region_map:
Teaser: '#edit-field-teaser-media-wrapper'
Content: '.region-content'
Paragraphs: '#edit-field-paragraphs-wrapper'
Right Sidebar: '.layout-region-node-secondary'
Footer Bar: '.layout-region-node-footer'
selectors:
message_selector: '.messages'
error_message_selector: '.messages.messages-error'
success_message_selector: '.messages.messages-status'

# Local configuration for Travis CI test run is provided over environment variable: $BEHAT_PARAMS

+ 0
- 33
tests/behat/behat.yml View File

@@ -1,33 +0,0 @@
default:
suites:
default:
paths:
features: '%paths.base%/features'
contexts:
- Thunder\behat\FeatureContext
- Devinci\DevinciExtension\Context\JavascriptContext:
maximum_wait: 120
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\DrushContext
- Drupal\DrupalExtension\Context\MessageContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Behat\MinkExtension:
files_path: "%paths.base%/files/"
goutte: ~
selenium2: ~
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drupal'
region_map:
Teaser: '#edit-field-teaser-media-wrapper'
Content: '.region-content'
Paragraphs: '#edit-field-paragraphs-wrapper'
Right Sidebar: '.layout-region-node-secondary'
Footer Bar: '.layout-region-node-footer'
selectors:
message_selector: '.messages'
error_message_selector: '.messages.messages-error'
success_message_selector: '.messages.messages-status'
imports:
- behat.local.yml

+ 0
- 38
tests/behat/features/article/article.javascript.feature View File

@@ -1,38 +0,0 @@
@api
@javascript
@critical
@article

Feature: Article Teaser

Scenario: As Administrator I make Article with Teaser
Given I am logged in as a user with the "administrator" role

# Create basic channel term.
Given I am on "admin/structure/taxonomy/manage/channel/add"
And I fill in "Behat Teaser channel" for "Name"
And I press "Save"

# Create basic article structure.
Given I am on "/node/add/article"
And I select "Behat Teaser channel" from "Channel"
And I fill in "Behat Teaser Title" for "Title"
And I fill in "This is the page title" for "SEO Title"
And I wait for page to load content
And I expand "URL path settings" option in the "Right Sidebar" region
And I uncheck the box "Generate automatic URL alias"
And I fill in "/behat-teaser-channel/article-teaser" for "URL alias" in the "Right Sidebar" region

# Add 'Teaser'.
And I press "Select entities" in the "Teaser" region
And I wait for page to load content
And I drop the file "thunder-main-1.png" in drop zone and select it
And I wait for page to load content
And I should see an image in the "Teaser" region
And I press "Save and publish" for drop-down button in the "Footer Bar" region

# Check channel page.
Given I am on "/behat-teaser-channel"
And I wait for "Behat Teaser Title"
Then I should see the text "Behat Teaser Title"
Then I should see an image in the "Content" region

+ 0
- 39
tests/behat/features/article/paragraphs/paragraph.media.feature View File

@@ -1,39 +0,0 @@
@api
@javascript
@critical
@article

Feature: Paragraph Media

Scenario: As Administrator I make Article with Media Paragraph
Given I am logged in as a user with the "administrator" role

# Create basic channel term.
Given I am on "admin/structure/taxonomy/manage/channel/add"
And I fill in "Behat channel" for "Name"
And I press "Save"

# Create basic article structure.
Given I am on "/node/add/article"
And I select "Behat channel" from "Channel"
And I fill in "Behat Test Title" for "Title"
And I fill in "This is the page title" for "SEO Title"
And I wait for page to load content
And I expand "URL path settings" option in the "Right Sidebar" region
And I uncheck the box "Generate automatic URL alias"
And I fill in "/behat/article-media" for "URL alias" in the "Right Sidebar" region

# Add 'Media' paragraph.
And I press "Add Media" for drop-down button in the "Paragraphs" region
And I wait for page to load content
And I press "Select entities" in the "Paragraphs" region
And I wait for page to load content
And I drop the file "thunder-main-1.png" in drop zone and select it
And I wait for page to load content
And I should see an image in the "Paragraphs" region
And I press "Save and publish" for drop-down button in the "Footer Bar" region

# Check node page.
Given I am on "/behat/article-media"
And I wait for "Behat Test Title"
Then I should see an image in the "Content" region

+ 0
- 34
tests/behat/features/article/paragraphs/paragraph.quote.feature View File

@@ -1,34 +0,0 @@
@api
@javascript
@critical
@article

Feature: Paragraph Quote

Scenario: As Administrator I make Article with Quote Paragraph
Given I am logged in as a user with the "administrator" role

# Create basic channel term.
Given I am on "admin/structure/taxonomy/manage/channel/add"
And I fill in "Behat channel" for "Name"
And I press "Save"

# Create basic article structure.
Given I am on "/node/add/article"
And I select "Behat channel" from "Channel"
And I fill in "Behat Test Title" for "Title"
And I fill in "This is the page title" for "SEO Title"
And I wait for page to load content
And I expand "URL path settings" option in the "Right Sidebar" region
And I uncheck the box "Generate automatic URL alias"
And I fill in "/behat/article-quote" for "URL alias" in the "Right Sidebar" region

# Add 'Quote' paragraph.
And I press "Add Quote" for drop-down button in the "Paragraphs" region
And I wait for page to load content
And I fill CKEditor with "Simple Quote Paragraph Test" in the "Paragraphs" region
And I press "Save and publish" for drop-down button in the "Footer Bar" region

# Check node page.
Given I am on "/behat/article-quote"
Then I should see the text "Simple Quote Paragraph Test"

+ 0
- 34
tests/behat/features/article/paragraphs/paragraph.text.feature View File

@@ -1,34 +0,0 @@
@api
@javascript
@critical
@article

Feature: Paragraph Text

Scenario: As Administrator I make Article with Text Paragraph
Given I am logged in as a user with the "administrator" role

# Create basic channel term.
Given I am on "admin/structure/taxonomy/manage/channel/add"
And I fill in "Behat channel" for "Name"
And I press "Save"

# Create basic article structure.
Given I am on "/node/add/article"
And I select "Behat channel" from "Channel"
And I fill in "Behat Test Title" for "Title"
And I fill in "This is the page title" for "SEO Title"
And I wait for page to load content
And I expand "URL path settings" option in the "Right Sidebar" region
And I uncheck the box "Generate automatic URL alias"
And I fill in "/behat/article-text" for "URL alias" in the "Right Sidebar" region

# Add 'Text' paragraph.
And I press "Add Text" for drop-down button in the "Paragraphs" region
And I wait for page to load content
And I fill CKEditor with "Simple Text Paragraph Test" in the "Paragraphs" region
And I press "Save and publish" for drop-down button in the "Footer Bar" region

# Check node page.
Given I am on "/behat/article-text"
Then I should see the text "Simple Text Paragraph Test"

+ 0
- 393
tests/behat/features/bootstrap/Thunder/behat/FeatureContext.php View File

@@ -1,393 +0,0 @@
<?php

namespace Thunder\behat;

use Behat\Behat\Context\SnippetAcceptingContext;
use Drupal\DrupalExtension\Context\RawDrupalContext;

/**
* Class FeatureContext.
*
* Defines application features from the specific context.
*/
class FeatureContext extends RawDrupalContext implements SnippetAcceptingContext {

/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct() {
}

/**
* Set screen size before test.
*
* @BeforeScenario
*/
public function beforeScenario() {
$this->getSession()->getDriver()->resizeWindow(1680, 3150);
}

/**
* Get defined Region.
*
* @param string $region
* Region name defined in YAML file.
*
* @return \Behat\Mink\Element\NodeElement|mixed|null
* Returns element when found, otherwise throws exception.
*
* @throws \Exception
*/
public function getRegion($region) {
$session = $this->getSession();
$regionObj = $session->getPage()->find('region', $region);
if (!$regionObj) {
throw new \Exception(
sprintf(
'No region "%s" found on the page %s.',
$region,
$session->getCurrentUrl()
)
);
}

return $regionObj;
}

/**
* Wait for AJAX to finish, so that content on page is updated.
*
* @Given I wait for page to load content
*/
public function iWaitForPageToUpdate() {
$this->getSession()
->wait(5000, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
}

/**
* Checks on drop-down button option in defined region.
*
* @param string $option
* The option text of the button to be pressed.
* @param string $region
* The region in which the button should be pressed.
*
* @throws \Exception
* If region or button within it cannot be found.
*
* @When I press :option for drop-down button in the :region( region)
*/
public function iClickOnDropDownButtonOptionInRegion($option, $region) {
$regionObj = $this->getRegion($region);

// Click drop-down button.
$toggleButton = $regionObj->find('css', '.dropbutton-toggle');
if (empty($toggleButton)) {
throw new \Exception(
sprintf(
'The drop-down button was not found in the region "%s" on the page %s',
$region,
$this->getSession()->getCurrentUrl()
)
);
}
$toggleButton->click();

// Select option.
$dropdownOption = $regionObj->find('named', array('button', $option));
if (empty($dropdownOption)) {
throw new \Exception(
sprintf(
'The drop-down option "%s" was not found in the region "%s" on the page %s',
$option,
$this->getSession()->getCurrentUrl()
)
);
}

$dropdownOption->click();
}

/**
* File value in CKEditor from defined region.
*
* @param string $value
* Text that will be written in CKEditor.
* @param string $region
* The region that contains CKEditor.
*
* @throws \Exception
*
* @Then I fill CKEditor with :value in the :region( region)
*/
public function iFillInCkEditorInRegion($value, $region) {
$regionObj = $this->getRegion($region);

// Find CKEditor.
$ckEditor = $regionObj->find('css', '.form-textarea');

if (empty($ckEditor)) {
throw new \Exception(
sprintf(
'CKEditor was not found in the region "%s" on the page %s',
$region,
$this->getSession()->getCurrentUrl()
)
);
}
$ckEditorId = $ckEditor->getAttribute('id');

$this->getSession()
->executeScript("CKEDITOR.instances[\"$ckEditorId\"].setData(\"$value\");");
}

/**
* Expand/Collapse option in defined region.
*
* @param string $option
* The option text of the button to be pressed.
* @param string $region
* The region in which the menu option should be expanded/collapsed.
*
* @throws \Exception
* If region or menu option within it cannot be found.
*
* @When I expand/collapse :option option in the :region( region)
*/
public function iToggleOptionInRegion($option, $region) {
$regionObj = $this->getRegion($region);

// Find menu option to expand/collapse.
$xpathQuery = "//details/child::summary[text() = '$option']";
$menuOption = $regionObj->find('xpath', $xpathQuery);

// Sometimes it's rendered as link tag.
if (empty($menuOption)) {
$xpathQuery = "//details/summary/child::a[text() = '$option']";
$menuOption = $regionObj->find('xpath', $xpathQuery);
}

if (empty($menuOption)) {
throw new \Exception(
sprintf(
'Unable to find option "%s" in the region "%s" on the page %s',
$option,
$region,
$this->getSession()->getCurrentUrl()
)
);
}

$menuOption->click();
}

/**
* Upload file to drop zone of Entity selector.
*
* Mimic functionality by exposing file field and uploading over it.
*
* @param string $path
* File name used to be uploaded in Drop files field.
*
* @throws \Exception
* If file field is not found for drop down.
*
* @When I drop the file :path in drop zone and select it
*/
public function dropFileInSelectEntities($path) {

// Select entity browser iframe.
$iframe = $this->getSession()
->getPage()
->find('css', 'iframe.entity-browser-modal-iframe');
if (empty($iframe)) {
throw new \Exception(
sprintf(
'Unable to find entity browser iframe on page %s',
$this->getSession()->getCurrentUrl()
)
);
}
$iframeName = $iframe->getAttribute('name');

// Go into iframe scope from Entity Browsers.
$this->getSession()->switchToIFrame($iframeName);

// Wait that iframe is loaded and jQuery is available.
$this->getSession()->wait(10000, '(typeof jQuery !== "undefined")');

// Click all tabs until we find upload Tab.
$tabLinks = $this->getSession()->getPage()->findAll('css', '.eb-tabs a');
if (empty($tabLinks)) {
throw new \Exception(
sprintf(
'Unable to find tabs in entity browser iframe on page %s',
$this->getSession()->getCurrentUrl()
)
);
}

// Click all tabs until input file field for upload is found.
$fileFieldSelector = "input[type='file'].dz-hidden-input";
foreach ($tabLinks as $tabLink) {
/* @var \Behat\Mink\Element\NodeElement $tabLink */
$tabLink->click();

$fileField = $this->getSession()
->getPage()
->find('css', $fileFieldSelector);

if (!empty($fileField)) {
break;
}
}

if (empty($fileField)) {
throw new \Exception(
sprintf(
'The drop-down file field was not found on the page %s',
$this->getSession()->getCurrentUrl()
)
);
}

// Make file field visible and isolate possible problems with "multiple".
$this->getSession()
->executeScript('jQuery("' . $fileFieldSelector . '").show(0).css("visibility","visible").width(200).height(30).removeAttr("multiple");');

// Generate full path to file.
if ($this->getMinkParameter('files_path')) {
$fullPath = rtrim(realpath($this->getMinkParameter('files_path')), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $path;
if (is_file($fullPath)) {
$path = $fullPath;
}
}

$fileField->attachFile($path);

// Wait for file to upload and use press Select button.
$this->iWaitForPageToUpdate();

// Wait up to 10 sec that "Submit" button is active.
$this->getSession()->wait(
10000,
'(typeof jQuery === "undefined" || !jQuery(\'input[name="op"]\').is(":disabled"))'
);

// Go back to Page scope.
$this->getSession()->switchToWindow();

// Click Select button - inside iframe.
$this->getSession()
->executeScript('document.querySelector(\'iframe[name="' . $iframeName . '"]\').contentWindow.jQuery(\'input[name="op"]\').click();');

// Wait up to 10 sec that main page is loaded with new selected images.
$this->getSession()->wait(
10000,
'(typeof jQuery === "undefined" || jQuery(\'.button.js-form-submit.form-submit[value="Remove"]\').length > 0)'
);
}

/**
* Check that alt text for image exists is displayed.
*
* NOTE: We specify a regex to allow escaped quotes in the alt text.
*
* @param string $text
* Alt text that should be checked.
* @param string $region
* The region that contains image.
*
* @throws \Exception
*
* @Then /^I should see the image alt "(?P<text>(?:[^"]|\\")*)" in the "(?P<region>[^"]*)" region$/
*/
public function assertAltRegion($text, $region) {
$regionObj = $this->getRegion($region);
$element = $regionObj->find('css', 'img');
if (empty($element)) {
throw new \Exception(sprintf('No alt text matching "%s" in the "%s" region on the page %s', $text, $region, $this->getSession()
->getCurrentUrl()));
}
$tmp = $element->getAttribute('alt');
if ($text == $tmp) {
$result = $text;
}
if (empty($result)) {
throw new \Exception(sprintf('No alt text matching "%s" in the "%s" region on the page %s', $text, $region, $this->getSession()
->getCurrentUrl()));
}
}

/**
* Asserts that an image is present and not broken.
*
* @param string $region
* The region where image should appear.
*
* @throws \Exception
*
* @Then I should see an image in the :region region
*/
public function assertValidImageRegion($region) {
$regionObj = $this->getRegion($region);

// In order to give browser chance to load image, wait for 10sec.
$elements = $regionObj->waitFor(10, function () use ($regionObj) {
return $regionObj->findAll('css', 'img');
});

if (empty($elements)) {
throw new \Exception(sprintf('Image was not found in the "%s" region on the page %s', $region, $this->getSession()
->getCurrentUrl()));
}

$src = $elements[0]->getAttribute('src');
if (empty($src)) {
$src = $elements[0]->getAttribute('srcset');
}

if (!empty($src)) {
$params = array('http' => array('method' => 'HEAD'));
$context = stream_context_create($params);
$file_uri = file_create_url(ltrim($src, '/'));
$fp = @fopen($file_uri, 'rb', FALSE, $context);

if (!$fp) {
throw new \Exception(sprintf('Unable to download <img src="%s"> in the "%s" region on the page %s', $src, $region, $this->getSession()
->getCurrentUrl()));
}

$meta = stream_get_meta_data($fp);
fclose($fp);
if ($meta === FALSE) {
throw new \Exception(sprintf('Error reading from <img src="%s"> in the "%s" region on the page %s', $src, $region, $this->getSession()
->getCurrentUrl()));
}

$wrapper_data = $meta['wrapper_data'];
$found = FALSE;
if (is_array($wrapper_data)) {
foreach ($wrapper_data as $header) {
if (substr(strtolower($header), 0, 19) == 'content-type: image') {
$found = TRUE;
}
}
}

if (!$found) {
throw new \Exception(sprintf('Not a valid image <img src="%s"> in the "%s" region on the page %s', $src, $region, $this->getSession()
->getCurrentUrl()));
}
}
else {
throw new \Exception(sprintf('No image had no src="..." attribute in the "%s" region on the page %s', $region, $this->getSession()
->getCurrentUrl()));
}
}

}

BIN
tests/behat/files/thunder-main-1.png View File

Before After
Width: 639  |  Height: 358  |  Size: 463KB

BIN
tests/fixtures/reference.jpg View File

Before After
Width: 400  |  Height: 250  |  Size: 20KB

+ 331
- 0
tests/modules/thunder_test/js/jquery.simulate.js View File

@@ -0,0 +1,331 @@
/*!
* jQuery Simulate [email protected] - simulate browser mouse and keyboard events
* https://github.com/jquery/jquery-simulate
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* Date: @DATE
*/

;(function( $, undefined ) {

var rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/;

$.fn.simulate = function( type, options ) {
return this.each(function() {
new $.simulate( this, type, options );
});
};

$.simulate = function( elem, type, options ) {
var method = $.camelCase( "simulate-" + type );

this.target = elem;
this.options = options;

if ( this[ method ] ) {
this[ method ]();
} else {
this.simulateEvent( elem, type, options );
}
};

$.extend( $.simulate, {

keyCode: {
BACKSPACE: 8,
COMMA: 188,
DELETE: 46,
DOWN: 40,
END: 35,
ENTER: 13,
ESCAPE: 27,
HOME: 36,
LEFT: 37,
NUMPAD_ADD: 107,
NUMPAD_DECIMAL: 110,
NUMPAD_DIVIDE: 111,
NUMPAD_ENTER: 108,
NUMPAD_MULTIPLY: 106,
NUMPAD_SUBTRACT: 109,
PAGE_DOWN: 34,
PAGE_UP: 33,
PERIOD: 190,
RIGHT: 39,
SPACE: 32,
TAB: 9,
UP: 38
},

buttonCode: {
LEFT: 0,
MIDDLE: 1,
RIGHT: 2
}
});

$.extend( $.simulate.prototype, {

simulateEvent: function( elem, type, options ) {
var event = this.createEvent( type, options );
this.dispatchEvent( elem, type, event, options );
},

createEvent: function( type, options ) {
if ( rkeyEvent.test( type ) ) {
return this.keyEvent( type, options );
}

if ( rmouseEvent.test( type ) ) {
return this.mouseEvent( type, options );
}
},

mouseEvent: function( type, options ) {
var event, eventDoc, doc, body;
options = $.extend({
bubbles: true,
cancelable: (type !== "mousemove"),
view: window,
detail: 0,
screenX: 0,
screenY: 0,
clientX: 1,
clientY: 1,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
relatedTarget: undefined
}, options );

if ( document.createEvent ) {
event = document.createEvent( "MouseEvents" );
event.initMouseEvent( type, options.bubbles, options.cancelable,
options.view, options.detail,
options.screenX, options.screenY, options.clientX, options.clientY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
options.button, options.relatedTarget || document.body.parentNode );

// IE 9+ creates events with pageX and pageY set to 0.
// Trying to modify the properties throws an error,
// so we define getters to return the correct values.
if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
eventDoc = event.relatedTarget.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;

Object.defineProperty( event, "pageX", {
get: function() {
return options.clientX +
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
( doc && doc.clientLeft || body && body.clientLeft || 0 );
}
});
Object.defineProperty( event, "pageY", {
get: function() {
return options.clientY +
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
( doc && doc.clientTop || body && body.clientTop || 0 );
}
});
}
} else if ( document.createEventObject ) {
event = document.createEventObject();
$.extend( event, options );
// standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx
// old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx
// so we actually need to map the standard back to oldIE
event.button = {
0: 1,
1: 4,
2: 2
}[ event.button ] || ( event.button === -1 ? 0 : event.button );
}

return event;
},

keyEvent: function( type, options ) {
var event;
options = $.extend({
bubbles: true,
cancelable: true,
view: window,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
keyCode: 0,
charCode: undefined
}, options );

if ( document.createEvent ) {
try {
event = document.createEvent( "KeyEvents" );
event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
options.keyCode, options.charCode );
// initKeyEvent throws an exception in WebKit
// see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution
// and also https://bugs.webkit.org/show_bug.cgi?id=13368
// fall back to a generic event until we decide to implement initKeyboardEvent
} catch( err ) {
event = document.createEvent( "Events" );
event.initEvent( type, options.bubbles, options.cancelable );
$.extend( event, {
view: options.view,
ctrlKey: options.ctrlKey,
altKey: options.altKey,
shiftKey: options.shiftKey,
metaKey: options.metaKey,
keyCode: options.keyCode,
charCode: options.charCode
});
}
} else if ( document.createEventObject ) {
event = document.createEventObject();
$.extend( event, options );
}

if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) {
event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
event.charCode = undefined;
}

return event;
},

dispatchEvent: function( elem, type, event ) {
if ( elem.dispatchEvent ) {
elem.dispatchEvent( event );
} else if ( type === "click" && elem.click && elem.nodeName.toLowerCase() === "input" ) {
elem.click();
} else if ( elem.fireEvent ) {
elem.fireEvent( "on" + type, event );
}
},

simulateFocus: function() {
var focusinEvent,
triggered = false,
element = $( this.target );

function trigger() {
triggered = true;
}

element.bind( "focus", trigger );
element[ 0 ].focus();

if ( !triggered ) {
focusinEvent = $.Event( "focusin" );
focusinEvent.preventDefault();
element.trigger( focusinEvent );
element.triggerHandler( "focus" );
}
element.unbind( "focus", trigger );
},

simulateBlur: function() {
var focusoutEvent,
triggered = false,
element = $( this.target );

function trigger() {
triggered = true;
}

element.bind( "blur", trigger );
element[ 0 ].blur();

// blur events are async in IE
setTimeout(function() {
// IE won't let the blur occur if the window is inactive
if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
element[ 0 ].ownerDocument.body.focus();
}

// Firefox won't trigger events if the window is inactive
// IE doesn't trigger events if we had to manually focus the body
if ( !triggered ) {
focusoutEvent = $.Event( "focusout" );
focusoutEvent.preventDefault();
element.trigger( focusoutEvent );
element.triggerHandler( "blur" );
}
element.unbind( "blur", trigger );
}, 1 );
}
});



/** complex events **/

function findCenter( elem ) {
var offset,
document = $( elem.ownerDocument );
elem = $( elem );
offset = elem.offset();

return {
x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
};
}

function findCorner( elem ) {
var offset,
document = $( elem.ownerDocument );
elem = $( elem );
offset = elem.offset();

return {
x: offset.left - document.scrollLeft(),
y: offset.top - document.scrollTop()
};
}

$.extend( $.simulate.prototype, {
simulateDrag: function() {
var i = 0,
target = this.target,
eventDoc = target.ownerDocument,
options = this.options,
center = options.handle === "corner" ? findCorner( target ) : findCenter( target ),
x = Math.floor( center.x ),
y = Math.floor( center.y ),
coord = { clientX: x, clientY: y },
dx = options.dx || ( options.x !== undefined ? options.x - x : 0 ),
dy = options.dy || ( options.y !== undefined ? options.y - y : 0 ),
moves = options.moves || 3;

this.simulateEvent( target, "mousedown", coord );

for ( ; i < moves ; i++ ) {
x += dx / moves;
y += dy / moves;

coord = {
clientX: Math.round( x ),
clientY: Math.round( y )
};

this.simulateEvent( eventDoc, "mousemove", coord );
}

if ( $.contains( eventDoc, target ) ) {
this.simulateEvent( target, "mouseup", coord );
this.simulateEvent( target, "click", coord );
} else {
this.simulateEvent( eventDoc, "mouseup", coord );
}
}
});

})( jQuery );

+ 6
- 0
tests/modules/thunder_test/thunder_test.info.yml View File

@@ -0,0 +1,6 @@
name: 'Thunder test'
type: module
description: 'Support module for the thunder tests.'
core: 8.x
package: Testing
version: VERSION

+ 7
- 0
tests/modules/thunder_test/thunder_test.libraries.yml View File

@@ -0,0 +1,7 @@
thunder_test_simulate_jquery:
version: VERSION
js:
js/jquery.simulate.js: {}
dependencies:
- core/jquery
- core/drupal

+ 13
- 0
tests/modules/thunder_test/thunder_test.module View File

@@ -0,0 +1,13 @@
<?php

/**
* @file
* Contains hooks for Thunder JavaScript tests.
*/

/**
* Add jQuery library to simulate actions.
*/
function thunder_test_page_attachments(array &$page) {
$page['#attached']['library'][] = 'thunder_test/thunder_test_simulate_jquery';
}

+ 67
- 0
tests/src/FunctionalJavascript/ArticleCreationTest.php View File

@@ -0,0 +1,67 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

/**
* Tests the article creation.
*
* @group Thunder
*/
class ArticleCreationTest extends ThunderJavascriptTestBase {

use ThunderParagraphsTestTrait;
use ThunderMediaTestTrait;

/**
* Test Creation of Article.
*/
public function testCreateArticle() {
$this->drupalGet('node/add/article');

$page = $this->getSession()->getPage();

$page->selectFieldOption('field_channel', 1);

$page->fillField('title[0][value]', 'Test article');
$page->fillField('field_seo_title[0][value]', 'Massive gaining seo traffic text');

$this->selectMedia('field_teaser_media', 'image_browser', ['media:1']);

// Paragraph 1.
$this->addMediaParagraph('field_paragraphs', ['media:5']);

// Paragraph 2.
$this->addTextParagraph('field_paragraphs', 'Awesome text');

// Paragraph 3.
$this->addGalleryParagraph('field_paragraphs', 'Test gallery', [
'media:1',
'media:5',
]);

// Paragraph 4.
$this->addTextParagraph('field_paragraphs', 'Awesome quote', 'quote');

$this->scrollElementInView('#edit-actions');

$this->createScreenshot($this->getScreenshotFolder() . '/ArticleCreationTest_BeforeSave_' . date('Ymd_His') . '.png');

$page->pressButton('Save as unpublished');

$this->createScreenshot($this->getScreenshotFolder() . '/ArticleCreationTest_AfterSave_' . date('Ymd_His') . '.png');

$this->assertPageTitle('Massive gaining seo traffic text');
$this->assertSession()->pageTextContains('Test article');

$this->assertSession()->pageTextContains('Awesome text');
$this->assertSession()->pageTextContains('Awesome quote');

$this->assertSession()
->elementExists('xpath', '//div[contains(@class, "field--name-field-paragraphs")]/div[contains(@class, "field__item")][1]//img');
$this->assertSession()
->elementExists('xpath', '//div[contains(@class, "field--name-field-paragraphs")]/div[contains(@class, "field__item")][3]//img');

$this->getSession()->stop();
}

}

+ 63
- 0
tests/src/FunctionalJavascript/MediaGalleryModifyTest.php View File

@@ -0,0 +1,63 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

/**
* Tests the Gallery media modification.
*
* @group Thunder
*/
class MediaGalleryModifyTest extends ThunderJavascriptTestBase {


/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'thunder_test',
];

/**
* Test order change for Gallery.
*
* @throws \Exception
*/
public function testOrderChange() {

$this->drupalGet("node/7/edit");

$page = $this->getSession()->getPage();

$this->createScreenshot($this->getScreenshotFolder() . '/MediaGalleryModifyTest_BeforeOrderChange_' . date('Ymd_His') . '.png');

$this->scrollElementInView('[name="field_paragraphs_0_edit"]');
$page->pressButton('field_paragraphs_0_edit');

$this->assertSession()->assertWaitOnAjaxRequest();

$editButton = $page->find('css', 'input[data-drupal-selector="edit-field-paragraphs-0-subform-field-media-entities-0-actions-ief-entity-edit"]');
$editButton->click();
$this->assertSession()->assertWaitOnAjaxRequest();

$cssSelector = 'div[data-drupal-selector="edit-field-paragraphs-0-subform-field-media-form-inline-entity-form-entities-0-form-field-media-images-current"]';

$this->scrollElementInView($cssSelector . ' > *:nth-child(2)');
$this->getSession()
->getDriver()
->executeScript('jQuery(\'' . $cssSelector . ' div[data-entity-id="media:8"]\').simulate( "drag", { moves: 1, dx: 0, dy: 300 });');

$this->createScreenshot($this->getScreenshotFolder() . '/MediaGalleryModifyTest_AfterOrderChange_' . date('Ymd_His') . '.png');

$secondElement = $page->find('xpath', '//div[@data-drupal-selector="edit-field-paragraphs-0-subform-field-media-form-inline-entity-form-entities-0-form-field-media-images-current"]/div[2]');
if (empty($secondElement)) {
throw new \Exception('Second element in Gallery is not found');
}

$this->assertSame('media:8', $secondElement->getAttribute('data-entity-id'));

$this->getSession()->stop();
}

}

+ 60
- 0
tests/src/FunctionalJavascript/MediaImageModifyTest.php View File

@@ -0,0 +1,60 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;
use Drupal\media_entity\Entity\Media;

/**
* Tests the Image media modification.
*
* @group Thunder
*/
class MediaImageModifyTest extends ThunderJavascriptTestBase {

/**
* Test Focal Point change.
*/
public function testFocalPointChange() {

// Media ID used for testing.
$mediaId = 9;

$page = $this->getSession()->getPage();

$this->drupalGet("media/$mediaId/edit");

$this->createScreenshot($this->getScreenshotFolder() . '/MediaImageModifyTest_BeforeFocalPointChange_' . date('Ymd_His') . '.png');

$this->getSession()
->getDriver()
->executeScript('var e = new jQuery.Event("click"); e.offsetX = 48; e.offsetY = 15; jQuery(".focal-point-wrapper img").trigger(e);');

$this->createScreenshot($this->getScreenshotFolder() . '/MediaImageModifyTest_AfterFocalPointChange_' . date('Ymd_His') . '.png');

$page->pressButton('Save and keep publish');

$media = Media::load($mediaId);
$img = $media->get('field_image')->target_id;

$file = File::load($img);
$path = $file->getFileUri();

$derivativeUri = ImageStyle::load('teaser')->buildUri($path);

ImageStyle::load('teaser')->createDerivative($path, $derivativeUri);

$image1 = new \Imagick($derivativeUri);
$image2 = new \Imagick(dirname(__FILE__) . '/../../fixtures/reference.jpg');

$result = $image1->compareImages($image2, \Imagick::METRIC_MEANSQUAREERROR);

$this->assertTrue($result[1] < 0.01, 'Images are identical');

$image1->clear();
$image2->clear();
$this->getSession()->stop();
}

}

+ 205
- 0
tests/src/FunctionalJavascript/ThunderJavascriptTestBase.php View File

@@ -0,0 +1,205 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

use Behat\Mink\Driver\Selenium2Driver;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\Tests\BrowserTestBase;

/**
* Base class for Thunder Javascript functional tests.
*
* @package Drupal\Tests\thunder\FunctionalJavascript
*/
abstract class ThunderJavascriptTestBase extends JavascriptTestBase {

/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'thunder';

/**
* {@inheritdoc}
*/
protected $minkDefaultDriverClass = Selenium2Driver::class;

/**
* Directory path for saving screenshots.
*
* @var string
*/
protected $screenshotDirectory = '/tmp/thunder-travis-ci';

/**
* {@inheritdoc}
*/
protected function initMink() {
// Set up the template cache used by the PhantomJS mink driver.
$path = $this->tempFilesDirectory . DIRECTORY_SEPARATOR . 'browsertestbase-templatecache';

$this->minkDefaultDriverArgs = [
'firefox',
NULL,
'http://127.0.0.1:4444/wd/hub',
];

if (!file_exists($path)) {
mkdir($path);
}

try {
return BrowserTestBase::initMink();
}
catch (Exception $e) {
$this->markTestSkipped('An unexpected error occurred while starting Mink: ' . $e->getMessage());
}
}

/**
* {@inheritdoc}
*/
protected function drupalLogin(AccountInterface $account) {
if ($this->loggedInUser) {
$this->drupalLogout();
}

$this->drupalGet('user');
$this->submitForm(array(
'name' => $account->getUsername(),
'pass' => $account->passRaw,
), t('Log in'));

// @see BrowserTestBase::drupalUserIsLoggedIn()
$account->sessionId = $this->getSession()
->getCookie($this->getSessionName());
$this->assertTrue($this->drupalUserIsLoggedIn($account), SafeMarkup::format('User %name successfully logged in.', array('name' => $account->getUsername())));

$this->loggedInUser = $account;
$this->container->get('current_user')->setAccount($account);
}

/**
* {@inheritdoc}
*/
protected function drupalLogout() {
// Make a request to the logout page, and redirect to the user page, the
// idea being if you were properly logged out you should be seeing a login
// screen.
$assert_session = $this->assertSession();
$this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
$assert_session->fieldExists('name');
$assert_session->fieldExists('pass');

// @see BrowserTestBase::drupalUserIsLoggedIn()
unset($this->loggedInUser->sessionId);
$this->loggedInUser = FALSE;
$this->container->get('current_user')
->setAccount(new AnonymousUserSession());
}

/**
* {@inheritdoc}
*/
protected function setUp() {

parent::setUp();

$editor = $this->drupalCreateUser();
$editor->addRole('editor');
$editor->save();
$this->drupalLogin($editor);
}

/**
* Opening of Inline Entity Form.
*
* @param string $fieldName
* Field Name.
*/
protected function openIefComplex($fieldName) {

$page = $this->getSession()->getPage();

$selector = "div[data-drupal-selector='edit-" . str_replace('_', '-', $fieldName) . "-wrapper'] > div";

$this->assertSession()->elementExists('css', $selector);

$iefForm = $page->find('css', $selector);

$iefId = $iefForm->getAttribute('id');

$page->pressButton(str_replace('inline-entity-form', 'ief', $iefId) . '-add');

$this->assertSession()->assertWaitOnAjaxRequest();
}

/**
* Waits and asserts that a given element is visible.
*
* @param string $selector
* The CSS selector.
* @param int $timeout
* (Optional) Timeout in milliseconds, defaults to 1000.
* @param string $message
* (Optional) Message to pass to assertJsCondition().
*/
protected function waitUntilVisible($selector, $timeout = 1000, $message = '') {
$condition = "jQuery('" . $selector . ":visible').length > 0";
$this->assertJsCondition($condition, $timeout, $message);
}

/**
* Get directory for saving of screenshots.
*
* Directory will be created if it does not already exist.
*
* @return string
* Return directory path to store screenshots.
*
* @throws \Exception
*/
protected function getScreenshotFolder() {
if (!is_dir($this->screenshotDirectory)) {
if (mkdir($this->screenshotDirectory, 0777, TRUE) === FALSE) {
throw new \Exception('Unable to create directory: ' . $this->screenshotDirectory);
}
}

return realpath($this->screenshotDirectory);
}

/**
* Scroll element with defined css selector in middle of browser view.
*
* @param string $cssSelector
* CSS Selector for element that should be centralized.
*/
protected function scrollElementInView($cssSelector) {
$this->getSession()
->executeScript('var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); var elementTop = jQuery(\'' . $cssSelector . '\').offset().top; window.scroll(0, elementTop-(viewPortHeight/2));');
}

/**
* Assert page title.
*
* @param string $expectedTitle
* Expected title.
*/
protected function assertPageTitle($expectedTitle) {
$driver = $this->getSession()->getDriver();
if ($driver instanceof Selenium2Driver) {
$actualTitle = $driver->getWebDriverSession()->title();

static::assertTrue($expectedTitle === $actualTitle, 'Title found');
}
else {
$this->assertSession()->titleEquals($expectedTitle);
}
}

}

+ 79
- 0
tests/src/FunctionalJavascript/ThunderMediaTestTrait.php View File

@@ -0,0 +1,79 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

/**
* Trait for handling of Media related test actions.
*
* @package Drupal\Tests\thunder\FunctionalJavascript
*/
trait ThunderMediaTestTrait {

/**
* Select Medias for field.
*
* @param string $fieldName
* Field name.
* @param string $entityBrowser
* Entity browser identifier.
* @param array $medias
* List of media identifiers.
*/
public function selectMedia($fieldName, $entityBrowser, $medias) {

/** @var \Behat\Mink\Element\DocumentElement $page */
$page = $this->getSession()->getPage();

$this->assertSession()->assertWaitOnAjaxRequest();

$buttonName = $fieldName . '_entity_browser_entity_browser';
$this->scrollElementInView("[name=\"{$buttonName}\"]");
$page->pressButton($buttonName);

$this->assertSession()->assertWaitOnAjaxRequest();

$this->getSession()
->switchToIFrame('entity_browser_iframe_' . $entityBrowser);
$this->assertSession()->assertWaitOnAjaxRequest();

foreach ($medias as $media) {
$this->getSession()
->executeScript("jQuery('[name=\"entity_browser_select[$media]\"]').prop('checked', true);");
}

$page->pressButton('Select entities');

if ($entityBrowser == 'multiple_image_browser') {
$page->pressButton('Use selected');
}

$this->getSession()->switchToIFrame();
$this->assertSession()->assertWaitOnAjaxRequest();

$this->waitUntilVisible('div[data-drupal-selector="edit-' . str_replace('_', '-', $fieldName) . '-wrapper"] img');
}

/**
* Create gallery with selected medias.
*
* @param string $name
* Name of gallery.
* @param string $fieldName
* Field name.
* @param string $medias
* List of media identifiers.
*/
public function createGallery($name, $fieldName, $medias) {

$page = $this->getSession()->getPage();

$selector = "input[data-drupal-selector='edit-" . str_replace('_', '-', $fieldName) . "-form-inline-entity-form-name-0-value']";
$this->assertSession()->elementExists('css', $selector);

$nameField = $page->find('css', $selector);
$nameField->setValue($name);

$this->selectMedia("${fieldName}_form_inline_entity_form_field_media_images", 'multiple_image_browser', $medias);
}

}

+ 117
- 0
tests/src/FunctionalJavascript/ThunderParagraphsTestTrait.php View File

@@ -0,0 +1,117 @@
<?php

namespace Drupal\Tests\thunder\FunctionalJavascript;

/**
* Trait for handling of Paragraph related test actions.
*
* @package Drupal\Tests\thunder\FunctionalJavascript
*/
trait ThunderParagraphsTestTrait {

/**
* Counter used to count number of added Paragraphs.
*
* @var int $paragraphCount
*/
protected $paragraphCount;

/**
* Add paragraph for field with defined paragraph type.
*
* @param string $fieldName
* Field name.
* @param string $type
* Type of the paragraph.
*/
public function addParagraph($fieldName, $type) {

$page = $this->getSession()->getPage();

$toggleButtonSelector = '#edit-' . str_replace('_', '-', $fieldName) . '-wrapper .dropbutton-toggle button';
$toggleButton = $page->find('css', $toggleButtonSelector);
$this->scrollElementInView($toggleButtonSelector);

$toggleButton->click();
$this->assertSession()->assertWaitOnAjaxRequest();

$addMoreButtonName = "${fieldName}_${type}_add_more";
$this->scrollElementInView("[name=\"$addMoreButtonName\"]");
$page->pressButton($addMoreButtonName);
$this->assertSession()->assertWaitOnAjaxRequest();

if (!isset($this->paragraphCount[$fieldName])) {
$this->paragraphCount[$fieldName] = 0;
}
else {
$this->paragraphCount[$fieldName]++;
}

$this->waitUntilVisible('div[data-drupal-selector="edit-' . str_replace('_', '-', $fieldName) . '-' . $this->paragraphCount[$fieldName] . '-subform"]');
}

/**
* Add Media paragraph.
*
* @param string $fieldName
* Field name.
* @param array $media
* List of media identifiers.
*/
public function addMediaParagraph($fieldName, $media) {

$this->addParagraph($fieldName, 'media');

$count = $this->paragraphCount[$fieldName];
$this->selectMedia("${fieldName}_${count}_subform_field_media", 'media_browser', $media);

}

/**
* Add Gallery paragraph.
*
* @param string $fieldName
* Field name.
* @param string $name
* Name of the gallery.
* @param array $media
* List of media identifiers.
*/
public function addGalleryParagraph($fieldName, $name, $media) {

$this->addParagraph($fieldName, 'gallery');
$count = $this->paragraphCount[$fieldName];

$this->openIefComplex("${fieldName}_${count}_subform_field_media");

$this->createGallery($name, "${fieldName}_${count}_subform_field_media", $media);

}

/**
* Adding text type paragraphs.
*
* @param string $fieldName
* Field name.
* @param string $text
* Text for paragraph.
* @param string $type
* Type of text paragraph.
*/
public function addTextParagraph($fieldName, $text, $type = 'text') {

$this->addParagraph($fieldName, $type);
$count = $this->paragraphCount[$fieldName];

$page = $this->getSession()->getPage();

$ckEditor = $page->find('css', "textarea[name='${fieldName}[${count}][subform][field_text][0][value]']");

$ckEditorId = $ckEditor->getAttribute('id');

$this->getSession()
->getDriver()
->executeScript("CKEDITOR.instances[\"$ckEditorId\"].setData(\"$text\");");
}

}

Loading…
Cancel
Save