diff --git a/core/modules/node/node.tokens.inc b/core/modules/node/node.tokens.inc
index 2fcf0ddfa8..cb1a931cab 100644
--- a/core/modules/node/node.tokens.inc
+++ b/core/modules/node/node.tokens.inc
@@ -130,21 +130,21 @@ function node_tokens($type, $tokens, array $data, array $options, BubbleableMeta
           $translation = \Drupal::entityManager()->getTranslationFromContext($node, $langcode, ['operation' => 'node_tokens']);
           if ($translation->hasField('body') && ($items = $translation->get('body')) && !$items->isEmpty()) {
             $item = $items[0];
-            // If the summary was requested and is not empty, use it.
-            if ($name == 'summary' && !empty($item->summary)) {
-              $output = $item->summary_processed;
+
+            if ($name === 'body') {
+              $output = $item->processed;
             }
-            // Attempt to provide a suitable version of the 'body' field.
             else {
-              $output = $item->processed;
-              // A summary was requested.
-              if ($name == 'summary') {
-                // Generate an optionally trimmed summary of the body field.
-
+              // If the summary was requested and is not empty, use it.
+              if (!empty($item->summary)) {
+                $output = $item->summary_processed;
+              }
+              // Else generate an optionally trimmed summary of the body field.
+              else {
                 // Get the 'trim_length' size used for the 'teaser' mode, if
                 // present, or use the default trim_length size.
                 $display_options = entity_get_display('node', $node->getType(), 'teaser')->getComponent('body');
-                if (isset($display_options['settings']['trim_length'])) {
+                if ($display_options && ($display_options['type'] === 'text_trimmed'|| $display_options['type'] === 'text_summary_or_trimmed')) {
                   $length = $display_options['settings']['trim_length'];
                 }
                 else {
@@ -152,11 +152,10 @@ function node_tokens($type, $tokens, array $data, array $options, BubbleableMeta
                   $length = $settings['trim_length'];
                 }
 
-                $output = text_summary($output, $item->format, $length);
+                $output = text_summary($item->value, $item->format, $length);
               }
             }
-            // "processed" returns a \Drupal\Component\Render\MarkupInterface
-            // via check_markup().
+
             $replacements[$original] = $output;
           }
           break;
diff --git a/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php b/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
index dd39b6d26d..67f1091169 100644
--- a/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
+++ b/core/modules/node/tests/src/Kernel/NodeTokenReplaceTest.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
+use Drupal\node\NodeInterface;
 use Drupal\Tests\system\Kernel\Token\TokenReplaceKernelTestBase;
 
 /**
@@ -18,6 +19,13 @@
 class NodeTokenReplaceTest extends TokenReplaceKernelTestBase {
 
   /**
+   * User account used as a node author.
+   *
+   * @var \Drupal\user\Entity\User
+   */
+  protected $account;
+
+  /**
    * Modules to enable.
    *
    * @var array
@@ -34,10 +42,12 @@ protected function setUp() {
     $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']);
     $node_type->save();
     node_add_body_field($node_type);
+
+    $this->account = $this->createUser();
   }
 
   /**
-   * Creates a node, then tests the tokens generated from it.
+   * Tests all node tokens.
    */
   public function testNodeTokenReplacement() {
     $url_options = [
@@ -45,13 +55,12 @@ public function testNodeTokenReplacement() {
       'language' => $this->interfaceLanguage,
     ];
 
-    // Create a user and a node.
-    $account = $this->createUser();
+    // Create a node.
     /* @var $node \Drupal\node\NodeInterface */
     $node = Node::create([
       'type' => 'article',
       'tnid' => 0,
-      'uid' => $account->id(),
+      'uid' => $this->account->id(),
       'title' => '<blink>Blinking Text</blink>',
       'body' => [['value' => 'Regular NODE body for the test.', 'summary' => 'Fancy NODE summary.', 'format' => 'plain_text']],
     ]);
@@ -69,9 +78,9 @@ public function testNodeTokenReplacement() {
     $tests['[node:langcode]'] = $node->language()->getId();
     $tests['[node:url]'] = $node->url('canonical', $url_options);
     $tests['[node:edit-url]'] = $node->url('edit-form', $url_options);
-    $tests['[node:author]'] = $account->getUsername();
+    $tests['[node:author]'] = $this->account->getUsername();
     $tests['[node:author:uid]'] = $node->getOwnerId();
-    $tests['[node:author:name]'] = $account->getUsername();
+    $tests['[node:author:name]'] = $this->account->getUsername();
     $tests['[node:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($node->getCreatedTime(), ['langcode' => $this->interfaceLanguage->getId()]);
     $tests['[node:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($node->getChangedTime(), ['langcode' => $this->interfaceLanguage->getId()]);
 
@@ -102,30 +111,111 @@ public function testNodeTokenReplacement() {
     foreach ($tests as $input => $expected) {
       $bubbleable_metadata = new BubbleableMetadata();
       $output = $this->tokenService->replace($input, ['node' => $node], ['langcode' => $this->interfaceLanguage->getId()], $bubbleable_metadata);
-      $this->assertEqual($output, $expected, format_string('Node token %token replaced.', ['%token' => $input]));
+      $this->assertEqual($output, $expected, new FormattableMarkup('Node token %token replaced.', ['%token' => $input]));
       $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
     }
+  }
 
-    // Repeat for a node without a summary.
+  /**
+   * Tests the [node:summary] token.
+   */
+  public function testNodeSummaryTokenReplacement() {
+    // Length of this text is 843 characters and it's longer than a default
+    // trim length setting (600).
+    $text = '<blink>Lorem ipsum</blink> dolor sit amet, consectetur adipiscing elit. '
+      . 'Morbi diam dui, finibus et purus ac, elementum pretium augue. '
+      . 'Fusce lacus nisl, feugiat sit amet blandit sed, mattis eu ligula. Duis laoreet dui non felis maximus fringilla. '
+      . 'Maecenas tempor magna id urna dapibus, in elementum mauris consequat. '
+      . 'Praesent in urna non felis fringilla ullamcorper interdum eu risus. '
+      . 'Vestibulum id erat ultrices, varius est sed, dignissim dui. Vestibulum a dapibus nisl. '
+      . 'Maecenas vestibulum nibh a aliquet mollis. Donec ac justo eget justo interdum faucibus. '
+      . 'Aenean sit amet finibus turpis. Donec viverra vel eros eget varius. Praesent rutrum est diam. '
+      . 'Mauris at odio scelerisque, mattis mi et, tempor eros. '
+      . 'Vestibulum sollicitudin sem quis diam sollicitudin posuere. Nam finibus vestibulum suscipit. '
+      . 'Fusce tempor tincidunt lorem vitae mollis.';
+    // Create a node.
+    /* @var $node \Drupal\node\NodeInterface */
     $node = Node::create([
       'type' => 'article',
-      'uid' => $account->id(),
+      'uid' => $this->account->id(),
       'title' => '<blink>Blinking Text</blink>',
-      'body' => [['value' => 'A string that looks random like TR5c2I', 'format' => 'plain_text']],
+      'body' => [['value' => $text, 'format' => 'plain_text']],
     ]);
     $node->save();
 
-    // Generate and test token - use full body as expected value.
-    $tests = [];
-    $tests['[node:summary]'] = $node->body->processed;
+    // Get teaser node view display.
+    $view_display = \Drupal::entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->load('node.article.teaser');
+    if (!$view_display) {
+      $values = [
+        'targetEntityType' => 'node',
+        'bundle' => 'article',
+        'mode' => 'teaser',
+        'status' => TRUE,
+      ];
+      $view_display = \Drupal::entityTypeManager()
+        ->getStorage('entity_view_display')
+        ->create($values);
+    }
 
-    // Test to make sure that we generated something for each token.
-    $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated for node without a summary.');
+    // Token [node:summary] should use trim settings of "Summary or trimmed"
+    // formatter of teaser view mode.
+    $view_display->setComponent('body', [
+      'type' => 'text_summary_or_trimmed',
+      'settings' => ['trim_length' => 160],
+    ]);
+    $view_display->save();
+    $expected_trimmed_to_160 = Html::escape('<blink>Lorem ipsum</blink> dolor sit amet, consectetur adipiscing elit. '
+      . 'Morbi diam dui, finibus et purus ac, elementum pretium augue.');
+    $this->assertNodeSummaryTokenReplacement($node, $expected_trimmed_to_160);
+
+    // Token [node:summary] should use trim settings of "Trimmed"
+    // formatter of teaser view mode.
+    $view_display->setComponent('body', [
+      'type' => 'text_trimmed',
+      'settings' => ['trim_length' => 80],
+    ]);
+    $view_display->save();
+    $expected_trimmed_to_80 = Html::escape('<blink>Lorem ipsum</blink> dolor sit amet, consectetur adipiscing elit.');
+    $this->assertNodeSummaryTokenReplacement($node, $expected_trimmed_to_80);
+
+    // Token [node:summary] should not pickup trim length setting of any other
+    // formatter rather than "Summary or trimmed" or "Trimmed", even if setting
+    // name matches.
+    // Fallbacks to default trim length setting (600).
+    $view_display->setComponent('body', [
+      'type' => 'text_default',
+      'settings' => ['trim_length' => 42],
+    ]);
+    $view_display->save();
+    $expected_trimmed_to_600 = Html::escape('<blink>Lorem ipsum</blink> dolor sit amet, consectetur adipiscing elit. '
+      . 'Morbi diam dui, finibus et purus ac, elementum pretium augue. Fusce lacus nisl, feugiat sit amet blandit sed, mattis eu ligula. '
+      . 'Duis laoreet dui non felis maximus fringilla. Maecenas tempor magna id urna dapibus, in elementum mauris consequat. '
+      . 'Praesent in urna non felis fringilla ullamcorper interdum eu risus. '
+      . 'Vestibulum id erat ultrices, varius est sed, dignissim dui. Vestibulum a dapibus nisl. '
+      . 'Maecenas vestibulum nibh a aliquet mollis. Donec ac justo eget justo interdum faucibus. '
+      . 'Aenean sit amet finibus turpis.');
+    $this->assertNodeSummaryTokenReplacement($node, $expected_trimmed_to_600);
+
+    // Token [node:summary] should not pickup trim length setting if teaser
+    // view mode does not exist.
+    // Fallbacks to "Summary or trimmed" default trim length setting (600).
+    $view_display->delete();
+    $this->assertNodeSummaryTokenReplacement($node, $expected_trimmed_to_600);
+  }
 
-    foreach ($tests as $input => $expected) {
-      $output = $this->tokenService->replace($input, ['node' => $node], ['language' => $this->interfaceLanguage]);
-      $this->assertEqual($output, $expected, new FormattableMarkup('Node token %token replaced for node without a summary.', ['%token' => $input]));
-    }
+  /**
+   * Asserts that [node:summary] token works as expected.
+   *
+   * @param \Drupal\node\NodeInterface $node
+   *   Node to assert against.
+   * @param string $expected
+   *   Expected token replacement output.
+   */
+  protected function assertNodeSummaryTokenReplacement(NodeInterface $node, $expected) {
+    $output = $this->tokenService->replace('[node:summary]', ['node' => $node], ['langcode' => $this->interfaceLanguage->getId()]);
+    $this->assertEqual($output, $expected, new FormattableMarkup('Node token %token replaced.', ['%token' => '[node:summary]']));
   }
 
 }
