class ScaffoldTest

Same name in other branches
  1. 9 core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php \Drupal\Tests\Composer\Plugin\Scaffold\Functional\ScaffoldTest
  2. 8.9.x core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php \Drupal\Tests\Composer\Plugin\Scaffold\Functional\ScaffoldTest
  3. 11.x core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php \Drupal\Tests\Composer\Plugin\Scaffold\Functional\ScaffoldTest

Tests Composer Scaffold.

The purpose of this test file is to exercise all of the different kinds of scaffold operations: copy, symlinks, skips and so on.

@group Scaffold

Hierarchy

Expanded class hierarchy of ScaffoldTest

File

core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php, line 22

Namespace

Drupal\Tests\Composer\Plugin\Scaffold\Functional
View source
class ScaffoldTest extends TestCase {
    use AssertUtilsTrait;
    use PhpUnitCompatibilityTrait;
    
    /**
     * The root of this project.
     *
     * Used to substitute this project's base directory into composer.json files
     * so Composer can find it.
     *
     * @var string
     */
    protected $projectRoot;
    
    /**
     * Directory to perform the tests in.
     *
     * @var string
     */
    protected $fixturesDir;
    
    /**
     * The Symfony FileSystem component.
     *
     * @var \Symfony\Component\Filesystem\Filesystem
     */
    protected $fileSystem;
    
