Skip to content
Snippets Groups Projects
Verified Commit 0bfc9722 authored by Dave Long's avatar Dave Long
Browse files

Issue #3453341 by mondrake, alexpott, longwave: Bootstrap HtmlOutputLogger from phpunit.xml

parent 0ac3d2a0
No related branches found
No related tags found
28 merge requests!12227Issue #3181946 by jonmcl, mglaman,!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!9470[10.3.x-only-DO-NOT-MERGE]: #3331771 Fix file_get_contents(): Passing null to parameter,!8736Update the Documention As per the Function uses.,!8528Issue #3456871 by Tim Bozeman: Support NULL services,!8513Issue #3453786: DefaultSelection should document why values for target_bundles NULL and [] behave as they do,!8126Added escape fucntionality on admintoolbar close icon,!5423Draft: Resolve #3329907 "Test2",!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3478Issue #3337882: Deleted menus are not removed from content type config,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2062Issue #3246454: Add weekly granularity to views date sort,!1105Issue #3025039: New non translatable field on translatable content throws error,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!213Issue #2906496: Give Media a menu item under Content
Pipeline #209320 canceled
Pipeline: drupal

#209325

    ......@@ -27,15 +27,6 @@
    <env name="SIMPLETEST_BASE_URL" value=""/>
    <!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/database_name#table_prefix -->
    <env name="SIMPLETEST_DB" value=""/>
    <!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output -->
    <env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/>
    <!-- To avoid overcrowding the output in CI environments, browser tests
    will not print the individual links in the test run report by default.
    The output in Drupal testing environment is saved as an artifact that
    can be browsed or downloaded from Gitlab. However, if you need to
    print the individual links locally you can set the
    BROWSERTEST_OUTPUT_VERBOSE environment variable to "true". -->
    <!-- <env name="BROWSERTEST_OUTPUT_VERBOSE" value="true"/> -->
    <!-- By default, browser tests will output links that use the base URL set
    in SIMPLETEST_BASE_URL. However, if your SIMPLETEST_BASE_URL is an internal
    path (such as may be the case in a virtual or Docker-based environment),
    ......@@ -65,6 +56,25 @@
    <!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["chrome", { "goog:chromeOptions": { "w3c": false } }, "http://localhost:4444/wd/hub"]' For using the Firefox browser, replace "chrome" with "firefox" -->
    <env name="MINK_DRIVER_ARGS_WEBDRIVER" value=""/>
    </php>
    <extensions>
    <!-- Functional tests HTML output logging. -->
    <bootstrap class="Drupal\TestTools\Extension\HtmlLogging\HtmlOutputLogger">
    <!-- The directory where the browser output will be stored. If a relative
    path is specified, it will be relative to the current working directory
    of the process running the PHPUnit CLI. In CI environments, this can be
    overridden by the value set for the "BROWSERTEST_OUTPUT_DIRECTORY"
    environment variable.
    -->
    <parameter name="outputDirectory" value="sites/simpletest/browser_output"/>
    <!-- By default browser tests print the individual links in the test run
    report. To avoid overcrowding the output in CI environments, you can
    set the "verbose" parameter or the "BROWSERTEST_OUTPUT_VERBOSE"
    environment variable to "false". In GitLabCI, the output is saved
    anyway as an artifact that can be browsed or downloaded from Gitlab.
    -->
    <parameter name="verbose" value="true"/>
    </bootstrap>
    </extensions>
    <testsuites>
    <testsuite name="unit">
    <directory>tests/Drupal/Tests</directory>
    ......
    ......@@ -4,6 +4,7 @@
    namespace Drupal\FunctionalTests\Test;
    use Drupal\Core\File\FileExists;
    use Drupal\Tests\BrowserTestBase;
    use Symfony\Component\Process\Process;
    ......@@ -28,13 +29,36 @@ class FunctionalTestDebugHtmlOutputTest extends BrowserTestBase {
    public function testFunctionalTestDebugHtmlOutput(): void {
    $command = [
    'vendor/bin/phpunit',
    'core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputHelperTest.php',
    ];
    // Test with the default settings in phpunit.xml.dist.
    $config = [
    '--configuration',
    'core',
    'core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputHelperTest.php',
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    $process->run();
    $this->assertEquals(0, $process->getExitCode(),
    'COMMAND: ' . $process->getCommandLine() . "\n" .
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output was generated.', $process->getOutput());
    $this->assertStringContainsString('Drupal_FunctionalTests_Test_FunctionalTestDebugHtmlOutputHelperTest', $process->getOutput());
    // Test with the default output directory, specified by BROWSERTEST_OUTPUT_DIRECTORY.
    $process = new Process($command);
    // Test without verbose output, set in xml.
    $alteredConfigFile = $this->getAlteredPhpunitXmlConfigurationFile(
    '<parameter name="verbose" value="true"/>',
    '<parameter name="verbose" value="false"/>',
    );
    $config = [
    '--configuration',
    $alteredConfigFile,
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    ......@@ -44,24 +68,57 @@ public function testFunctionalTestDebugHtmlOutput(): void {
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertMatchesRegularExpression('/HTML output was generated, \d+ page\(s\)\./m', $process->getOutput());
    unlink($alteredConfigFile);
    // Test with verbose output.
    $process = new Process($command);
    // Test without verbose output, overridden by BROWSERTEST_OUTPUT_VERBOSE
    // environment variable.
    $config = [
    '--configuration',
    'core',
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    $process->run(NULL, [
    'BROWSERTEST_OUTPUT_VERBOSE' => '1',
    'BROWSERTEST_OUTPUT_VERBOSE' => 'false',
    ]);
    $this->assertEquals(0, $process->getExitCode(),
    'COMMAND: ' . $process->getCommandLine() . "\n" .
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output was generated.', $process->getOutput());
    $this->assertStringContainsString('Drupal_FunctionalTests_Test_FunctionalTestDebugHtmlOutputHelperTest', $process->getOutput());
    $this->assertMatchesRegularExpression('/HTML output was generated, \d+ page\(s\)\./m', $process->getOutput());
    // Test with a wrong output directory.
    $process = new Process($command);
    // Test with a wrong output directory, set in xml.
    $alteredConfigFile = $this->getAlteredPhpunitXmlConfigurationFile(
    '<parameter name="outputDirectory" value="sites/simpletest/browser_output"/>',
    '<parameter name="outputDirectory" value="can_we_assume_that_a_subdirectory_with_this_name_does_not_exist"/>',
    );
    $config = [
    '--configuration',
    $alteredConfigFile,
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    $process->run(NULL, [
    'BROWSERTEST_OUTPUT_DIRECTORY' => FALSE,
    ]);
    $this->assertEquals(0, $process->getExitCode(),
    'COMMAND: ' . $process->getCommandLine() . "\n" .
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output directory can_we_assume_that_a_subdirectory_with_this_name_does_not_exist is not a writable directory.', $process->getOutput());
    unlink($alteredConfigFile);
    // Test with a wrong output directory, overridden by
    // BROWSERTEST_OUTPUT_DIRECTORY environment variable.
    $config = [
    '--configuration',
    'core',
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    ......@@ -73,6 +130,57 @@ public function testFunctionalTestDebugHtmlOutput(): void {
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output directory can_we_assume_that_a_subdirectory_with_this_name_does_not_exist is not a writable directory.', $process->getOutput());
    // Test disabling by setting BROWSERTEST_OUTPUT_DIRECTORY = ''.
    $config = [
    '--configuration',
    'core',
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    $process->run(NULL, [
    'BROWSERTEST_OUTPUT_DIRECTORY' => '',
    ]);
    $this->assertEquals(0, $process->getExitCode(),
    'COMMAND: ' . $process->getCommandLine() . "\n" .
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output disabled by BROWSERTEST_OUTPUT_DIRECTORY = \'\'.', $process->getOutput());
    // Test missing 'outputDirectory' parameter.
    $alteredConfigFile = $this->getAlteredPhpunitXmlConfigurationFile(
    '<parameter name="outputDirectory" value="sites/simpletest/browser_output"/>',
    '',
    );
    $config = [
    '--configuration',
    $alteredConfigFile,
    ];
    $process = new Process(array_merge($command, $config));
    $process->setWorkingDirectory($this->root)
    ->setTimeout(300)
    ->setIdleTimeout(300);
    $process->run(NULL, [
    'BROWSERTEST_OUTPUT_DIRECTORY' => FALSE,
    ]);
    $this->assertEquals(0, $process->getExitCode(),
    'COMMAND: ' . $process->getCommandLine() . "\n" .
    'OUTPUT: ' . $process->getOutput() . "\n" .
    'ERROR: ' . $process->getErrorOutput() . "\n");
    $this->assertStringContainsString('HTML output directory not specified.', $process->getOutput());
    unlink($alteredConfigFile);
    }
    private function getAlteredPhpunitXmlConfigurationFile(array|string $search, array|string $replace): string {
    $fileSystem = \Drupal::service('file_system');
    $copiedConfigFile = $fileSystem->tempnam($this->root . \DIRECTORY_SEPARATOR . 'core', 'pux');
    $fileSystem->copy($this->root . \DIRECTORY_SEPARATOR . 'core' . \DIRECTORY_SEPARATOR . 'phpunit.xml.dist', $copiedConfigFile, FileExists::Replace);
    $content = file_get_contents($copiedConfigFile);
    $content = str_replace($search, $replace, $content);
    file_put_contents($copiedConfigFile, $content);
    return $fileSystem->realpath($copiedConfigFile);
    }
    }
    ......@@ -4,21 +4,24 @@
    namespace Drupal\TestTools\Extension\HtmlLogging;
    use PHPUnit\Event\Facade;
    use PHPUnit\Event\TestRunner\Finished as TestRunnerFinished;
    use PHPUnit\Event\TestRunner\Started as TestRunnerStarted;
    use PHPUnit\Runner\Extension\Extension;
    use PHPUnit\Runner\Extension\Facade;
    use PHPUnit\Runner\Extension\ParameterCollection;
    use PHPUnit\TextUI\Configuration\Configuration;
    /**
    * Drupal's extension for providing HTML output results for functional tests.
    *
    * @internal
    */
    final class HtmlOutputLogger {
    final class HtmlOutputLogger implements Extension {
    /**
    * The singleton instance.
    * The status of the extension.
    */
    private static ?self $instance = NULL;
    private bool $enabled = FALSE;
    /**
    * A file with list of links to HTML pages generated.
    ......@@ -26,53 +29,66 @@ final class HtmlOutputLogger {
    private ?string $browserOutputFile = NULL;
    /**
    * @throws \PHPUnit\Event\EventFacadeIsSealedException
    * @throws \PHPUnit\Util\Exception
    * @throws \PHPUnit\Event\UnknownSubscriberTypeException
    * @throws \RuntimeException
    * A file with list of links to HTML pages generated.
    */
    private function __construct(
    private readonly string $outputDirectory,
    private readonly bool $outputVerbose,
    private readonly Facade $facade,
    ) {
    $this->facade->registerSubscriber(new TestRunnerStartedSubscriber($this));
    $this->facade->registerSubscriber(new TestRunnerFinishedSubscriber($this));
    }
    private string $outputDirectory;
    /**
    * Initializes the extension.
    * Verbosity of the final report.
    *
    * @param string $outputDirectory
    * The directory where the HTML pages should be generated.
    * @param bool $outputVerbose
    * If TRUE, a list of links generated will be output at the end of the test
    * run; if FALSE, only a summary with the count of pages generated.
    *
    * @throws \PHPUnit\Event\EventFacadeIsSealedException
    * @throws \PHPUnit\Util\Exception
    * @throws \PHPUnit\Event\UnknownSubscriberTypeException
    * @throws \RuntimeException
    */
    public static function init(string $outputDirectory, bool $outputVerbose): void {
    if (self::$instance === NULL) {
    $realDirectory = realpath($outputDirectory);
    private bool $outputVerbose;
    /**
    * {@inheritdoc}
    */
    public function bootstrap(
    Configuration $configuration,
    Facade $facade,
    ParameterCollection $parameters,
    ): void {
    // Determine output directory.
    $envDirectory = getenv('BROWSERTEST_OUTPUT_DIRECTORY');
    if ($envDirectory === "") {
    print "HTML output disabled by BROWSERTEST_OUTPUT_DIRECTORY = ''.\n\n";
    return;
    }
    elseif ($envDirectory !== FALSE) {
    $directory = $envDirectory;
    }
    elseif ($parameters->has('outputDirectory')) {
    $directory = $parameters->get('outputDirectory');
    }
    else {
    print "HTML output directory not specified.\n\n";
    return;
    }
    $realDirectory = realpath($directory);
    if ($realDirectory === FALSE || !is_dir($realDirectory) || !is_writable($realDirectory)) {
    print "HTML output directory {$outputDirectory} is not a writable directory.\n\n";
    print "HTML output directory {$directory} is not a writable directory.\n\n";
    return;
    }
    self::$instance = new self($realDirectory, $outputVerbose, Facade::instance());
    $this->outputDirectory = $realDirectory;
    // Determine output verbosity.
    $envVerbose = getenv('BROWSERTEST_OUTPUT_VERBOSE');
    if ($envVerbose !== FALSE) {
    $verbose = $envVerbose;
    }
    elseif ($parameters->has('verbose')) {
    $verbose = $parameters->get('verbose');
    }
    else {
    $verbose = FALSE;
    }
    $this->outputVerbose = filter_var($verbose, \FILTER_VALIDATE_BOOLEAN);
    /**
    * Determines if the extension is enabled.
    *
    * @return bool
    * TRUE if enabled, FALSE if disabled.
    */
    public static function isEnabled(): bool {
    return self::$instance !== NULL;
    $facade->registerSubscriber(new TestRunnerStartedSubscriber($this));
    $facade->registerSubscriber(new TestRunnerFinishedSubscriber($this));
    $this->enabled = TRUE;
    }
    /**
    ......@@ -84,11 +100,10 @@ public static function isEnabled(): bool {
    * @throws \RuntimeException
    */
    public static function log(string $logEntry): void {
    if (!self::isEnabled()) {
    $browserOutputFile = getenv('BROWSERTEST_OUTPUT_FILE');
    if ($browserOutputFile === FALSE) {
    throw new \RuntimeException("HTML output is not enabled");
    }
    $browserOutputFile = getenv('BROWSERTEST_OUTPUT_FILE');
    file_put_contents($browserOutputFile, $logEntry . "\n", FILE_APPEND);
    }
    ......@@ -96,7 +111,7 @@ public static function log(string $logEntry): void {
    * Empties the list of the HTML output created during the test run.
    */
    public function testRunnerStarted(TestRunnerStarted $event): void {
    if (!self::isEnabled()) {
    if (!$this->enabled) {
    throw new \RuntimeException("HTML output is not enabled");
    }
    ......@@ -118,7 +133,7 @@ public function testRunnerStarted(TestRunnerStarted $event): void {
    * Prints the list of HTML output generated during the test.
    */
    public function testRunnerFinished(TestRunnerFinished $event): void {
    if (!self::isEnabled()) {
    if (!$this->enabled) {
    throw new \RuntimeException("HTML output is not enabled");
    }
    ......
    ......@@ -125,7 +125,8 @@ protected function htmlOutput($message = NULL) {
    * Creates the directory to store browser output.
    */
    protected function initBrowserOutputFile() {
    $this->htmlOutputEnabled = HtmlOutputLogger::isEnabled();
    $browserOutputFile = getenv('BROWSERTEST_OUTPUT_FILE');
    $this->htmlOutputEnabled = $browserOutputFile !== FALSE;
    $this->htmlOutputBaseUrl = getenv('BROWSERTEST_OUTPUT_BASE_URL') ?: $GLOBALS['base_url'];
    if ($this->htmlOutputEnabled) {
    $this->htmlOutputClassName = str_replace("\\", "_", static::class);
    ......
    ......@@ -29,9 +29,6 @@ Settings to change in this file:
    * SIMPLETEST_DB: The URL of your Drupal database
    * The bootstrap attribute of the top-level phpunit tag, to take into account
    the location of the file
    * BROWSERTEST_OUTPUT_DIRECTORY: Set to sites/simpletest/browser_output;
    you will also want to uncomment the printerClass attribute of the
    top-level phpunit tag.
    ### Additional setup for JavaScript tests
    ......
    ......@@ -9,7 +9,6 @@
    use Drupal\TestTools\ErrorHandler\BootstrapErrorHandler;
    use Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler;
    use Drupal\TestTools\Extension\HtmlLogging\HtmlOutputLogger;
    use PHPUnit\Runner\ErrorHandler as PhpUnitErrorHandler;
    use Symfony\Component\ErrorHandler\DebugClassLoader;
    ......@@ -177,9 +176,3 @@ class_alias('\Drupal\Tests\DocumentElement', '\Behat\Mink\Element\DocumentElemen
    // changes.
    DebugClassLoader::enable();
    }
    // Functional tests HTML output logging.
    $browserTestOutputDirectory = getenv('BROWSERTEST_OUTPUT_DIRECTORY');
    if ($browserTestOutputDirectory !== FALSE) {
    HtmlOutputLogger::init($browserTestOutputDirectory, (bool) getenv('BROWSERTEST_OUTPUT_VERBOSE') ?? FALSE);
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please to comment