InstallerTestBase.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. namespace Drupal\FunctionalTests\Installer;
  3. use Drupal\Core\DrupalKernel;
  4. use Drupal\Core\Language\Language;
  5. use Drupal\Core\Session\UserSession;
  6. use Drupal\Core\Site\Settings;
  7. use Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware;
  8. use Drupal\Tests\BrowserTestBase;
  9. use Drupal\Tests\RequirementsPageTrait;
  10. use GuzzleHttp\HandlerStack;
  11. use Symfony\Component\DependencyInjection\ContainerBuilder;
  12. use Symfony\Component\DependencyInjection\Reference;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use Symfony\Component\HttpFoundation\RequestStack;
  15. /**
  16. * Base class for testing the interactive installer.
  17. */
  18. abstract class InstallerTestBase extends BrowserTestBase {
  19. use RequirementsPageTrait;
  20. /**
  21. * Custom settings.php values to write for a test run.
  22. *
  23. * @var array
  24. * An array of settings to write out, in the format expected by
  25. * drupal_rewrite_settings().
  26. */
  27. protected $settings = [];
  28. /**
  29. * The language code in which to install Drupal.
  30. *
  31. * @var string
  32. */
  33. protected $langcode = 'en';
  34. /**
  35. * The installation profile to install.
  36. *
  37. * @var string
  38. */
  39. protected $profile = 'testing';
  40. /**
  41. * Additional parameters to use for installer screens.
  42. *
  43. * @see FunctionalTestSetupTrait::installParameters()
  44. *
  45. * @var array
  46. */
  47. protected $parameters = [];
  48. /**
  49. * A string translation map used for translated installer screens.
  50. *
  51. * Keys are English strings, values are translated strings.
  52. *
  53. * @var array
  54. */
  55. protected $translations = [
  56. 'Save and continue' => 'Save and continue',
  57. ];
  58. /**
  59. * Whether the installer has completed.
  60. *
  61. * @var bool
  62. */
  63. protected $isInstalled = FALSE;
  64. /**
  65. * {@inheritdoc}
  66. */
  67. protected function setUp() {
  68. $this->isInstalled = FALSE;
  69. $this->setupBaseUrl();
  70. $this->prepareDatabasePrefix();
  71. // Install Drupal test site.
  72. $this->prepareEnvironment();
  73. // Define information about the user 1 account.
  74. $this->rootUser = new UserSession([
  75. 'uid' => 1,
  76. 'name' => 'admin',
  77. 'mail' => 'admin@example.com',
  78. 'pass_raw' => $this->randomMachineName(),
  79. ]);
  80. // If any $settings are defined for this test, copy and prepare an actual
  81. // settings.php, so as to resemble a regular installation.
  82. if (!empty($this->settings)) {
  83. // Not using File API; a potential error must trigger a PHP warning.
  84. copy(DRUPAL_ROOT . '/sites/default/default.settings.php', DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php');
  85. $this->writeSettings($this->settings);
  86. }
  87. // Note that FunctionalTestSetupTrait::installParameters() returns form
  88. // input values suitable for a programmed
  89. // \Drupal::formBuilder()->submitForm().
  90. // @see InstallerTestBase::translatePostValues()
  91. $this->parameters = $this->installParameters();
  92. // Set up a minimal container (required by BrowserTestBase). Set cookie and
  93. // server information so that XDebug works.
  94. // @see install_begin_request()
  95. $request = Request::create($GLOBALS['base_url'] . '/core/install.php', 'GET', [], $_COOKIE, [], $_SERVER);
  96. $this->container = new ContainerBuilder();
  97. $request_stack = new RequestStack();
  98. $request_stack->push($request);
  99. $this->container
  100. ->set('request_stack', $request_stack);
  101. $this->container
  102. ->setParameter('language.default_values', Language::$defaultValues);
  103. $this->container
  104. ->register('language.default', 'Drupal\Core\Language\LanguageDefault')
  105. ->addArgument('%language.default_values%');
  106. $this->container
  107. ->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
  108. ->addArgument(new Reference('language.default'));
  109. $this->container
  110. ->register('http_client', 'GuzzleHttp\Client')
  111. ->setFactory('http_client_factory:fromOptions');
  112. $this->container
  113. ->register('http_client_factory', 'Drupal\Core\Http\ClientFactory')
  114. ->setArguments([new Reference('http_handler_stack')]);
  115. $handler_stack = HandlerStack::create();
  116. $test_http_client_middleware = new TestHttpClientMiddleware();
  117. $handler_stack->push($test_http_client_middleware(), 'test.http_client.middleware');
  118. $this->container
  119. ->set('http_handler_stack', $handler_stack);
  120. $this->container
  121. ->set('app.root', DRUPAL_ROOT);
  122. \Drupal::setContainer($this->container);
  123. // Setup Mink.
  124. $this->initMink();
  125. // Set up the browser test output file.
  126. $this->initBrowserOutputFile();
  127. $this->visitInstaller();
  128. // Select language.
  129. $this->setUpLanguage();
  130. // Select profile.
  131. $this->setUpProfile();
  132. // Address the requirements problem screen, if any.
  133. $this->setUpRequirementsProblem();
  134. // Configure settings.
  135. $this->setUpSettings();
  136. // @todo Allow test classes based on this class to act on further installer
  137. // screens.
  138. // Configure site.
  139. $this->setUpSite();
  140. if ($this->isInstalled) {
  141. // Import new settings.php written by the installer.
  142. $request = Request::createFromGlobals();
  143. $class_loader = require $this->container->get('app.root') . '/autoload.php';
  144. Settings::initialize($this->container->get('app.root'), DrupalKernel::findSitePath($request), $class_loader);
  145. $this->configDirectories['sync'] = Settings::get('config_sync_directory');
  146. // After writing settings.php, the installer removes write permissions
  147. // from the site directory. To allow drupal_generate_test_ua() to write
  148. // a file containing the private key for drupal_valid_test_ua(), the site
  149. // directory has to be writable.
  150. // BrowserTestBase::tearDown() will delete the entire test site directory.
  151. // Not using File API; a potential error must trigger a PHP warning.
  152. chmod($this->container->get('app.root') . '/' . $this->siteDirectory, 0777);
  153. $this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE);
  154. $this->kernel->boot();
  155. $this->kernel->preHandle($request);
  156. $this->container = $this->kernel->getContainer();
  157. // Manually configure the test mail collector implementation to prevent
  158. // tests from sending out emails and collect them in state instead.
  159. $this->container->get('config.factory')
  160. ->getEditable('system.mail')
  161. ->set('interface.default', 'test_mail_collector')
  162. ->save();
  163. $this->installDefaultThemeFromClassProperty($this->container);
  164. }
  165. }
  166. /**
  167. * {@inheritdoc}
  168. */
  169. protected function initFrontPage() {
  170. // We don't want to visit the front page with the installer when
  171. // initializing Mink, so we do nothing here.
  172. }
  173. /**
  174. * Visits the interactive installer.
  175. */
  176. protected function visitInstaller() {
  177. $this->drupalGet($GLOBALS['base_url'] . '/core/install.php');
  178. }
  179. /**
  180. * Installer step: Select language.
  181. */
  182. protected function setUpLanguage() {
  183. $edit = [
  184. 'langcode' => $this->langcode,
  185. ];
  186. $this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
  187. }
  188. /**
  189. * Installer step: Select installation profile.
  190. */
  191. protected function setUpProfile() {
  192. $edit = [
  193. 'profile' => $this->profile,
  194. ];
  195. $this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
  196. }
  197. /**
  198. * Installer step: Configure settings.
  199. */
  200. protected function setUpSettings() {
  201. $edit = $this->translatePostValues($this->parameters['forms']['install_settings_form']);
  202. $this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
  203. }
  204. /**
  205. * Installer step: Requirements problem.
  206. *
  207. * Override this method to test specific requirements warnings or errors
  208. * during the installer.
  209. *
  210. * @see system_requirements()
  211. */
  212. protected function setUpRequirementsProblem() {
  213. // Do nothing.
  214. }
  215. /**
  216. * Final installer step: Configure site.
  217. */
  218. protected function setUpSite() {
  219. $edit = $this->translatePostValues($this->parameters['forms']['install_configure_form']);
  220. $this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
  221. // If we've got to this point the site is installed using the regular
  222. // installation workflow.
  223. $this->isInstalled = TRUE;
  224. }
  225. /**
  226. * {@inheritdoc}
  227. *
  228. * FunctionalTestSetupTrait::refreshVariables() tries to operate on persistent
  229. * storage, which is only available after the installer completed.
  230. */
  231. protected function refreshVariables() {
  232. if ($this->isInstalled) {
  233. parent::refreshVariables();
  234. }
  235. }
  236. }