    /**
     * The Fixtures object.
     *
     * @var \Drupal\Tests\Composer\Plugin\Scaffold\Fixtures
     */
    protected $fixtures;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        $this->fileSystem = new Filesystem();
        $this->fixtures = new Fixtures();
        $this->fixtures
            ->createIsolatedComposerCacheDir();
        $this->projectRoot = $this->fixtures
            ->projectRoot();
        // The directory used for creating composer projects to test can be
        // configured using the SCAFFOLD_FIXTURE_DIR environment variable. Otherwise
        // a directory will be created in the system's temporary directory.
        $this->fixturesDir = getenv('SCAFFOLD_FIXTURE_DIR');
        if (!$this->fixturesDir) {
            $this->fixturesDir = $this->fixtures
                ->tmpDir($this->name());
        }
    }
    
    /**
     * {@inheritdoc}
     */
    protected function tearDown() : void {
        // Remove any temporary directories et. al. that were created.
        $this->fixtures
            ->tearDown();
    }
    
    /**
     * Creates the System-Under-Test.
     *
     * @param string $fixture_name
     *   The name of the fixture to use from
     *   core/tests/Drupal/Tests/Component/Scaffold/fixtures.
     * @param array $replacements
     *   Key : value mappings for placeholders to replace in composer.json
     *   templates.
     *
     * @return string
     *   The path to the created System-Under-Test.
     */
    protected function createSut($fixture_name, array $replacements = []) {
        $sut = $this->fixturesDir . '/' . $fixture_name;
        // Erase just our sut, to ensure it is clean. Recopy all of the fixtures.
        $this->fileSystem
            ->remove($sut);
        $replacements += [
            'PROJECT_ROOT' => $this->projectRoot,
        ];
        $this->fixtures
            ->cloneFixtureProjects($this->fixturesDir, $replacements);
        return $sut;
    }
    
    /**
     * Creates the system-under-test and runs a scaffold operation on it.
     *
     * @param string $fixture_name
     *   The name of the fixture to use from
     *   core/tests/Drupal/Tests/Component/Scaffold/fixtures.
     * @param bool $is_link
     *   Whether to use symlinks for 'replace' operations.
     * @param bool $relocated_docroot
     *   Whether the named fixture has a relocated document root.
     */
    public function scaffoldSut($fixture_name, $is_link = FALSE, $relocated_docroot = TRUE) {
        $sut = $this->createSut($fixture_name, [
            'SYMLINK' => $is_link ? 'true' : 'false',
        ]);
        // Run composer install to get the dependencies we need to test.
        $this->fixtures
            ->runComposer("install --no-ansi --no-scripts --no-plugins", $sut);
        // Test drupal:scaffold.
        $scaffoldOutput = $this->fixtures
            ->runScaffold($sut);
        // Calculate the docroot directory and assert that our fixture layout
        // matches what was stipulated in $relocated_docroot. Fail fast if
        // the caller provided the wrong value.
        $docroot = $sut;
        if ($relocated_docroot) {
            $docroot .= '/docroot';
            $this->assertFileExists($docroot);
        }
        else {
            $this->assertFileDoesNotExist($sut . '/docroot');
        }
        return new ScaffoldTestResult($docroot, $scaffoldOutput);
    }
    
    /**
     * Data provider for testScaffoldWithExpectedException.
     */
    public static function scaffoldExpectedExceptionTestValues() {
        return [
            [
                'drupal-drupal-missing-scaffold-file',
                'Scaffold file assets/missing-robots-default.txt not found in package fixtures/drupal-drupal-missing-scaffold-file.',
                TRUE,
            ],
            [
                'project-with-empty-scaffold-path',
                'No scaffold file path given for [web-root]/my-error in package fixtures/project-with-empty-scaffold-path',
                FALSE,
            ],
            [
                'project-with-illegal-dir-scaffold',
                'Scaffold file assets in package fixtures/project-with-illegal-dir-scaffold is a directory; only files may be scaffolded',
                FALSE,
            ],
        ];
    }
    
    /**
     * Tests that scaffold files throw when they have bad values.
     *
     * @param string $fixture_name
     *   The name of the fixture to use from
     *   core/tests/Drupal/Tests/Component/Scaffold/fixtures.
     * @param string $expected_exception_message
     *   The expected exception message.
     * @param bool $is_link
     *   Whether or not symlinking should be used.
     *
     * @dataProvider scaffoldExpectedExceptionTestValues
     */
    public function testScaffoldWithExpectedException($fixture_name, $expected_exception_message, $is_link) : void {
        // Test scaffold. Expect an error.
        $this->expectException(\Exception::class);
        $this->expectExceptionMessage($expected_exception_message);
        $this->scaffoldSut($fixture_name, $is_link);
    }
    
    /**
     * Try to scaffold a project that does not scaffold anything.
     */
    public function testEmptyProject() : void {
        $fixture_name = 'empty-fixture';
        $result = $this->scaffoldSut($fixture_name, FALSE, FALSE);
        $this->assertStringContainsString('Nothing scaffolded because no packages are allowed in the top-level composer.json file', $result->scaffoldOutput());
    }
    
    /**
     * Try to scaffold a project that allows a project with no scaffold files.
     */
    public function testProjectThatScaffoldsEmptyProject() : void {
        $fixture_name = 'project-allowing-empty-fixture';
        $result = $this->scaffoldSut($fixture_name, FALSE, FALSE);
        $this->assertStringContainsString('The allowed package fixtures/empty-fixture does not provide a file mapping for Composer Scaffold', $result->scaffoldOutput());
        $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), FALSE);
        $this->assertAutoloadFileCorrect($result->docroot());
    }
    public static function scaffoldOverridingSettingsExcludingHtaccessValues() {
        return [
            [
                'drupal-composer-drupal-project',
                TRUE,
                TRUE,
            ],
            [
                'drupal-drupal',
                FALSE,
                FALSE,
            ],
        ];
    }
    
    /**
     * Asserts that the drupal/assets scaffold files correct for sut.
     *
     * @param string $fixture_name
     *   The name of the fixture to use from
     *   core/tests/Drupal/Tests/Component/Scaffold/fixtures.
     * @param bool $is_link
     *   Whether to use symlinks for 'replace' operations.
     * @param bool $relocated_docroot
     *   Whether the named fixture has a relocated document root.
     *
     * @dataProvider scaffoldOverridingSettingsExcludingHtaccessValues
     */
    public function testScaffoldOverridingSettingsExcludingHtaccess($fixture_name, $is_link, $relocated_docroot) : void {
        $result = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot);
        $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), $is_link);
        $this->assertAutoloadFileCorrect($result->docroot(), $relocated_docroot);
        $this->assertDefaultSettingsFromScaffoldOverride($result->docroot(), $is_link);
        $this->assertHtaccessExcluded($result->docroot());
    }
    
    /**
     * Asserts that the appropriate file was replaced.
     *
     * Check the drupal/drupal-based project to confirm that the expected file was
     * replaced, and that files that were not supposed to be replaced remain
     * unchanged.
     */
    public function testDrupalDrupalFileWasReplaced() : void {
        $fixture_name = 'drupal-drupal-test-overwrite';
        $result = $this->scaffoldSut($fixture_name, FALSE, FALSE);
        $this->assertScaffoldedFile($result->docroot() . '/replace-me.txt', FALSE, 'from assets that replaces file');
        $this->assertScaffoldedFile($result->docroot() . '/keep-me.txt', FALSE, 'File in drupal-drupal-test-overwrite that is not replaced');
        $this->assertScaffoldedFile($result->docroot() . '/make-me.txt', FALSE, 'from assets that replaces file');
        $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), FALSE);
        $this->assertAutoloadFileCorrect($result->docroot());
        $this->assertScaffoldedFile($result->docroot() . '/robots.txt', FALSE, $fixture_name);
    }
    
    /**
     * Provides test values for testDrupalDrupalFileWasAppended.
     */
    public static function scaffoldAppendTestValues() : array {
        return array_merge(static::scaffoldAppendTestValuesToPermute(FALSE), static::scaffoldAppendTestValuesToPermute(TRUE), [
            [
                'drupal-drupal-append-settings',
                FALSE,
                'sites/default/settings.php',
                '<?php

// Default settings.php contents

include __DIR__ . "/settings-custom-additions.php";',
                'NOTICE Creating a new file at [web-root]/sites/default/settings.php. Examine the contents and ensure that it came out correctly.',
            ],
        ]);
    }
    
    /**
     * Tests values to run both with $is_link FALSE and $is_link TRUE.
     *
     * @param bool $is_link
     *   Whether or not symlinking should be used.
     */
    protected static function scaffoldAppendTestValuesToPermute($is_link) {
        return [
            [
                'drupal-drupal-test-append',
                $is_link,
                'robots.txt',
                '# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-test-append composer.json fixture.
# This content is prepended to the top of the existing robots.txt fixture.
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

# Test version of robots.txt from drupal/core.

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# This content is appended to the bottom of the existing robots.txt fixture.
# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-test-append composer.json fixture.
',
                'Prepend to [web-root]/robots.txt from assets/prepend-to-robots.txt',
            ],
            [
                'drupal-drupal-append-to-append',
                $is_link,
                'robots.txt',
                '# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-append-to-append composer.json fixture.
# This content is prepended to the top of the existing robots.txt fixture.
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

# Test version of robots.txt from drupal/core.

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# This content is appended to the bottom of the existing robots.txt fixture.
# robots.txt fixture scaffolded from "file-mappings" in profile-with-append composer.json fixture.

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# This content is appended to the bottom of the existing robots.txt fixture.
# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-append-to-append composer.json fixture.',
                'Append to [web-root]/robots.txt from assets/append-to-robots.txt',
            ],
        ];
    }
    
    /**
     * Tests a fixture where the robots.txt file is prepended / appended to.
     *
     * @param string $fixture_name
     *   The name of the fixture to use from
     *   core/tests/Drupal/Tests/Component/Scaffold/fixtures.
     * @param bool $is_link
     *   Whether or not symlinking should be used.
     * @param string $scaffold_file_path
     *   Relative path to the scaffold file target we are testing.
     * @param string $scaffold_file_contents
     *   A string expected to be contained inside the scaffold file we are testing.
     * @param string $scaffoldOutputContains
     *   A string expected to be contained in the scaffold command output.
     *
     * @dataProvider scaffoldAppendTestValues
     */
    public function testDrupalDrupalFileWasAppended(string $fixture_name, bool $is_link, string $scaffold_file_path, string $scaffold_file_contents, string $scaffoldOutputContains) : void {
        $result = $this->scaffoldSut($fixture_name, $is_link, FALSE);
        $this->assertStringContainsString($scaffoldOutputContains, $result->scaffoldOutput());
        $this->assertScaffoldedFile($result->docroot() . '/' . $scaffold_file_path, FALSE, $scaffold_file_contents);
        $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), $is_link);
        $this->assertAutoloadFileCorrect($result->docroot());
    }
    
    /**
     * Asserts that the default settings file was overridden by the test.
     *
     * @param string $docroot
     *   The path to the System-under-Test's docroot.
     * @param bool $is_link
     *   Whether or not symlinking is used.
     *
     * @internal
     */
    protected function assertDefaultSettingsFromScaffoldOverride(string $docroot, bool $is_link) : void {
        $this->assertScaffoldedFile($docroot . '/sites/default/default.settings.php', $is_link, 'scaffolded from the scaffold-override-fixture');
    }
    
    /**
     * Asserts that the .htaccess file was excluded by the test.
     *
     * @param string $docroot
     *   The path to the System-under-Test's docroot.
     *
     * @internal
     */
    protected function assertHtaccessExcluded(string $docroot) : void {
        // Ensure that the .htaccess.txt file was not written, as our
        // top-level composer.json excludes it from the files to scaffold.
        $this->assertFileDoesNotExist($docroot . '/.htaccess');
    }
    
    /**
     * Asserts that the scaffold files from drupal/assets are placed as expected.
     *
     * This tests that all assets from drupal/assets were scaffolded, save
     * for .htaccess, robots.txt and default.settings.php, which are scaffolded
     * in different ways in different tests.
     *
     * @param string $docroot
     *   The path to the System-under-Test's docroot.
     * @param bool $is_link
     *   Whether or not symlinking is used.
     *
     * @internal
     */
    protected function assertCommonDrupalAssetsWereScaffolded(string $docroot, bool $is_link) : void {
        // Assert scaffold files are written in the correct locations.
        $this->assertScaffoldedFile($docroot . '/.csslintrc', $is_link, 'Test version of .csslintrc from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/.editorconfig', $is_link, 'Test version of .editorconfig from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/.eslintignore', $is_link, 'Test version of .eslintignore from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/.eslintrc.json', $is_link, 'Test version of .eslintrc.json from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/.gitattributes', $is_link, 'Test version of .gitattributes from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/.ht.router.php', $is_link, 'Test version of .ht.router.php from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/sites/default/default.services.yml', $is_link, 'Test version of default.services.yml from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/sites/example.settings.local.php', $is_link, 'Test version of example.settings.local.php from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/sites/example.sites.php', $is_link, 'Test version of example.sites.php from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/index.php', $is_link, 'Test version of index.php from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/update.php', $is_link, 'Test version of update.php from drupal/core.');
        $this->assertScaffoldedFile($docroot . '/web.config', $is_link, 'Test version of web.config from drupal/core.');
    }
    
    /**
     * Assert that the autoload file was scaffolded and contains correct path.
     *
     * @param string $docroot
     *   Location of the doc root, where autoload.php should be written.
     * @param bool $relocated_docroot
     *   Whether the document root is relocated or now.
     *
     * @internal
     */
    protected function assertAutoloadFileCorrect(string $docroot, bool $relocated_docroot = FALSE) : void {
        $autoload_path = $docroot . '/autoload.php';
        // Ensure that the autoload.php file was written.
        $this->assertFileExists($autoload_path);
        $contents = file_get_contents($autoload_path);
        $expected = "return require __DIR__ . '/vendor/autoload.php';";
        if ($relocated_docroot) {
            $expected = "return require __DIR__ . '/../vendor/autoload.php';";
        }
        $this->assertStringContainsString($expected, $contents);
    }

}

