diff --git a/core/modules/user/config/install/user.mail.yml b/core/modules/user/config/install/user.mail.yml
index b8efa9e91d..c466a40eee 100644
--- a/core/modules/user/config/install/user.mail.yml
+++ b/core/modules/user/config/install/user.mail.yml
@@ -1,3 +1,8 @@
+register_password_set:
+  body: "[user:name],\n\nThank you for registering at [site:name]. You may now log in and verify your account by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once. You will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
+  subject: 'Account details for [user:name] at [site:name]'
+register_password_set_activation:
+  body: "[user:name],\n\nYour account at [site:name] has been activated.\n\nYou will be able to log in to [site:login-url] in the future using:\n\nusername: [user:name]\npassword: your password.\n\n--  [site:name] team"
 cancel_confirm:
   body: "[user:display-name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n--  [site:name] team"
   subject: 'Account cancellation request for [user:display-name] at [site:name]'
diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml
index 8372ccdb73..e3a40689b9 100644
--- a/core/modules/user/config/install/user.settings.yml
+++ b/core/modules/user/config/install/user.settings.yml
@@ -9,6 +9,7 @@ notify:
   register_admin_created: true
   register_no_approval_required: true
   register_pending_approval: true
+  register_password_set: false
 register: visitors
 cancel_method: user_cancel_block
 password_reset_timeout: 86400
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 2f9bda44f2..e9e1b6d119 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -10,6 +10,9 @@ user.settings:
     verify_mail:
       type: boolean
       label: 'Require email verification when a visitor creates an account'
+    register_password_set:
+      type: boolean
+      label: 'Require people to choose a password during registration.'
     notify:
       type: mapping
       label: 'Notify user'
@@ -52,36 +55,42 @@ user.settings:
       label: 'Enable password strength indicator'
 
 user.mail:
- type: config_object
- label: 'Email settings'
- mapping:
-  cancel_confirm:
-    type: mail
-    label: 'Account cancellation confirmation'
-  password_reset:
-    type: mail
-    label: 'Password recovery'
-  register_admin_created:
-    type: mail
-    label: 'Account created by administrator'
-  register_no_approval_required:
-    type: mail
-    label: 'Registration confirmation (No approval required)'
-  register_pending_approval:
-    type: mail
-    label: 'Registration confirmation (Pending approval)'
-  register_pending_approval_admin:
-    type: mail
-    label: 'Admin (user awaiting approval)'
-  status_activated:
-    type: mail
-    label: 'Account activation'
-  status_blocked:
-    type: mail
-    label: 'Account blocked'
-  status_canceled:
-    type: mail
-    label: 'Account cancelled'
+  type: config_object
+  label: 'Email settings'
+  mapping:
+    cancel_confirm:
+      type: mail
+      label: 'Account cancellation confirmation'
+    password_reset:
+      type: mail
+      label: 'Password recovery'
+    register_admin_created:
+      type: mail
+      label: 'Account created by administrator'
+    register_no_approval_required:
+      type: mail
+      label: 'Registration confirmation (No approval required)'
+    register_password_set:
+      type: mail
+      label: 'Registration with password confirmation (No approval required)'
+    register_password_set_activation:
+      type: mail
+      label: 'Registration with password activation'
+    register_pending_approval:
+      type: mail
+      label: 'Registration confirmation (Pending approval)'
+    register_pending_approval_admin:
+      type: mail
+      label: 'Admin (user awaiting approval)'
+    status_activated:
+      type: mail
+      label: 'Account activation'
+    status_blocked:
+      type: mail
+      label: 'Account blocked'
+    status_canceled:
+      type: mail
+      label: 'Account cancelled'
 
 user.flood:
   type: config_object
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index 893fdd251b..4a0b35272e 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -162,7 +162,7 @@ public function form(array $form, FormStateInterface $form_state) {
         }
       }
     }
