Skip to content

Commit

Permalink
Merge pull request #861 from City-of-Helsinki/UHF-10908
Browse files Browse the repository at this point in the history
UHF-10908 FunctionalJS test for cookie banner
  • Loading branch information
khalima authored Dec 12, 2024
2 parents 79ab4ac + aa91b08 commit f096789
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 23 deletions.
54 changes: 35 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ on:
name: CI
env:
SYMFONY_DEPRECATIONS_HELPER: disabled
SIMPLETEST_BASE_URL: http://app:8888
COMPOSER_DISCARD_CHANGES: true
COMPOSER_MIRROR_PATH_REPOS: 1
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['8.1', '8.2', '8.3']
php-versions: ['8.3']
container:
image: ghcr.io/city-of-helsinki/drupal-php-docker:${{ matrix.php-versions }}-alpine
options: --hostname app
image: ghcr.io/city-of-helsinki/drupal-web:${{ matrix.php-versions }}-dev
options: --hostname app --user 1001
services:
db:
image: mysql:8
Expand All @@ -26,6 +27,15 @@ jobs:
MYSQL_ROOT_PASSWORD: drupal
ports:
- 3306:3306
chromium:
image: selenium/standalone-chromium
env:
SE_NODE_OVERRIDE_MAX_SESSIONS: "true"
SE_NODE_MAX_SESSIONS: "16"
SE_START_XVFB: "false"
SE_START_VNC: "false"
SE_SESSION_RETRY_INTERVAL: "1"
SE_SESSION_REQUEST_TIMEOUT: "10"

steps:
- uses: actions/checkout@v4
Expand All @@ -38,6 +48,13 @@ jobs:
- name: Set Drupal root
run: echo "DRUPAL_ROOT=$HOME/drupal" >> $GITHUB_ENV

# Actions worker overrides the default entrypoint with "tail -f /dev/null", so
# we have to start services manually.
- name: Start services
env:
WEBROOT: ${{ env.DRUPAL_ROOT }}/public
run: entrypoint &

- name: Set module folder
run: |
echo "MODULE_FOLDER=$DRUPAL_ROOT/public/modules/contrib/$MODULE_NAME" >> $GITHUB_ENV
Expand All @@ -48,23 +65,17 @@ jobs:
git clone --depth=1 https://github.com/City-of-Helsinki/drupal-helfi-platform.git $DRUPAL_ROOT
rm -rf $DRUPAL_ROOT/.git
# We use COMPOSER_MIRROR_PATH_REPOS=1 to mirror local repository
# instead of symlinking it to prevent code coverage issues with
# phpunit. Copy .git folder manually so codecov can generate line by
# line coverage.
- name: Install required composer dependencies
working-directory: ${{ env.DRUPAL_ROOT }}
run: |
composer config repositories.5 path $GITHUB_WORKSPACE
composer require drupal/$MODULE_NAME -W
# We use COMPOSER_MIRROR_PATH_REPOS=1 to mirror local repository
# instead of symlinking it to prevent code coverage issues with
# phpunit. Copy .git folder manually so codecov can generate line by
# line coverage.
cp -r $GITHUB_WORKSPACE/.git $MODULE_FOLDER/
- name: Install Drupal
working-directory: ${{ env.DRUPAL_ROOT }}
run: |
php -d sendmail_path=$(which true); vendor/bin/drush --yes -v site-install minimal --db-url="$SIMPLETEST_DB"
vendor/bin/drush en $MODULE_NAME helfi_platform_config_base -y
- name: Run PHPCS
working-directory: ${{ env.DRUPAL_ROOT }}
run: vendor/bin/phpcs $MODULE_FOLDER --standard=$MODULE_FOLDER/phpcs.xml --extensions=php,module,inc,install,test,info
Expand All @@ -73,9 +84,11 @@ jobs:
working-directory: ${{ env.DRUPAL_ROOT }}
run: vendor/bin/phpstan analyze -c $MODULE_FOLDER/phpstan.neon $MODULE_FOLDER

