Repository: jhipster/jhipster-lite (java)

Issue:
Implement the option to add new instructions at the end of `tasks.test` in the `build.gradle.kts`
### I suggest changing the existing GradleConfiguration API.

from:

```java
.gradleConfigurations()
  .configuration()
.and()
```

to:

```java
.configurations()
  .addConfigurarion("")
  .addTasksTestInstruction("")
.and()
```

### Here is how the changes will affect the existing code:

[At LocalProfileModuleFactory](https://github.com/jhipster/jhipster-lite/blob/main/src/main/java/tech/jhipster/lite/generator/server/springboot/localeprofile/domain/LocalProfileModuleFactory.java#L27-L43)  from:
```java
.gradleConfigurations()
  .configuration(
    """
    tasks.build {
      dependsOn("processResources")
    }

    tasks.processResources {
      filesMatching("**/*.yml") {
        filter { it.replace("@spring.profiles.active@", springProfilesActive) }
      }
      filesMatching("**/*.properties") {
        filter { it.replace("@spring.profiles.active@", springProfilesActive) }
      }
    }
    """
  )
```

to:

```java
.gradleConfigurations()
  .addConfigurarion(
    """
    tasks.build {
      dependsOn("processResources")
    }

    tasks.processResources {
      filesMatching("**/*.yml") {
        filter { it.replace("@spring.profiles.active@", springProfilesActive) }
      }
      filesMatching("**/*.properties") {
        filter { it.replace("@spring.profiles.active@", springProfilesActive) }
      }
    }
    """
  )
```

[At JacocoModuleFactory](https://github.com/jhipster/jhipster-lite/blob/main/src/main/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactory.java#L79-L82) from:
```java
 .optionalReplacements()
  .in(path("build.gradle.kts"))
  .add(EXISTING_BUILD_GRADLE_TASK_TEST_NEEDLE, ADD_JACOCO_BUILD_GRADLE_TASK_TEST)
  .and()
.and()
```

to:
```java
.configurations()
  .addTasksTestInstruction("""finalizedBy("jacocoTestReport")""")
.and()
```
 
The goal of this issue is to [make this possible](https://github.com/renanfranca/sample-frontend-gradle/pull/2/files#diff-c0dfa6bc7a8685217f70a860145fbdf416d449eaff052fa28352c5cec1a98c06L123-L124). Implement the option to add  instructions at the end of `tasks.test` in the `build.gradle.kts` 

Pull request:
support add instructions at the end of tasks.test in the build.gradle.kts
Fix #9813 

Patch:
diff --git a/src/main/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactory.java
index e7221690e..1d4b3a602 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactory.java
@@ -3,7 +3,6 @@ package tech.jhipster.lite.generator.server.javatool.jacoco.domain;
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
 import static tech.jhipster.lite.module.domain.mavenplugin.MavenBuildPhase.*;
 
-import java.util.regex.Pattern;
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.gradleplugin.GradleMainBuildPlugin;
 import tech.jhipster.lite.module.domain.javabuild.ArtifactId;
@@ -12,8 +11,6 @@ import tech.jhipster.lite.module.domain.javabuild.VersionSlug;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPlugin;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPlugin.MavenPluginOptionalBuilder;
 import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
-import tech.jhipster.lite.module.domain.replacement.ElementReplacer;
-import tech.jhipster.lite.module.domain.replacement.RegexReplacer;
 import tech.jhipster.lite.shared.error.domain.Assert;
 
 public class JacocoModuleFactory {
@@ -22,47 +19,6 @@ public class JacocoModuleFactory {
   private static final GroupId JACOCO_GROUP = groupId("org.jacoco");
   private static final ArtifactId JACOCO_ARTIFACT_ID = artifactId("jacoco-maven-plugin");
   private static final VersionSlug JACOCO_VERSION = versionSlug(JACOCO);
-  private static final Pattern BUILD_GRADLE_TASK_TEST_NEEDLE = Pattern.compile(
-    """
-    tasks.test {
-      filter {
-        includeTestsMatching("**Test*")
-        excludeTestsMatching("**IT*")
-        excludeTestsMatching("**CucumberTest*")
-      }
-      useJUnitPlatform()
-    }
-    """,
-    Pattern.LITERAL
-  );
-  private static final ElementReplacer EXISTING_BUILD_GRADLE_TASK_TEST_NEEDLE = new RegexReplacer(
-    (contentBeforeReplacement, replacement) -> BUILD_GRADLE_TASK_TEST_NEEDLE.matcher(contentBeforeReplacement).find(),
-    BUILD_GRADLE_TASK_TEST_NEEDLE
-  );
-  private static final String ADD_JACOCO_BUILD_GRADLE_TASK_TEST =
-    """
-    tasks.test {
-      filter {
-        includeTestsMatching("**Test*")
-        excludeTestsMatching("**IT*")
-        excludeTestsMatching("**CucumberTest*")
-      }
-      useJUnitPlatform()
-      finalizedBy("jacocoTestReport")
-    }
-    """;
-  private static final String ADD_JACOCO_WITH_MIN_COVERAGE_CHECK_BUILD_GRADLE_TASK_TEST =
-    """
-    tasks.test {
-      filter {
-        includeTestsMatching("**Test*")
-        excludeTestsMatching("**IT*")
-        excludeTestsMatching("**CucumberTest*")
-      }
-      useJUnitPlatform()
-      finalizedBy("jacocoTestCoverageVerification")
-    }
-    """;
 
   public JHipsterModule buildJacocoModule(JHipsterModuleProperties properties) {
     Assert.notNull("properties", properties);
@@ -76,11 +32,13 @@ public class JacocoModuleFactory {
       .gradlePlugins()
         .plugin(gradleJacocoPlugin())
         .and()
-      .optionalReplacements()
-        .in(path("build.gradle.kts"))
-        .add(EXISTING_BUILD_GRADLE_TASK_TEST_NEEDLE, ADD_JACOCO_BUILD_GRADLE_TASK_TEST)
+      .gradleConfigurations()
+        .addTasksTestInstruction(
+          """
+          finalizedBy("jacocoTestReport")\
+          """
+        )
         .and()
-      .and()
       .build();
     //@formatter:on
   }
@@ -97,11 +55,13 @@ public class JacocoModuleFactory {
       .gradlePlugins()
         .plugin(gradleJacocoWithMinCoverageCheckPlugin())
         .and()
-      .optionalReplacements()
-        .in(path("build.gradle.kts"))
-        .add(EXISTING_BUILD_GRADLE_TASK_TEST_NEEDLE, ADD_JACOCO_WITH_MIN_COVERAGE_CHECK_BUILD_GRADLE_TASK_TEST)
+      .gradleConfigurations()
+        .addTasksTestInstruction(
+          """
+          finalizedBy("jacocoTestCoverageVerification")\
+          """
+        )
         .and()
-      .and()
       .build();
     //@formatter:on
   }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/localeprofile/domain/LocalProfileModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/localeprofile/domain/LocalProfileModuleFactory.java
index 9854e2690..db34bd4e6 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/localeprofile/domain/LocalProfileModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/localeprofile/domain/LocalProfileModuleFactory.java
@@ -25,7 +25,7 @@ public class LocalProfileModuleFactory {
         .set(new PropertyKey(SPRING_PROFILES_ACTIVE), new PropertyValue(""))
         .and()
       .gradleConfigurations()
-        .configuration(
+        .addConfiguration(
           """
           tasks.build {
             dependsOn("processResources")
diff --git a/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/JHipsterModuleGradleConfigurations.java b/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/JHipsterModuleGradleConfigurations.java
index a0ae12f73..040321f04 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/JHipsterModuleGradleConfigurations.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/JHipsterModuleGradleConfigurations.java
@@ -2,19 +2,25 @@ package tech.jhipster.lite.module.domain.gradleconfiguration;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.function.Function;
+import java.util.stream.Stream;
 import tech.jhipster.lite.module.domain.JHipsterModule.JHipsterModuleBuilder;
 import tech.jhipster.lite.module.domain.javabuild.command.AddGradleConfiguration;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradleTasksTestInstruction;
 import tech.jhipster.lite.module.domain.javabuild.command.JavaBuildCommands;
 import tech.jhipster.lite.shared.error.domain.Assert;
 
 public final class JHipsterModuleGradleConfigurations {
 
   private final Collection<GradleConfiguration> configurations;
+  private final Collection<GradleTasksTestInstruction> tasksTestInstructions;
 
   public JHipsterModuleGradleConfigurations(JHipsterModuleGradleConfigurationBuilder builder) {
     Assert.notNull("configurations", builder.configurations);
+    Assert.notNull("tasksTestInstructions", builder.tasksTestInstructions);
 
     this.configurations = builder.configurations;
+    this.tasksTestInstructions = builder.tasksTestInstructions;
   }
 
   public static JHipsterModuleGradleConfigurationBuilder builder(JHipsterModuleBuilder module) {
@@ -22,13 +28,30 @@ public final class JHipsterModuleGradleConfigurations {
   }
 
   public JavaBuildCommands buildChanges() {
-    return new JavaBuildCommands(configurations.stream().map(configuration -> new AddGradleConfiguration(configuration.get())).toList());
+    return Stream.of(configurationToAddGradleConfiguration(), tasksTestInstructionsToAddTasksTestInstruction())
+      .flatMap(Function.identity())
+      .reduce(JavaBuildCommands.EMPTY, JavaBuildCommands::merge);
+  }
+
+  private Stream<JavaBuildCommands> configurationToAddGradleConfiguration() {
+    return Stream.of(
+      new JavaBuildCommands(configurations.stream().map(configuration -> new AddGradleConfiguration(configuration.get())).toList())
+    );
+  }
+
+  private Stream<JavaBuildCommands> tasksTestInstructionsToAddTasksTestInstruction() {
+    return Stream.of(
+      new JavaBuildCommands(
+        tasksTestInstructions.stream().map(tasksTestInstruction -> new AddGradleTasksTestInstruction(tasksTestInstruction.get())).toList()
+      )
+    );
   }
 
   public static final class JHipsterModuleGradleConfigurationBuilder {
 
     private final JHipsterModuleBuilder module;
     private final Collection<GradleConfiguration> configurations = new ArrayList<>();
+    private final Collection<GradleTasksTestInstruction> tasksTestInstructions = new ArrayList<>();
 
     private JHipsterModuleGradleConfigurationBuilder(JHipsterModuleBuilder module) {
       Assert.notNull("module", module);
@@ -36,12 +59,18 @@ public final class JHipsterModuleGradleConfigurations {
       this.module = module;
     }
 
-    public JHipsterModuleGradleConfigurationBuilder configuration(String configuration) {
+    public JHipsterModuleGradleConfigurationBuilder addConfiguration(String configuration) {
       configurations.add(new GradleConfiguration(configuration));
 
       return this;
     }
 
+    public JHipsterModuleGradleConfigurationBuilder addTasksTestInstruction(String instruction) {
+      tasksTestInstructions.add(new GradleTasksTestInstruction(instruction));
+
+      return this;
+    }
+
     public JHipsterModuleBuilder and() {
       return module;
     }
diff --git a/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/JavaBuildCommand.java b/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/JavaBuildCommand.java
index ffc35f7b6..a5d66b2dd 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/JavaBuildCommand.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/JavaBuildCommand.java
@@ -13,4 +13,5 @@ public sealed interface JavaBuildCommand
     RemoveJavaDependencyManagement,
     SetBuildProperty,
     SetVersion,
-    AddGradleConfiguration {}
+    AddGradleConfiguration,
+    AddGradleTasksTestInstruction {}
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
index 4dd1562f6..589e2b806 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
@@ -80,6 +80,7 @@ public class FileSystemJavaBuildCommandsHandler {
       case AddJavaBuildProfile addJavaBuildProfile -> handler.handle(addJavaBuildProfile);
       case AddGradlePlugin addGradlePlugin -> handler.handle(addGradlePlugin);
       case AddGradleConfiguration addGradleConfiguration -> handler.handle(addGradleConfiguration);
+      case AddGradleTasksTestInstruction addGradleTasksTestInstruction -> handler.handle(addGradleTasksTestInstruction);
     }
   }
 }
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/JavaDependenciesCommandHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/JavaDependenciesCommandHandler.java
index 1ab2b09e1..22f911ff0 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/JavaDependenciesCommandHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/JavaDependenciesCommandHandler.java
@@ -26,4 +26,6 @@ public interface JavaDependenciesCommandHandler {
   void handle(AddGradlePlugin command);
 
   void handle(AddGradleConfiguration command);
+
+  void handle(AddGradleTasksTestInstruction command);
 }
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
index 221ec09a3..35b0f5a3b 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
@@ -1,20 +1,23 @@
 package tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
-import static tech.jhipster.lite.module.domain.replacement.ReplacementCondition.always;
-import static tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle.VersionsCatalog.libraryAlias;
-import static tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle.VersionsCatalog.pluginAlias;
+import static tech.jhipster.lite.module.domain.replacement.ReplacementCondition.*;
+import static tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle.VersionsCatalog.*;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-import tech.jhipster.lite.module.domain.*;
+import tech.jhipster.lite.module.domain.Indentation;
+import tech.jhipster.lite.module.domain.JHipsterModuleContext;
+import tech.jhipster.lite.module.domain.JHipsterProjectFilePath;
 import tech.jhipster.lite.module.domain.buildproperties.BuildProperty;
 import tech.jhipster.lite.module.domain.buildproperties.PropertyKey;
 import tech.jhipster.lite.module.domain.file.*;
@@ -70,6 +73,7 @@ public class GradleCommandHandler implements JavaDependenciesCommandHandler {
     "^// jhipster-needle-gradle-free-configuration-blocks$",
     Pattern.MULTILINE
   );
+  private static final Pattern GRADLE_TASKS_TEST_NEEDLE = Pattern.compile("^\\s+// jhipster-needle-gradle-tasks-test$", Pattern.MULTILINE);
   private static final String PROFILE_CONDITIONAL_TEMPLATE =
     """
     if (profiles.contains("%s")) {
@@ -527,4 +531,20 @@ public class GradleCommandHandler implements JavaDependenciesCommandHandler {
       context
     );
   }
+
+  @Override
+  public void handle(AddGradleTasksTestInstruction command) {
+    MandatoryReplacer replacer = new MandatoryReplacer(
+      new RegexNeedleBeforeReplacer(
+        (contentBeforeReplacement, newText) -> !contentBeforeReplacement.contains(newText),
+        GRADLE_TASKS_TEST_NEEDLE
+      ),
+      indentation.times(1) + command.get()
+    );
+    fileReplacer.handle(
+      projectFolder,
+      ContentReplacers.of(new MandatoryFileReplacer(new JHipsterProjectFilePath(BUILD_GRADLE_FILE), replacer)),
+      context
+    );
+  }
 }
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/maven/MavenCommandHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/maven/MavenCommandHandler.java
index 802a9c18f..3263087ae 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/maven/MavenCommandHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/maven/MavenCommandHandler.java
@@ -14,13 +14,18 @@ import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 import tech.jhipster.lite.module.domain.Indentation;
-import tech.jhipster.lite.module.domain.buildproperties.*;
+import tech.jhipster.lite.module.domain.buildproperties.BuildProperty;
+import tech.jhipster.lite.module.domain.buildproperties.PropertyKey;
+import tech.jhipster.lite.module.domain.buildproperties.PropertyValue;
 import tech.jhipster.lite.module.domain.javabuild.MavenBuildExtension;
 import tech.jhipster.lite.module.domain.javabuild.VersionSlug;
 import tech.jhipster.lite.module.domain.javabuild.command.*;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileActivation;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileId;
-import tech.jhipster.lite.module.domain.javadependency.*;
+import tech.jhipster.lite.module.domain.javadependency.DependencyId;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyClassifier;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
 import tech.jhipster.lite.module.domain.mavenplugin.*;
 import tech.jhipster.lite.module.infrastructure.secondary.javadependency.JavaDependenciesCommandHandler;
 import tech.jhipster.lite.shared.enumeration.domain.Enums;
@@ -340,6 +345,11 @@ public class MavenCommandHandler implements JavaDependenciesCommandHandler {
     // Gradle commands are ignored
   }
 
+  @Override
+  public void handle(AddGradleTasksTestInstruction command) {
+    // Gradle commands are ignored
+  }
+
   private Plugin toMavenPlugin(AddMavenPlugin command) {
     Plugin mavenPlugin = new Plugin();
     mavenPlugin.setArtifactId(command.dependencyId().artifactId().get());
diff --git a/src/main/resources/generator/buildtool/gradle/build.gradle.kts.mustache b/src/main/resources/generator/buildtool/gradle/build.gradle.kts.mustache
index 706dc6152..4a602927e 100644
--- a/src/main/resources/generator/buildtool/gradle/build.gradle.kts.mustache
+++ b/src/main/resources/generator/buildtool/gradle/build.gradle.kts.mustache
@@ -44,6 +44,7 @@ tasks.test {
     excludeTestsMatching("**CucumberTest*")
   }
   useJUnitPlatform()
+  // jhipster-needle-gradle-tasks-test
 }
 
 val integrationTest = task<Test>("integrationTest") {
diff --git a/src/main/resources/generator/dependencies/angular/package.json b/src/main/resources/generator/dependencies/angular/package.json
index e4ae0fae8..79a01612d 100644
--- a/src/main/resources/generator/dependencies/angular/package.json
+++ b/src/main/resources/generator/dependencies/angular/package.json
@@ -1,8 +1,8 @@
 {
   "dependencies": {
-    "@angular/cdk": "17.3.9",
-    "@angular/core": "17.3.9",
-    "@angular/material": "17.3.9",
+    "@angular/cdk": "17.3.8",
+    "@angular/core": "17.3.8",
+    "@angular/material": "17.3.8",
     "keycloak-js": "24.0.4",
     "rxjs": "7.8.1",
     "tslib": "2.6.2",
@@ -11,13 +11,13 @@
   "devDependencies": {
     "@angular-builders/jest": "17.0.3",
     "@angular-devkit/build-angular": "17.3.7",
-    "@angular-eslint/builder": "17.4.1",
-    "@angular-eslint/eslint-plugin": "17.4.1",
-    "@angular-eslint/eslint-plugin-template": "17.4.1",
-    "@angular-eslint/schematics": "17.4.1",
-    "@angular-eslint/template-parser": "17.4.1",
+    "@angular-eslint/builder": "17.4.0",
+    "@angular-eslint/eslint-plugin": "17.4.0",
+    "@angular-eslint/eslint-plugin-template": "17.4.0",
+    "@angular-eslint/schematics": "17.4.0",
+    "@angular-eslint/template-parser": "17.4.0",
     "@angular/cli": "17.3.7",
-    "@angular/compiler-cli": "17.3.9",
+    "@angular/compiler-cli": "17.3.8",
     "@types/jest": "29.5.12",
     "@types/node": "20.12.12",
     "@typescript-eslint/eslint-plugin": "7.9.0",
diff --git a/src/main/resources/generator/dependencies/common/package.json b/src/main/resources/generator/dependencies/common/package.json
index 321986223..94d050648 100644
--- a/src/main/resources/generator/dependencies/common/package.json
+++ b/src/main/resources/generator/dependencies/common/package.json
@@ -26,7 +26,7 @@
     "lint-staged": "15.2.2",
     "mkdirp": "3.0.1",
     "node": "20.13.1",
-    "npm": "10.8.0",
+    "npm": "10.7.0",
     "npm-run-all": "4.1.5",
     "onchange": "7.1.0",
     "path-exists-cli": "2.0.0",
diff --git a/src/main/resources/generator/dependencies/vue/package.json b/src/main/resources/generator/dependencies/vue/package.json
index 4ec2eea7d..a27a177dc 100644
--- a/src/main/resources/generator/dependencies/vue/package.json
+++ b/src/main/resources/generator/dependencies/vue/package.json
@@ -27,6 +27,6 @@
     "vite": "5.2.11",
     "vitest": "1.6.0",
     "vitest-sonar-reporter": "2.0.0",
-    "vue-tsc": "2.0.18"
+    "vue-tsc": "2.0.17"
   }
 }


Test patch:
diff --git a/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/GradleTasksTestInstruction.java b/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/GradleTasksTestInstruction.java
new file mode 100644
index 000000000..2b3983640
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/domain/gradleconfiguration/GradleTasksTestInstruction.java
@@ -0,0 +1,13 @@
+package tech.jhipster.lite.module.domain.gradleconfiguration;
+
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+record GradleTasksTestInstruction(String instruction) {
+  public GradleTasksTestInstruction {
+    Assert.notBlank("instruction", instruction);
+  }
+
+  public String get() {
+    return instruction();
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/AddGradleTasksTestInstruction.java b/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/AddGradleTasksTestInstruction.java
new file mode 100644
index 000000000..beba3b81f
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/domain/javabuild/command/AddGradleTasksTestInstruction.java
@@ -0,0 +1,13 @@
+package tech.jhipster.lite.module.domain.javabuild.command;
+
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+public record AddGradleTasksTestInstruction(String instruction) implements JavaBuildCommand {
+  public AddGradleTasksTestInstruction {
+    Assert.notNull("instruction", instruction);
+  }
+
+  public String get() {
+    return instruction;
+  }
+}
diff --git a/src/test/java/tech/jhipster/lite/cucumber/rest/CucumberRestTestContext.java b/src/test/java/tech/jhipster/lite/cucumber/rest/CucumberRestTestContext.java
index 0fc55c175..08706d60d 100644
--- a/src/test/java/tech/jhipster/lite/cucumber/rest/CucumberRestTestContext.java
+++ b/src/test/java/tech/jhipster/lite/cucumber/rest/CucumberRestTestContext.java
@@ -196,9 +196,9 @@ public final class CucumberRestTestContext {
 
     private void retry() {
       try {
-        ClientHttpResponse clientHttpResponse = execution.execute(request, body);
+        ClientHttpResponse response = execution.execute(request, body);
 
-        addResponse(request, clientHttpResponse, execution, body);
+        addResponse(request, response, execution, body);
       } catch (IOException e) {
         throw new AssertionError("Error while retrying last call: " + e.getMessage(), e);
       }
diff --git a/src/test/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactoryTest.java
index 26e406bc4..76af50239 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/javatool/jacoco/domain/JacocoModuleFactoryTest.java
@@ -180,15 +180,8 @@ class JacocoModuleFactoryTest {
         )
         .containing(
           """
-          tasks.test {
-            filter {
-              includeTestsMatching("**Test*")
-              excludeTestsMatching("**IT*")
-              excludeTestsMatching("**CucumberTest*")
-            }
-            useJUnitPlatform()
             finalizedBy("jacocoTestReport")
-          }
+            // jhipster-needle-gradle-tasks-test
           """
         )
         .and()
@@ -252,15 +245,8 @@ class JacocoModuleFactoryTest {
       )
       .containing(
         """
-        tasks.test {
-          filter {
-            includeTestsMatching("**Test*")
-            excludeTestsMatching("**IT*")
-            excludeTestsMatching("**CucumberTest*")
-          }
-          useJUnitPlatform()
           finalizedBy("jacocoTestCoverageVerification")
-        }
+          // jhipster-needle-gradle-tasks-test
         """
       )
       .and()
diff --git a/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java b/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
index 49db31ac8..b84c69854 100644
--- a/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
+++ b/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
@@ -100,7 +100,7 @@ public final class JHipsterModulesFixture {
       .plugin(asciidoctorPlugin())
       .and()
     .gradleConfigurations()
-      .configuration(
+      .addConfiguration(
         """
         tasks.build {
           dependsOn("processResources")
@@ -120,6 +120,13 @@ public final class JHipsterModulesFixture {
       .plugin(jacocoGradlePlugin())
       .plugin(checkstyleGradlePlugin())
       .and()
+    .gradleConfigurations()
+      .addTasksTestInstruction(
+        """
+        finalizedBy("jacocoTestReport")\
+        """
+      )
+    .and()
     .javaBuildProfiles()
       .addProfile(localBuildProfile())
         .activation(buildProfileActivation().activeByDefault(false))
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepositoryTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepositoryTest.java
index 71aed8316..79a30d725 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepositoryTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepositoryTest.java
@@ -488,6 +488,12 @@ class FileSystemJHipsterModulesRepositoryTest {
           // jhipster-needle-gradle-free-configuration-blocks
           """
         )
+        .containing(
+          """
+            finalizedBy("jacocoTestReport")
+            // jhipster-needle-gradle-tasks-test
+          """
+        )
         .and()
       .hasFile("buildSrc/build.gradle.kts")
         .containing(
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemPackageJsonHandlerTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemPackageJsonHandlerTest.java
index f2edffa82..2ec26115a 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemPackageJsonHandlerTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemPackageJsonHandlerTest.java
@@ -49,7 +49,7 @@ class FileSystemPackageJsonHandlerTest {
         packageJson.handle(
           Indentation.DEFAULT,
           emptyFolder(),
-          packageJson(p -> p.addScript(scriptKey("key"), scriptCommand("value"))),
+          packageJson(packageJson -> packageJson.addScript(scriptKey("key"), scriptCommand("value"))),
           emptyModuleContext()
         )
     ).isExactlyInstanceOf(MissingPackageJsonException.class);
@@ -68,7 +68,7 @@ class FileSystemPackageJsonHandlerTest {
     packageJson.handle(
       Indentation.DEFAULT,
       folder,
-      packageJson(p -> p.addDevDependency(packageName("@playwright/test"), VersionSource.COMMON)),
+      packageJson(packageJson -> packageJson.addDevDependency(packageName("@playwright/test"), VersionSource.COMMON)),
       emptyModuleContext()
     );
 
@@ -82,7 +82,7 @@ class FileSystemPackageJsonHandlerTest {
     packageJson.handle(
       Indentation.DEFAULT,
       folder,
-      packageJson(p -> p.addScript(scriptKey("@prettier/plugin-xml"), scriptCommand("test"))),
+      packageJson(packageJson -> packageJson.addScript(scriptKey("@prettier/plugin-xml"), scriptCommand("test"))),
       emptyModuleContext()
     );
 
@@ -116,7 +116,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addScript(scriptKey("key"), scriptCommand("value"))),
+        packageJson(packageJson -> packageJson.addScript(scriptKey("key"), scriptCommand("value"))),
         emptyModuleContext()
       );
 
@@ -139,8 +139,8 @@ class FileSystemPackageJsonHandlerTest {
         Indentation.DEFAULT,
         folder,
         packageJson(
-          p -> p.addScript(scriptKey("key"), scriptCommand("value")),
-          p -> p.addScript(scriptKey("key2"), scriptCommand("value2"))
+          packageJson -> packageJson.addScript(scriptKey("key"), scriptCommand("value")),
+          packageJson -> packageJson.addScript(scriptKey("key2"), scriptCommand("value2"))
         ),
         emptyModuleContext()
       );
@@ -163,7 +163,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addScript(scriptKey("key"), scriptCommand("value"))),
+        packageJson(packageJson -> packageJson.addScript(scriptKey("key"), scriptCommand("value"))),
         emptyModuleContext()
       );
 
@@ -184,7 +184,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addScript(scriptKey("build"), scriptCommand("test"))),
+        packageJson(packageJson -> packageJson.addScript(scriptKey("build"), scriptCommand("test"))),
         emptyModuleContext()
       );
 
@@ -207,7 +207,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addScript(scriptKey("build"), scriptCommand("test"))),
+        packageJson(packageJson -> packageJson.addScript(scriptKey("build"), scriptCommand("test"))),
         emptyModuleContext()
       );
 
@@ -234,7 +234,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDevDependency(packageName("@prettier/plugin-xmll"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDevDependency(packageName("@prettier/plugin-xmll"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -258,7 +258,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDevDependency(packageName("@prettier/plugin-xmll"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDevDependency(packageName("@prettier/plugin-xmll"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -281,7 +281,10 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDevDependency(packageName("@angular/animations"), VersionSource.ANGULAR, packageName("@angular/core"))),
+        packageJson(
+          packageJson ->
+            packageJson.addDevDependency(packageName("@angular/animations"), VersionSource.ANGULAR, packageName("@angular/core"))
+        ),
         emptyModuleContext()
       );
 
@@ -303,7 +306,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDevDependency(packageName("@prettier/plugin-xml"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDevDependency(packageName("@prettier/plugin-xml"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -326,7 +329,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.removeDevDependency(packageName("@prettier/plugin-xml"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.removeDevDependency(packageName("@prettier/plugin-xml"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -356,7 +359,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -380,7 +383,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDependency(packageName("@fortawesome/fontawesome-svg-coree"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDependency(packageName("@fortawesome/fontawesome-svg-coree"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -403,7 +406,9 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDependency(packageName("@angular/animations"), VersionSource.ANGULAR, packageName("@angular/core"))),
+        packageJson(
+          packageJson -> packageJson.addDependency(packageName("@angular/animations"), VersionSource.ANGULAR, packageName("@angular/core"))
+        ),
         emptyModuleContext()
       );
 
@@ -425,7 +430,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.addDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.addDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
@@ -448,7 +453,7 @@ class FileSystemPackageJsonHandlerTest {
       packageJson.handle(
         Indentation.DEFAULT,
         folder,
-        packageJson(p -> p.removeDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
+        packageJson(packageJson -> packageJson.removeDependency(packageName("@fortawesome/fontawesome-svg-core"), VersionSource.COMMON)),
         emptyModuleContext()
       );
 
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
index 7861be4e1..6c4fe709c 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
@@ -1,12 +1,14 @@
 package tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle;
 
 import static org.assertj.core.api.Assertions.*;
-import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE;
+import static org.junit.jupiter.params.provider.EnumSource.Mode.*;
 import static tech.jhipster.lite.TestFileUtils.*;
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
 import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.*;
 
-import java.nio.file.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.stream.Stream;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -15,15 +17,22 @@ import org.junit.jupiter.params.provider.EnumSource;
 import org.junit.jupiter.params.provider.MethodSource;
 import tech.jhipster.lite.UnitTest;
 import tech.jhipster.lite.module.domain.Indentation;
-import tech.jhipster.lite.module.domain.buildproperties.*;
+import tech.jhipster.lite.module.domain.buildproperties.BuildProperty;
+import tech.jhipster.lite.module.domain.buildproperties.PropertyKey;
+import tech.jhipster.lite.module.domain.buildproperties.PropertyValue;
 import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.gradleplugin.GradlePlugin;
 import tech.jhipster.lite.module.domain.javabuild.command.*;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileActivation;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileId;
-import tech.jhipster.lite.module.domain.javadependency.*;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyType;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyVersion;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
-import tech.jhipster.lite.module.infrastructure.secondary.*;
+import tech.jhipster.lite.module.infrastructure.secondary.FileSystemJHipsterModuleFiles;
+import tech.jhipster.lite.module.infrastructure.secondary.FileSystemProjectFiles;
+import tech.jhipster.lite.module.infrastructure.secondary.FileSystemReplacer;
 
 @UnitTest
 class GradleCommandHandlerTest {
@@ -1491,7 +1500,7 @@ class GradleCommandHandlerTest {
         """
         tasks.build {
           dependsOn("processResources")
-        }
+        }\
         """
       )
     );
@@ -1501,19 +1510,86 @@ class GradleCommandHandlerTest {
         """
         tasks.build {
           dependsOn("processResources")
-        }
+        }\
         """
       )
     );
 
-    assertThat(buildGradleContent(projectFolder)).contains(
+    assertThat(buildGradleContent(projectFolder)).containsOnlyOnce(
       """
-
       tasks.build {
         dependsOn("processResources")
+      }\
+      """
+    );
+  }
+
+  @Test
+  void shouldAddTasksTestInstruction() {
+    JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
+
+    new GradleCommandHandler(Indentation.DEFAULT, projectFolder, emptyModuleContext(), files, fileReplacer).handle(
+      new AddGradleTasksTestInstruction(
+        """
+        dependsOn("testNpm")\
+        """
+      )
+    );
+
+    assertThat(buildGradleContent(projectFolder)).contains(
+      """
+      tasks.test {
+        filter {
+          includeTestsMatching("**Test*")
+          excludeTestsMatching("**IT*")
+          excludeTestsMatching("**CucumberTest*")
+        }
+        useJUnitPlatform()
+        dependsOn("testNpm")
+        // jhipster-needle-gradle-tasks-test
       }
+      """
+    );
+  }
 
-      // jhipster-needle-gradle-free-configuration-blocks\
+  @Test
+  void shouldNotDuplicateExistingTasksTestInstruction() {
+    JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
+    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+      Indentation.DEFAULT,
+      projectFolder,
+      emptyModuleContext(),
+      files,
+      fileReplacer
+    );
+    gradleCommandHandler.handle(
+      new AddGradleTasksTestInstruction(
+        """
+        dependsOn("testNpm")\
+        """
+      )
+    );
+
+    gradleCommandHandler.handle(
+      new AddGradleTasksTestInstruction(
+        """
+        dependsOn("testNpm")\
+        """
+      )
+    );
+
+    assertThat(buildGradleContent(projectFolder)).contains(
+      """
+      tasks.test {
+        filter {
+          includeTestsMatching("**Test*")
+          excludeTestsMatching("**IT*")
+          excludeTestsMatching("**CucumberTest*")
+        }
+        useJUnitPlatform()
+        dependsOn("testNpm")
+        // jhipster-needle-gradle-tasks-test
+      }
       """
     );
   }
diff --git a/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/FileSystemProjectsRepositoryTest.java b/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/FileSystemProjectsRepositoryTest.java
index 38ff6d9bd..70e1bf311 100644
--- a/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/FileSystemProjectsRepositoryTest.java
+++ b/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/FileSystemProjectsRepositoryTest.java
@@ -139,9 +139,9 @@ class FileSystemProjectsRepositoryTest {
       ObjectMapper json = mock(ObjectMapper.class);
       when(json.writerWithDefaultPrettyPrinter()).thenReturn(writer);
 
-      FileSystemProjectsRepository fileSystemProjectsRepository = new FileSystemProjectsRepository(json, mock(ProjectFormatter.class));
+      FileSystemProjectsRepository projects = new FileSystemProjectsRepository(json, mock(ProjectFormatter.class));
 
-      assertThatThrownBy(() -> fileSystemProjectsRepository.save(projectHistory())).isExactlyInstanceOf(GeneratorException.class);
+      assertThatThrownBy(() -> projects.save(projectHistory())).isExactlyInstanceOf(GeneratorException.class);
     }
 
     @Test
@@ -178,9 +178,9 @@ class FileSystemProjectsRepositoryTest {
       ObjectMapper json = mock(ObjectMapper.class);
       when(json.readValue(any(byte[].class), eq(PersistedProjectHistory.class))).thenThrow(IOException.class);
 
-      FileSystemProjectsRepository fileSystemProjectsRepository = new FileSystemProjectsRepository(json, mock(ProjectFormatter.class));
+      FileSystemProjectsRepository projects = new FileSystemProjectsRepository(json, mock(ProjectFormatter.class));
 
-      assertThatThrownBy(() -> fileSystemProjectsRepository.getHistory(path)).isExactlyInstanceOf(GeneratorException.class);
+      assertThatThrownBy(() -> projects.getHistory(path)).isExactlyInstanceOf(GeneratorException.class);
     }
 
     @Test
diff --git a/src/test/resources/projects/empty-gradle/build.gradle.kts b/src/test/resources/projects/empty-gradle/build.gradle.kts
index 89cc51dc6..21d581bf1 100644
--- a/src/test/resources/projects/empty-gradle/build.gradle.kts
+++ b/src/test/resources/projects/empty-gradle/build.gradle.kts
@@ -45,6 +45,7 @@ tasks.test {
     excludeTestsMatching("**CucumberTest*")
   }
   useJUnitPlatform()
+  // jhipster-needle-gradle-tasks-test
 }
 
 val integrationTest = task<Test>("integrationTest") {
diff --git a/src/test/resources/projects/init-gradle/build.gradle.kts b/src/test/resources/projects/init-gradle/build.gradle.kts
index 08b7d8907..665862f57 100644
--- a/src/test/resources/projects/init-gradle/build.gradle.kts
+++ b/src/test/resources/projects/init-gradle/build.gradle.kts
@@ -45,6 +45,7 @@ tasks.test {
     excludeTestsMatching("**CucumberTest*")
   }
   useJUnitPlatform()
+  // jhipster-needle-gradle-tasks-test
 }
 
 val integrationTest = task<Test>("integrationTest") {