-    elseif (!$config->get('verify_mail') || $admin_create) {
+    elseif (!$config->get('verify_mail') || $config->get('register_password_set') || $admin_create) {
       $form['account']['pass'] = [
         '#type' => 'password_confirm',
         '#size' => 25,
diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php
index 0ef0920cbf..6a4707e0f2 100644
--- a/core/modules/user/src/AccountSettingsForm.php
+++ b/core/modules/user/src/AccountSettingsForm.php
@@ -107,8 +107,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Administrator role'),
       '#open' => TRUE,
     ];
-    // Do not allow users to set the anonymous or authenticated user roles as the
-    // administrator role.
+    // Do not allow users to set the anonymous or authenticated user roles as
+    // the administrator role.
     $roles = user_role_names(TRUE);
     unset($roles[RoleInterface::AUTHENTICATED_ID]);
 
@@ -163,6 +163,22 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#default_value' => $config->get('verify_mail'),
       '#description' => $this->t('New users will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password. With this setting disabled, users will be logged in immediately upon registering, and may select their own passwords during registration.'),
     ];
+    $form['registration_cancellation']['register_password_set'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Require people to choose a password during registration.'),
+      '#description' => $this->t('If <em>Require e-mail verification</em> is disabled, this setting is automatically enabled.'),
+      '#default_value' => $config->get('register_password_set'),
+      '#states' => [
+        // Disable this option if email_verification is unchecked.
+        'disabled' => [
+          'input[name="user_email_verification"]' => ['checked' => FALSE],
+        ],
+        // Enable this option if email_verification is checked.
+        'enabled' => [
+          'input[name="user_email_verification"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
     $form['registration_cancellation']['user_password_strength'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Enable password strength indicator'),
@@ -172,7 +188,11 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'radios',
       '#title' => $this->t('When cancelling a user account'),
       '#default_value' => $config->get('cancel_method'),
-      '#description' => $this->t('Users with the %select-cancel-method or %administer-users <a href=":permissions-url">permissions</a> can override this default method.', ['%select-cancel-method' => $this->t('Select method for cancelling account'), '%administer-users' => $this->t('Administer users'), ':permissions-url' => Url::fromRoute('user.admin_permissions')->toString()]),
+      '#description' => $this->t('Users with the %select-cancel-method or %administer-users <a href=":permissions-url">permissions</a> can override this default method.', [
+        '%select-cancel-method' => $this->t('Select method for cancelling account'),
+        '%administer-users' => $this->t('Administer users'),
+        ':permissions-url' => Url::fromRoute('user.admin_permissions')->toString(),
+      ]),
     ];
     $form['registration_cancellation']['user_cancel_method'] += user_cancel_methods();
     foreach (Element::children($form['registration_cancellation']['user_cancel_method']) as $key) {
@@ -281,6 +301,45 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#rows' => 15,
     ];
 
+    $form['email_password_set_activation'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Welcome (password set at registration)'),
+      '#collapsed' => TRUE,
+      '#description' => $this->t('Edit the welcome e-mail messages sent to new members upon registering, when no administrator approval is required and password has already been set.') . ' ' . $email_token_help,
+      '#group' => 'email',
+    ];
+    $form['email_password_set_activation']['user_mail_register_password_set_activation_subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Subject'),
+      '#default_value' => $mail_config->get('register_password_set_activation.subject'),
+      '#maxlength' => 180,
+    ];
+    $form['email_password_set_activation']['user_mail_register_password_set_activation_body'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Body'),
+      '#default_value' => $mail_config->get('register_password_set_activation.body'),
+      '#rows' => 15,
+    ];
+    $form['email_password_set'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Account activation (password set at registration)'),
+      '#collapsed' => TRUE,
+      '#description' => $this->t('Edit the activation e-mail messages sent to new members upon registering, when no administrator approval is required and password has already been set during registration.') . ' ' . $email_token_help,
+      '#group' => 'email',
+    ];
+    $form['email_password_set']['user_mail_register_password_set_subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Subject'),
+      '#default_value' => $mail_config->get('register_password_set.subject'),
+      '#maxlength' => 180,
+    ];
+    $form['email_password_set']['user_mail_register_password_set_body'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Body'),
+      '#default_value' => $mail_config->get('register_password_set.body'),
+      '#rows' => 15,
+    ];
+
     $form['email_password_reset'] = [
       '#type' => 'details',
       '#title' => $this->t('Password recovery'),
@@ -434,6 +493,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       ->set('password_strength', $form_state->getValue('user_password_strength'))
       ->set('verify_mail', $form_state->getValue('user_email_verification'))
       ->set('cancel_method', $form_state->getValue('user_cancel_method'))
+      ->set('register_password_set', $form_state->getValue('register_password_set'))
       ->set('notify.status_activated', $form_state->getValue('user_mail_status_activated_notify'))
       ->set('notify.status_blocked', $form_state->getValue('user_mail_status_blocked_notify'))
       ->set('notify.status_canceled', $form_state->getValue('user_mail_status_canceled_notify'))
@@ -447,6 +507,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       ->set('register_admin_created.subject', $form_state->getValue('user_mail_register_admin_created_subject'))
       ->set('register_no_approval_required.body', $form_state->getValue('user_mail_register_no_approval_required_body'))
       ->set('register_no_approval_required.subject', $form_state->getValue('user_mail_register_no_approval_required_subject'))
+      ->set('register_password_set_activation.body', $form_state->getValue('user_mail_register_password_set_activation_body'))
+      ->set('register_password_set_activation.subject', $form_state->getValue('user_mail_register_password_set_activation_subject'))
+      ->set('register_password_set.body', $form_state->getValue('user_mail_register_password_set_body'))
+      ->set('register_password_set.subject', $form_state->getValue('user_mail_register_password_set_subject'))
       ->set('register_pending_approval.body', $form_state->getValue('user_mail_register_pending_approval_body'))
       ->set('register_pending_approval.subject', $form_state->getValue('user_mail_register_pending_approval_subject'))
       ->set('register_pending_approval_admin.body', $form_state->getValue('register_pending_approval_admin_body'))
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index dfffbfbb00..8ec6735431 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -97,7 +97,10 @@ public function preSave(EntityStorageInterface $storage) {
 
     // Make sure that the authenticated/anonymous roles are not persisted.
     foreach ($this->get('roles') as $index => $item) {
-      if (in_array($item->target_id, [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID])) {
+      if (in_array($item->target_id, [
+        RoleInterface::ANONYMOUS_ID,
+        RoleInterface::AUTHENTICATED_ID,
+      ])) {
         $this->get('roles')->offsetUnset($index);
       }
     }
@@ -136,6 +139,16 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
       if ($this->status->value != $this->original->status->value) {
         // The user's status is changing; conditionally send notification email.
         $op = $this->status->value == 1 ? 'status_activated' : 'status_blocked';
+
+        // Send the password_set activation e-mail to the user
+        // if the account never signed in and if we are
+        // called from the user_register_form and password_register
+        // is set to enabled.
+        if (!$this->login->value && \Drupal::config('user.settings')->get('register_password_set') && !$this->original->status->value && $this->status->value) {
+          $op = 'register_password_set_activation';
+        }
+
+        // Notify the user.
         _user_mail_notify($op, $this);
       }
     }
@@ -188,7 +201,10 @@ public function hasRole($rid) {
    */
   public function addRole($rid) {
 
-    if (in_array($rid, [RoleInterface::AUTHENTICATED_ID, RoleInterface::ANONYMOUS_ID])) {
+    if (in_array($rid, [
+      RoleInterface::AUTHENTICATED_ID,
+      RoleInterface::ANONYMOUS_ID,
+    ])) {
       throw new \InvalidArgumentException('Anonymous or authenticated role ID must not be assigned manually.');
     }
 
diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php
index f1efe4a79c..43fe8b2a44 100644
--- a/core/modules/user/src/RegisterForm.php
+++ b/core/modules/user/src/RegisterForm.php
@@ -15,7 +15,6 @@ class RegisterForm extends AccountForm {
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
-    $user = $this->currentUser();
     /** @var \Drupal\user\UserInterface $account */
     $account = $this->entity;
 
@@ -69,15 +68,23 @@ protected function actions(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    $config = $this->config('user.settings');
     $admin = $form_state->getValue('administer_users');
 
-    if (!\Drupal::config('user.settings')->get('verify_mail') || $admin) {
+    if (!$config->get('verify_mail') || ($config->get('verify_mail') && $config->get('register_password_set')) || $admin) {
       $pass = $form_state->getValue('pass');
     }
     else {
       $pass = \Drupal::service('password_generator')->generate();
     }
 
+    // If we are not an admin and we try to register and password_register
+    // is set, make sure the status is set to disabled before we save
+    // the newly created account.
+    if ($config->get('verify_mail') && $config->get('register_password_set') && !$form_state->getValue('uid') && !$admin) {
+      $form_state->setValue('status', 0);
+    }
+
     // Remove unneeded values.
     $form_state->cleanValues();
 
@@ -91,6 +98,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
+    $config = $this->config('user.settings');
     $account = $this->entity;
     $pass = $account->getPassword();
     $admin = $form_state->getValue('administer_users');
@@ -103,17 +111,31 @@ public function save(array $form, FormStateInterface $form_state) {
     $form_state->set('user', $account);
     $form_state->setValue('uid', $account->id());
 
-    $this->logger('user')->notice('New user: %name %email.', ['%name' => $form_state->getValue('name'), '%email' => '<' . $form_state->getValue('mail') . '>', 'type' => $account->toLink($this->t('Edit'), 'edit-form')->toString()]);
+    $this->logger('user')->notice('New user: %name %email.', [
+      '%name' => $form_state->getValue('name'),
+      '%email' => '<' . $form_state->getValue('mail') . '>',
+      'type' => $account->toLink($this->t('Edit'), 'edit-form')->toString(),
+    ]);
 
     // Add plain text password into user account to generate mail tokens.
     $account->password = $pass;
 
     // New administrative account without notification.
     if ($admin && !$notify) {
-      $this->messenger()->addStatus($this->t('Created a new user account for <a href=":url">%name</a>. No email has been sent.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
+      $this->messenger()->addStatus($this->t('Created a new user account for <a href=":url">%name</a>. No email has been sent.', [
+        ':url' => $account->toUrl()->toString(),
+        '%name' => $account->getAccountName(),
+      ]));
+    }
+    // Email verification enabled, but users set a password during registration.
+    elseif (!$admin && $config->get('register') == UserInterface::REGISTER_VISITORS && $config->get('register_password_set') && !$account->isActive()) {
+      // Notify the user.
+      _user_mail_notify('register_pending_approval', $account);
+      $this->messenger()->addStatus($this->t('A welcome message with further instructions has been sent to your email address.'));
+      $form_state->setRedirect('<front>');
     }
     // No email verification required; log in user immediately.
-    elseif (!$admin && !\Drupal::config('user.settings')->get('verify_mail') && $account->isActive()) {
+    elseif (!$admin && !$config->get('verify_mail') && $account->isActive()) {
       _user_mail_notify('register_no_approval_required', $account);
       user_login_finalize($account);
       $this->messenger()->addStatus($this->t('Registration successful. You are now logged in.'));
@@ -122,13 +144,19 @@ public function save(array $form, FormStateInterface $form_state) {
     // No administrator approval required.
     elseif ($account->isActive() || $notify) {
       if (!$account->getEmail() && $notify) {
-        $this->messenger()->addStatus($this->t('The new user <a href=":url">%name</a> was created without an email address, so no welcome message was sent.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
+        $this->messenger()->addStatus($this->t('The new user <a href=":url">%name</a> was created without an email address, so no welcome message was sent.', [
+          ':url' => $account->toUrl()->toString(),
+          '%name' => $account->getAccountName(),
+        ]));
       }
       else {
         $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
         if (_user_mail_notify($op, $account)) {
           if ($notify) {
-            $this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user <a href=":url">%name</a>.', [':url' => $account->toUrl()->toString(), '%name' => $account->getAccountName()]));
+            $this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user <a href=":url">%name</a>.', [
+              ':url' => $account->toUrl()->toString(),
+              '%name' => $account->getAccountName(),
+            ]));
           }
           else {
             $this->messenger()->addStatus($this->t('A welcome message with further instructions has been sent to your email address.'));
diff --git a/core/modules/user/tests/src/Functional/UserRegistrationTest.php b/core/modules/user/tests/src/Functional/UserRegistrationTest.php
index 76cf00caf2..843909638b 100644
--- a/core/modules/user/tests/src/Functional/UserRegistrationTest.php
+++ b/core/modules/user/tests/src/Functional/UserRegistrationTest.php
@@ -5,6 +5,8 @@
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Test\AssertMailTrait;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\Tests\BrowserTestBase;
@@ -17,6 +19,10 @@
  */
 class UserRegistrationTest extends BrowserTestBase {
 
+  use StringTranslationTrait;
+
+  use AssertMailTrait;
+
   /**
    * Modules to enable.
    *
@@ -29,6 +35,9 @@ class UserRegistrationTest extends BrowserTestBase {
    */
   protected $defaultTheme = 'stark';
 
+  /**
+   * Tests registration form with email verification.
+   */
   public function testRegistrationWithEmailVerification() {
     $config = $this->config('user.settings');
     // Require email verification.
@@ -42,11 +51,13 @@ public function testRegistrationWithEmailVerification() {
 
     // Allow registration by site visitors without administrator approval.
     $config->set('register', UserInterface::REGISTER_VISITORS)->save();
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
     $edit = [];
     $edit['name'] = $name = $this->randomMachineName();
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('A welcome message with further instructions has been sent to your email address.'), 'User registered successfully.');
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains($this->t('A welcome message with further instructions has been sent to your email address.'));
 
     /** @var EntityStorageInterface $storage */
     $storage = $this->container->get('entity_type.manager')->getStorage('user');
@@ -59,16 +70,21 @@ public function testRegistrationWithEmailVerification() {
 
     // Allow registration by site visitors, but require administrator approval.
     $config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
     $edit = [];
     $edit['name'] = $name = $this->randomMachineName();
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->container->get('entity_type.manager')->getStorage('user')->resetCache();
     $accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
     $new_user = reset($accounts);
     $this->assertFalse($new_user->isActive(), 'New account is blocked until approved by an administrator.');
   }
 
+  /**
+   * Tests registration form without email varification.
+   */
   public function testRegistrationWithoutEmailVerification() {
     $config = $this->config('user.settings');
     // Don't require email verification and allow registration by site visitors
@@ -83,40 +99,48 @@ public function testRegistrationWithoutEmailVerification() {
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
 
     // Try entering a mismatching password.
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
     $edit['pass[pass1]'] = '99999.0';
     $edit['pass[pass2]'] = '99999';
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.');
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains($this->t('The specified passwords do not match.'));
 
     // Enter a correct password.
     $edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
     $edit['pass[pass2]'] = $new_pass;
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->container->get('entity_type.manager')->getStorage('user')->resetCache();
     $accounts = $this->container->get('entity_type.manager')->getStorage('user')
       ->loadByProperties(['name' => $name, 'mail' => $mail]);
     $new_user = reset($accounts);
     $this->assertNotNull($new_user, 'New account successfully created with matching passwords.');
-    $this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.');
+    $this->assertSession()->responseContains($this->t('Registration successful. You are now logged in.'));
     $this->drupalLogout();
 
     // Allow registration by site visitors, but require administrator approval.
     $config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
     $edit = [];
     $edit['name'] = $name = $this->randomMachineName();
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
     $edit['pass[pass1]'] = $pass = $this->randomMachineName();
     $edit['pass[pass2]'] = $pass;
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval');
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains($this->t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'));
 
     // Try to log in before administrator approval.
+    $this->drupalGet('user/login');
+    $this->assertSession()->statusCodeEquals(200);
     $auth = [
       'name' => $name,
       'pass' => $pass,
     ];
-    $this->drupalPostForm('user/login', $auth, t('Log in'));
-    $this->assertText(t('The username @name has not been activated or is blocked.', ['@name' => $name]), 'User cannot log in yet.');
+    $this->submitForm($auth, $this->t('Log in'));
+    $this->assertSession()->pageTextContains(t('The username @name has not been activated or is blocked.', ['@name' => $name]));
 
     // Activate the new account.
     $accounts = $this->container->get('entity_type.manager')->getStorage('user')
@@ -127,14 +151,84 @@ public function testRegistrationWithoutEmailVerification() {
     $edit = [
       'status' => 1,
     ];
-    $this->drupalPostForm('user/' . $new_user->id() . '/edit', $edit, t('Save'));
+    $this->drupalGet('user/' . $new_user->id() . '/edit');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Save'));
     $this->drupalLogout();
 
     // Log in after administrator approval.
-    $this->drupalPostForm('user/login', $auth, t('Log in'));
-    $this->assertText(t('Member for'), 'User can log in after administrator approval.');
+    $this->drupalGet('user/login');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($auth, $this->t('Log in'));
+    $this->assertSession()->responseContains($this->t('Member for'));
+  }
+
+  /**
+   * Tests registration form with password set.
+   */
+  public function testRegistrationWithPasswordSet() {
+    // Require e-mail verification, but let's users choose a password during
+    // registration and allow registration by site visitors without
+    // administrator approval.
+    $this->config('user.settings')
+      ->set('verify_mail', TRUE)
+      ->set('register', UserInterface::REGISTER_VISITORS)
+      ->set('register_password_set', TRUE)
+      ->set('notify.register_pending_approval', TRUE)
+      ->save();
+
+    $edit = [];
+    $edit['name'] = $name = $this->randomMachineName();
+    $edit['mail'] = $mail = $edit['name'] . '@example.com';
+    $edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
+    $edit['pass[pass2]'] = $new_pass;
+
+    // Create a new user.
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains($this->t('A welcome message with further instructions has been sent to your email address.'));
+
+    // Make sure the user is still blocked.
+    $this->container->get('entity_type.manager')->getStorage('user')->resetCache();
+    $accounts = $this->container->get('entity_type.manager')->getStorage('user')->loadByProperties([
+      'name' => $name,
+      'mail' => $mail,
+    ]);
+    $new_user = reset($accounts);
+    $this->assertEmpty($new_user->status->value, 'New account is blocked until approved via e-mail confirmation.');
+
+    // Try to login before activating the account via e-mail.
+    $edit2 = [];
+    $edit2['name'] = $name;
+    $edit2['pass'] = $new_pass;
+    $this->drupalGet('user/login');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit2, $this->t('Log in'));
+    $this->assertSession()->responseContains($this->t('The username %name has not been activated or is blocked.', ['%name' => $name]));
+
+    // Try to activate the user.
+    $new_user->activate();
+    $new_user->save();
+    $_emails = $this->getMails();
+    $this->assertCount(2, $_emails);
+    $email = reset($_emails);
+    $urls = [];
+    preg_match('#.+user/reset/.+#', $email['body'], $urls);
+    $this->drupalGet($urls[0]);
+    $this->drupalPostForm(NULL, [], t('Log in'));
+
+    // Change the password.
+    $password = \Drupal::service('password_generator')->generate();
+    $edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText(t('The changes have been saved.'), 'Password changed.');
+
   }
 
+  /**
+   * Tests default registration form for email duplicates.
+   */
   public function testRegistrationEmailDuplicates() {
     // Don't require email verification and allow registration by site visitors
     // without administrator approval.
@@ -151,14 +245,18 @@ public function testRegistrationEmailDuplicates() {
     $edit['mail'] = $duplicate_user->getEmail();
 
     // Attempt to create a new account using an existing email address.
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying an exact duplicate email address displays an error message');
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->pageTextContains($this->t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]));
 
-    // Attempt to bypass duplicate email registration validation by adding spaces.
+    // Attempt to bypass duplicate email validation by adding spaces.
     $edit['mail'] = '   ' . $duplicate_user->getEmail() . '   ';
 
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying a duplicate email address with added whitespace displays an error message');
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->pageTextContains($this->t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]));
   }
 
   /**
@@ -210,7 +308,9 @@ public function testUuidFormState() {
     $edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
 
     // Create one account.
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->assertSession()->statusCodeEquals(200);
 
     $user_storage = \Drupal::entityTypeManager()->getStorage('user');
@@ -223,12 +323,17 @@ public function testUuidFormState() {
     $edit['mail'] = $edit['name'] . '@example.com';
     $edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
 
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->assertSession()->statusCodeEquals(200);
 
     $this->assertNotEmpty($user_storage->loadByProperties(['name' => $edit['name']]));
   }
 
+  /**
+   * Tests default registration form values.
+   */
   public function testRegistrationDefaultValues() {
     // Don't require email verification and allow registration by site visitors
     // without administrator approval.
@@ -252,20 +357,20 @@ public function testRegistrationDefaultValues() {
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
     $edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
     $edit['pass[pass2]'] = $new_pass;
-    $this->drupalPostForm(NULL, $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
 
     // Check user fields.
     $accounts = $this->container->get('entity_type.manager')->getStorage('user')
       ->loadByProperties(['name' => $name, 'mail' => $mail]);
     $new_user = reset($accounts);
-    $this->assertEqual($new_user->getAccountName(), $name, 'Username matches.');
-    $this->assertEqual($new_user->getEmail(), $mail, 'Email address matches.');
+    $this->assertEquals($new_user->getAccountName(), $name, 'Username matches.');
+    $this->assertEquals($new_user->getEmail(), $mail, 'Email address matches.');
     $this->assertTrue(($new_user->getCreatedTime() > REQUEST_TIME - 20), 'Correct creation time.');
-    $this->assertEqual($new_user->isActive(), $config_user_settings->get('register') == UserInterface::REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
-    $this->assertEqual($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
-    $this->assertEqual($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
-    $this->assertEqual($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
-    $this->assertEqual($new_user->init->value, $mail, 'Correct init field.');
+    $this->assertEquals($new_user->isActive(), $config_user_settings->get('register') == UserInterface::REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
+    $this->assertEquals($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
+    $this->assertEquals($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
+    $this->assertEquals($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
+    $this->assertEquals($new_user->init->value, $mail, 'Correct init field.');
   }
 
   /**
@@ -277,13 +382,21 @@ public function testRegistrationDefaultValues() {
   public function testUniqueFields() {
     $account = $this->drupalCreateUser();
 
-    $edit = ['mail' => 'test@example.com', 'name' => $account->getAccountName()];
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertRaw(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getAccountName()]));
+    $edit = [
+      'mail' => 'test@example.com',
+      'name' => $account->getAccountName(),
+    ];
 
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getAccountName()]));
+
+    $this->drupalGet('user/register');
+    $this->assertSession()->statusCodeEquals(200);
     $edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
-    $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertRaw(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
+    $this->submitForm($edit, $this->t('Create new account'));
+    $this->assertSession()->responseContains(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
   }
 
   /**
@@ -317,7 +430,7 @@ public function testRegistrationWithUserFields() {
 
     // Check that the field does not appear on the registration form.
     $this->drupalGet('user/register');
-    $this->assertNoText($field->label(), 'The field does not appear on user registration form');
+    $this->assertSession()->responseNotContains($field->label());
     $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:core.entity_form_display.user.user.register');
     $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:user.settings');
 
@@ -327,7 +440,7 @@ public function testRegistrationWithUserFields() {
       ->save();
 
     $this->drupalGet('user/register');
-    $this->assertText($field->label(), 'The field appears on user registration form');
+    $this->assertSession()->responseContains($field->label());
     $this->assertRegistrationFormCacheTagsWithUserFields();
 
     // Check that validation errors are correctly reported.
@@ -336,24 +449,24 @@ public function testRegistrationWithUserFields() {
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
     // Missing input in required field.
     $edit['test_user_field[0][value]'] = '';
-    $this->drupalPostForm(NULL, $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->assertRegistrationFormCacheTagsWithUserFields();
-    $this->assertRaw(t('@name field is required.', ['@name' => $field->label()]));
+    $this->assertSession()->responseContains($this->t('@name field is required.', ['@name' => $field->label()]));
     // Invalid input.
     $edit['test_user_field[0][value]'] = '-1';
-    $this->drupalPostForm(NULL, $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
     $this->assertRegistrationFormCacheTagsWithUserFields();
-    $this->assertRaw(t('%name does not accept the value -1.', ['%name' => $field->label()]));
+    $this->assertSession()->responseContains($this->t('%name does not accept the value -1.', ['%name' => $field->label()]));
 
     // Submit with valid data.
     $value = rand(1, 255);
     $edit['test_user_field[0][value]'] = $value;
-    $this->drupalPostForm(NULL, $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
     // Check user fields.
     $accounts = $this->container->get('entity_type.manager')->getStorage('user')
       ->loadByProperties(['name' => $name, 'mail' => $mail]);
     $new_user = reset($accounts);
-    $this->assertEqual($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
+    $this->assertEquals($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
 
     // Check that the 'add more' button works.
     $field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
@@ -364,21 +477,21 @@ public function testRegistrationWithUserFields() {
     $value = rand(1, 255);
     $edit = [];
     $edit['test_user_field[0][value]'] = $value;
-    $this->drupalPostForm(NULL, $edit, t('Add another item'));
-    $this->drupalPostForm(NULL, $edit, t('Add another item'));
+    $this->submitForm($edit, $this->t('Add another item'));
+    $this->submitForm($edit, $this->t('Add another item'));
     // Submit with three values.
     $edit['test_user_field[1][value]'] = $value + 1;
     $edit['test_user_field[2][value]'] = $value + 2;
     $edit['name'] = $name = $this->randomMachineName();
     $edit['mail'] = $mail = $edit['name'] . '@example.com';
-    $this->drupalPostForm(NULL, $edit, t('Create new account'));
+    $this->submitForm($edit, $this->t('Create new account'));
     // Check user fields.
     $accounts = $this->container->get('entity_type.manager')->getStorage('user')
       ->loadByProperties(['name' => $name, 'mail' => $mail]);
     $new_user = reset($accounts);
-    $this->assertEqual($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
-    $this->assertEqual($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
-    $this->assertEqual($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
+    $this->assertEquals($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
+    $this->assertEquals($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
+    $this->assertEquals($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
   }
 
   /**
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index a1395f4656..c4c987c5b9 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1020,6 +1020,7 @@ function user_role_revoke_permissions($rid, array $permissions = []) {
  *     self-registers.
  *   - 'register_pending_approval': Welcome message, user pending admin
  *     approval.
+ *   - 'register_password_set_activation': Activation message, password set.
  *   - 'password_reset': Password recovery request.
  *   - 'status_activated': Account activated.
  *   - 'status_blocked': Account blocked.
