function ComposerProjectTemplatesTest::testTemplateCreateProject

Same name in other branches
  1. 9 core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php \Drupal\BuildTests\Composer\Template\ComposerProjectTemplatesTest::testTemplateCreateProject()
  2. 8.9.x core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php \Drupal\BuildTests\Composer\Template\ComposerProjectTemplatesTest::testTemplateCreateProject()
  3. 10 core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php \Drupal\BuildTests\Composer\Template\ComposerProjectTemplatesTest::testTemplateCreateProject()

@dataProvider provideTemplateCreateProject

File

core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php, line 177

Class

ComposerProjectTemplatesTest
Demonstrate that Composer project templates can be built as patched.

Namespace

Drupal\BuildTests\Composer\Template

Code

public function testTemplateCreateProject($project, $package_dir, $docroot_dir) : void {
    // Make a working COMPOSER_HOME directory for setting global composer config
    $composer_home = $this->getWorkspaceDirectory() . '/composer-home';
    mkdir($composer_home);
    // Create an empty global composer.json file, just to avoid warnings.
    file_put_contents("{$composer_home}/composer.json", '{}');
    // Disable packagist globally (but only in our own custom COMPOSER_HOME).
    // It is necessary to do this globally rather than in our SUT composer.json
    // in order to ensure that Packagist is disabled during the
    // `composer create-project` command.
    $this->executeCommand("COMPOSER_HOME={$composer_home} composer config --no-interaction --global repo.packagist false");
    $this->assertCommandSuccessful();
    // Create a "Composer"-type repository containing one entry for every
    // package in the vendor directory.
    $vendor_packages_path = $this->getWorkspaceDirectory() . '/vendor_packages/packages.json';
    $this->makeVendorPackage($vendor_packages_path);
    // Make a copy of the code to alter in the workspace directory.
    $this->copyCodebase();
    // Tests are typically run on "-dev" versions, but we want to simulate
    // running them on a tagged release at the same stability as specified in
    // static::MINIMUM_STABILITY, in order to verify that everything will work
    // if/when we make such a release.
    $simulated_core_version = \Drupal::VERSION;
    $simulated_core_version_suffix = static::MINIMUM_STABILITY === 'stable' ? '' : '-' . static::MINIMUM_STABILITY . '99';
    $simulated_core_version = str_replace('-dev', $simulated_core_version_suffix, $simulated_core_version);
    Composer::setDrupalVersion($this->getWorkspaceDirectory(), $simulated_core_version);
    $this->assertDrupalVersion($simulated_core_version, $this->getWorkspaceDirectory());
    // Remove the packages.drupal.org entry (and any other custom repository)
    // from the SUT's repositories section. There is no way to do this via
    // `composer config --unset`, so we read and rewrite composer.json.
    $composer_json_path = $this->getWorkspaceDirectory() . "/{$package_dir}/composer.json";
    $composer_json = json_decode(file_get_contents($composer_json_path), TRUE);
    unset($composer_json['repositories']);
    $json = json_encode($composer_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    file_put_contents($composer_json_path, $json);
    // Set up the template to use our path repos. Inclusion of metapackages is
    // reported differently, so we load up a separate set for them.
    $metapackage_path_repos = $this->getPathReposForType($this->getWorkspaceDirectory(), 'Metapackage');
    $this->assertArrayHasKey('drupal/core-recommended', $metapackage_path_repos);
    $path_repos = array_merge($metapackage_path_repos, $this->getPathReposForType($this->getWorkspaceDirectory(), 'Plugin'));
    // Always add drupal/core as a path repo.
    $path_repos['drupal/core'] = $this->getWorkspaceDirectory() . '/core';
    foreach ($path_repos as $name => $path) {
        $this->executeCommand("composer config --no-interaction repositories.{$name} path {$path}", $package_dir);
        $this->assertCommandSuccessful();
    }
    // Change drupal/core-recommended to require the simulated version of
    // drupal/core.
    $core_recommended_dir = 'composer/Metapackage/CoreRecommended';
    $this->executeCommand("composer remove --no-interaction drupal/core --no-update", $core_recommended_dir);
    $this->assertCommandSuccessful();
    $this->executeCommand("composer require --no-interaction drupal/core:^{$simulated_core_version} --no-update", $core_recommended_dir);
    $this->assertCommandSuccessful();
    // Add our vendor package repository to our SUT's repositories section.
    // Call it "local" (although the name does not matter).
    $this->executeCommand("composer config --no-interaction repositories.local composer file://" . $vendor_packages_path, $package_dir);
    $this->assertCommandSuccessful();
    $repository_path = $this->getWorkspaceDirectory() . '/test_repository/packages.json';
    $this->makeTestPackage($repository_path, $simulated_core_version);
    $installed_composer_json = $this->getWorkspaceDirectory() . '/test_project/composer.json';
    $autoloader = $this->getWorkspaceDirectory() . '/test_project' . $docroot_dir . '/autoload.php';
    $recipes_dir = $this->getWorkspaceDirectory() . '/test_project/recipes';
    $this->assertFileDoesNotExist($autoloader);
    $this->assertDirectoryDoesNotExist($recipes_dir);
    $this->executeCommand("COMPOSER_HOME={$composer_home} COMPOSER_ROOT_VERSION={$simulated_core_version} composer create-project --no-ansi {$project} test_project {$simulated_core_version} -vvv --repository {$repository_path}");
    $this->assertCommandSuccessful();
    // Check the output of the project creation for the absence of warnings
    // about any non-allowed composer plugins.
    // Note: There are different warnings for disallowed composer plugins
    // depending on running in non-interactive mode or not. It seems the Drupal
    // CI environment always forces composer commands to run in the
    // non-interactive mode. The only thing these messages have in common is the
    // following string.
    $this->assertErrorOutputNotContains('See https://getcomposer.org/allow-plugins');
    // Ensure we used the project from our codebase.
    $this->assertErrorOutputContains("Installing {$project} ({$simulated_core_version}): Symlinking from {$package_dir}");
    // Ensure that we used drupal/core from our codebase. This probably means
    // that drupal/core-recommended was added successfully by the project.
    $this->assertErrorOutputContains("Installing drupal/core ({$simulated_core_version}): Symlinking from");
    // Verify that there is an autoloader. This is written by the scaffold
    // plugin, so its existence assures us that scaffolding happened.
    $this->assertFileExists($autoloader);
    // Verify recipes directory exists.
    $this->assertDirectoryExists($recipes_dir);
    // Verify that the minimum stability in the installed composer.json file
    // matches the stability of the simulated core version.
    $this->assertFileExists($installed_composer_json);
    $composer_json_contents = file_get_contents($installed_composer_json);
    $this->assertStringContainsString('"minimum-stability": "' . static::MINIMUM_STABILITY . '"', $composer_json_contents);
    // In order to verify that Composer used the path repos for our project, we
    // have to get the requirements from the project composer.json so we can
    // reconcile our expectations.
    $template_json_file = $this->getWorkspaceDirectory() . '/' . $package_dir . '/composer.json';
    $this->assertFileExists($template_json_file);
    $json_file = new JsonFile($template_json_file);
    $template_json = $json_file->read();
    // Get the require and require-dev information, and ensure that our
    // requirements are not erroneously empty.
    $this->assertNotEmpty($require = array_merge($template_json['require'] ?? [], $template_json['require-dev'] ?? []));
    // Verify that path repo packages were installed.
    $path_repos = array_keys($path_repos);
    foreach (array_keys($require) as $package_name) {
        if (in_array($package_name, $path_repos)) {
            // Metapackages do not report that they were installed as symlinks, but
            // we still must check that their installed version matches
            // COMPOSER_CORE_VERSION.
            if (array_key_exists($package_name, $metapackage_path_repos)) {
                $this->assertErrorOutputContains("Installing {$package_name} ({$simulated_core_version})");
            }
            else {
                $this->assertErrorOutputContains("Installing {$package_name} ({$simulated_core_version}): Symlinking from");
            }
        }
    }
}

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