From d562edaa141574ae3a48a6cbebce67e8054469a4 Mon Sep 17 00:00:00 2001
From: Lauri Eskola <lauri.eskola@acquia.com>
Date: Fri, 8 Dec 2023 19:59:57 +0200
Subject: [PATCH] Issue #3406612 by tedbow, phenaproxima: Exceptions in batch
 no longer are shown on the page: JavaScript error

---
 .../system/src/Controller/BatchController.php | 16 ++++++++
 .../batch_test/batch_test.callbacks.inc       | 12 ++++++
 .../modules/batch_test/batch_test.module      | 11 +++++
 .../src/Form/BatchTestSimpleForm.php          |  1 +
 .../Batch/ProcessingTest.php                  | 40 +++++++++++++++++++
 5 files changed, 80 insertions(+)
 create mode 100644 core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php

diff --git a/core/modules/system/src/Controller/BatchController.php b/core/modules/system/src/Controller/BatchController.php
index 804e5470e85f..7041d36427bb 100644
--- a/core/modules/system/src/Controller/BatchController.php
+++ b/core/modules/system/src/Controller/BatchController.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Batch\BatchStorageInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
@@ -14,6 +15,8 @@
  */
 class BatchController implements ContainerInjectionInterface {
 
+  use StringTranslationTrait;
+
   /**
    * Constructs a new BatchController.
    */
@@ -55,6 +58,19 @@ public function batchPage(Request $request) {
       return $output;
     }
     elseif (isset($output)) {
+      // Directly render a status message placeholder without any messages.
+      // Messages are not intended to be show on the batch page, but in the
+      // event an error in a AJAX callback the messages will be displayed.
+      // @todo Remove in https://drupal.org/i/3396099.
+      $output['batch_messages'] = [
+        '#theme' => 'status_messages',
+        '#message_list' => [],
+        '#status_headings' => [
+          'status' => $this->t('Status message'),
+          'error' => $this->t('Error message'),
+          'warning' => $this->t('Warning message'),
+        ],
+      ];
       $title = $output['#title'] ?? NULL;
       $page = [
         '#type' => 'page',
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
index b3ac6f303478..9f6e741fbb65 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
+++ b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
@@ -104,6 +104,18 @@ function _batch_test_callback_7($id, $sleep, &$context) {
   $context['results'][7][] = $id;
 }
 
+/**
+ * Implements callback_batch_operation().
+ *
+ * Performs a simple batch operation that optionally throws an exception.
+ */
+function _batch_test_callback_8(bool $throw_exception): void {
+  usleep(500);
+  if ($throw_exception) {
+    throw new Exception('Exception in batch');
+  }
+}
+
 /**
  * Implements callback_batch_operation().
  *
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.module b/core/modules/system/tests/modules/batch_test/batch_test.module
index 6dd80f61cffc..913b2986d175 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.module
+++ b/core/modules/system/tests/modules/batch_test/batch_test.module
@@ -190,6 +190,17 @@ function _batch_test_batch_7() {
   return $batch_builder->toArray() + ['batch_test_id' => 'batch_7'];
 }
 
+/**
+ * Batch 8: Throws an exception.
+ */
+function _batch_test_batch_8(): array {
+  $batch_builder = (new BatchBuilder())
+    ->setFile(\Drupal::service('extension.list.module')->getPath('batch_test') . '/batch_test.callbacks.inc')
+    ->addOperation('_batch_test_callback_8', [FALSE])
+    ->addOperation('_batch_test_callback_8', [TRUE]);
+  return $batch_builder->toArray() + ['batch_test_id' => 'batch_8'];
+}
+
 /**
  * Implements callback_batch_operation().
  *
diff --git a/core/modules/system/tests/modules/batch_test/src/Form/BatchTestSimpleForm.php b/core/modules/system/tests/modules/batch_test/src/Form/BatchTestSimpleForm.php
index 0cf0b301d608..d8b4858fcfbc 100644
--- a/core/modules/system/tests/modules/batch_test/src/Form/BatchTestSimpleForm.php
+++ b/core/modules/system/tests/modules/batch_test/src/Form/BatchTestSimpleForm.php
@@ -34,6 +34,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         'batch_4' => 'batch 4',
         'batch_6' => 'batch 6',
         'batch_7' => 'batch 7',
+        'batch_8' => 'batch 8',
       ],
       '#multiple' => TRUE,
     ];
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php b/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php
new file mode 100644
index 000000000000..79e3982312a9
--- /dev/null
+++ b/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\Tests\system\FunctionalJavascript\Batch;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * @group Batch
+ */
+class ProcessingTest extends WebDriverTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  protected static $modules = ['batch_test', 'test_page_test'];
+
+  /**
+   * {@inheritdoc}
+   *
+   * @todo Use the stark theme in https://drupal.org/i/3407067.
+   */
+  protected $defaultTheme = 'olivero';
+
+  /**
+   * Tests that a link to the error page is shown.
+   */
+  public function testLinkToErrorPageAppears(): void {
+    $edit = ['batch' => 'batch_8'];
+    $this->drupalGet('batch-test');
+    $this->submitForm($edit, 'Submit');
+    $this->assertNotNull($this->assertSession()->waitForLink('the error page'));
+    $this->assertSession()->assertNoEscaped('<');
+    $this->assertSession()->responseContains('Exception in batch');
+    $this->clickLink('the error page');
+    $this->assertSession()->pageTextContains('Redirection successful.');
+  }
+
+}
-- 
GitLab