Members

Title Sort descending Modifiers Object type Summary
AssertUtilsTrait::assertScaffoldedFile protected function Asserts that a given file exists and is/is not a symlink.
AssertUtilsTrait::assertScaffoldedFileDoesNotContain protected function Asserts that a file does not exist or exists and does not contain a value.
PhpUnitWarnings::$deprecationWarnings private static property Deprecation warnings from PHPUnit to raise with @trigger_error().
PhpUnitWarnings::addWarning public function Converts PHPUnit deprecation warnings to E_USER_DEPRECATED.
ScaffoldTest::$fileSystem protected property The Symfony FileSystem component.
ScaffoldTest::$fixtures protected property The Fixtures object.
ScaffoldTest::$fixturesDir protected property Directory to perform the tests in.
ScaffoldTest::$projectRoot protected property The root of this project.
ScaffoldTest::assertAutoloadFileCorrect protected function Assert that the autoload file was scaffolded and contains correct path.
ScaffoldTest::assertCommonDrupalAssetsWereScaffolded protected function Asserts that the scaffold files from drupal/assets are placed as expected.
ScaffoldTest::assertDefaultSettingsFromScaffoldOverride protected function Asserts that the default settings file was overridden by the test.
ScaffoldTest::assertHtaccessExcluded protected function Asserts that the .htaccess file was excluded by the test.
ScaffoldTest::createSut protected function Creates the System-Under-Test.
ScaffoldTest::scaffoldAppendTestValues public static function Provides test values for testDrupalDrupalFileWasAppended.
ScaffoldTest::scaffoldAppendTestValuesToPermute protected static function Tests values to run both with $is_link FALSE and $is_link TRUE.
ScaffoldTest::scaffoldExpectedExceptionTestValues public static function Data provider for testScaffoldWithExpectedException.
ScaffoldTest::scaffoldOverridingSettingsExcludingHtaccessValues public static function
ScaffoldTest::scaffoldSut public function Creates the system-under-test and runs a scaffold operation on it.
ScaffoldTest::setUp protected function
ScaffoldTest::tearDown protected function
ScaffoldTest::testDrupalDrupalFileWasAppended public function Tests a fixture where the robots.txt file is prepended / appended to.
ScaffoldTest::testDrupalDrupalFileWasReplaced public function Asserts that the appropriate file was replaced.
ScaffoldTest::testEmptyProject public function Try to scaffold a project that does not scaffold anything.
ScaffoldTest::testProjectThatScaffoldsEmptyProject public function Try to scaffold a project that allows a project with no scaffold files.
ScaffoldTest::testScaffoldOverridingSettingsExcludingHtaccess public function Asserts that the drupal/assets scaffold files correct for sut.
ScaffoldTest::testScaffoldWithExpectedException public function Tests that scaffold files throw when they have bad values.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.