- name: Start services
- name: Install Drupal
working-directory: ${{ env.DRUPAL_ROOT }}
run: vendor/bin/drush runserver $SIMPLETEST_BASE_URL --dns &
run: |
php -d sendmail_path=$(which true); vendor/bin/drush --yes -v site-install minimal --db-url="$SIMPLETEST_DB"
vendor/bin/drush en $MODULE_NAME helfi_platform_config_base -y
- name: Run PHPUnit tests
working-directory: ${{ env.DRUPAL_ROOT }}
Expand All @@ -86,9 +99,12 @@ jobs:
--coverage-clover=$MODULE_FOLDER/coverage.xml \
$MODULE_FOLDER
- name: Run codecov
working-directory: ${{ env.MODULE_FOLDER }}
run: codecov
- uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
directory: ${{ env.MODULE_FOLDER }}
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true

- name: Create an artifact from test report
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: 'HDBT cookie banner test module'
type: module
package: Custom
core_version_requirement: ^10 || ^11
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/**
* @file
* Contains hooks for hdbt cookie banner test module.
*/

declare(strict_types=1);

/**
* Implements hook_page_attachments().
*/
function hdbt_cookie_banner_test_page_attachments(array &$attachments) : void {
// Alter the HDBT cookie banner settings.
$attachments['#attached']['drupalSettings']['hdbt_cookie_banner']['spacerParentSelector'] = '.test-footer';
global $base_url;
$attachments['#attached']['drupalSettings']['hdbt_cookie_banner']['apiUrl'] = $base_url . '/api/cookie-banner';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
hdbt_cookie_banner_test.test_route:
path: '/test-page'
defaults:
_controller: '\Drupal\hdbt_cookie_banner_test\Controller\TestController::content'
_title: 'Test Page'
requirements:
_permission: 'access content'
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Drupal\hdbt_cookie_banner_test\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
* Provides a test page for the custom module.
*/
class TestController extends ControllerBase {

/**
* Test page content.
*
* @return array
* Render array.
*/
public function content() {
return [
'#type' => 'inline_template',
'#template' => '<p>Test Content</p><div class="test-footer"></div>',
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

namespace Drupal\Tests\hdbt_cookie_banner\FunctionalJavascript;

use Behat\Mink\Driver\Selenium2Driver;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;

/**
* Tests the functionality of the JavaScript cookie banner.
*
* @group hdbt_cookie_banner
*/
class CookieBannerTest extends WebDriverTestBase {

/**
* {@inheritdoc}
*/
protected static $modules = [
'hdbt_cookie_banner',
'hdbt_cookie_banner_test',
];

/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';

/**
* Setup the test environment.
*/
protected function setUp(): void {
parent::setUp();

// Get the path to the JSON file.
$module_path = \Drupal::service('extension.list.module')
->getPath('hdbt_cookie_banner');
$json_file_path = $module_path . '/assets/json/siteSettingsTemplate.json';

// Assert the file exists.
$this->assertTrue(file_exists($json_file_path));

// Load and decode the JSON.
$json_content = file_get_contents($json_file_path);
$this->assertNotEmpty($json_content, 'Decoded JSON data is not empty.');

// Get the public base URL (in a FunctionalJavascript test).
// Construct a URL for the hds-cookie-consent.min.js file.
$cookie_js_url = "/$module_path/assets/js/hds-cookie-consent.min.js";

// Change configuration value before the test runs.
$config = $this->config('hdbt_cookie_banner.settings');
$config
->set('use_custom_settings', TRUE)
->set('site_settings', $json_content)
->set('use_internal_hds_cookie_js', FALSE)
->set('hds_cookie_js_override', $cookie_js_url)
->save();

\Drupal::service('cache.default')->deleteAll();
}

/**
* Tests the cookie banner visibility and interaction.
*/
public function testCookieBanner() {
$this->drupalGet('/test-page');
$this->assertSession()->pageTextContains('Test Content');
$this->assertSession()->elementExists('css', '.test-footer');

// Get the web driver.
$driver = $this->getSession()->getDriver();

// Check if the driver is an instance of DrupalSelenium2Driver.
if ($driver instanceof Selenium2Driver) {
// Get all cookies from the browser.
$cookies = $driver->getWebDriverSession()->getCookie();

// Extract only the 'name' keys from all the cookies, as we want to check
// if a specific cookie "change-me" exists.
$cookieNames = array_column($cookies, 'name');
$this->assertNotContains(
'change-me',
$cookieNames,
'The cookie "change-me" was found, but it should not exist.',
);
}
else {
$this->fail('The driver is not an instance of Selenium2Driver.');
}

// Assert that the cookie banner is visible and click the accept button.
$this->assertCookieBannerIsVisible();

// Get the new cookies from the browser.
$new_cookies = $driver->getWebDriverSession()->getCookie();

// There should be a new cookie called "change-me".
$cookieNames = array_column($new_cookies, 'name');
$this->assertContains(
'change-me',
$cookieNames,
'The cookie "change-me" was not found after clicking the button.',
);

// Reload the page and assert that the cookie banner is not visible.
$this->drupalGet('/test-page');
$this->assertSession()->pageTextContains('Test Content');
$this->assertCookieBannerNotVisible();
}

/**
* Asserts that the cookie banner is visible.
*/
protected function assertCookieBannerIsVisible() {
// Get the Shadow DOM host and button selectors.
$shadowHostSelector = '.hds-cc__target';
$buttonSelector = '.hds-cc__all-cookies-button';

// Verify that the cookie banner is visible and click the accept button.
$js = <<<JS
const shadowHost = document.querySelector('$shadowHostSelector');
if (!shadowHost) {
throw new Error('Shadow host not found.');
}
const shadowRoot = shadowHost.shadowRoot;
if (!shadowRoot) {
throw new Error('Shadow root is not attached.');
}
const button = shadowRoot.querySelector('$buttonSelector');
if (!button) {
throw new Error('Button not found inside the shadow DOM.');
}
button.click();
JS;

// Execute the JavaScript in the browser context.
$this->getSession()->executeScript($js);
}

/**
* Asserts that the cookie banner is not visible.
*/
protected function assertCookieBannerNotVisible() {
// Get the Shadow DOM host and button selectors.
$shadowHostSelector = '.hds-cc__target';

// Verify that the cookie banner is not visible.
$js = <<<JS
const shadowHost = document.querySelector('$shadowHostSelector');
if (shadowHost) {
throw new Error('Shadow host still found.');
}
JS;
// Execute the JavaScript in the browser context.
$this->getSession()->executeScript($js);
}

}
7 changes: 3 additions & 4 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
<ini name="error_reporting" value="32767"/>
<!-- Do not limit the amount of memory tests take to run. -->
<ini name="memory_limit" value="-1"/>
<env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["chrome", {"browserName":"chrome","chromeOptions":{"w3c": false, "args":["--disable-gpu","--headless", "--no-sandbox"]}}, "http://127.0.0.1:4444"]' />
<env name="DTT_MINK_DRIVER_ARGS" value='["chrome", {"chromeOptions":{"w3c": false }}, "http://chromium:4444"]'/>
<env name="DTT_API_OPTIONS" value='{"socketTimeout": 360, "domWaitTimeout": 3600000}' />
<env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["chrome", {"browserName":"chrome", "goog:chromeOptions":{"w3c": true, "args":["--no-sandbox", "--ignore-certificate-errors", "--allow-insecure-localhost", "--headless", "--dns-prefetch-disable"]}}, "http://chromium:4444"]' />
<env name="DTT_MINK_DRIVER_ARGS" value='["chrome", {"browserName":"chrome", "goog:chromeOptions":{"w3c": true, "args":["--no-sandbox","--ignore-certificate-errors", "--allow-insecure-localhost", "--headless", "--dns-prefetch-disable"]}}, "http://chromium:4444"]'/> <env name="DTT_API_OPTIONS" value='{"socketTimeout": 360, "domWaitTimeout": 3600000}' />
<env name="DTT_API_URL" value="http://chromium:9222"/>
<env name="DTT_BASE_URL" value="http://app:8888"/>
<env name="DTT_BASE_URL" value="https://app"/>
</php>
<testsuites>
<testsuite name="unit">
Expand Down

0 comments on commit f096789

Please sign in to comment.