diff --git a/src/main/java/tech/jhipster/lite/generator/client/angular/core/application/AngularApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/angular/core/application/AngularApplicationService.java index 9f5f9522195..5b36b1a36e9 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/angular/core/application/AngularApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/angular/core/application/AngularApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.client.angular.core.domain.AngularModuleFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class AngularApplicationService { private final AngularModuleFactory factory; - public AngularApplicationService() { - factory = new AngularModuleFactory(); + public AngularApplicationService(NpmLazyInstaller npmLazyInstaller) { + factory = new AngularModuleFactory(npmLazyInstaller); } public JHipsterModule buildInitModule(JHipsterModuleProperties properties) { diff --git a/src/main/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactory.java index e18e0d0412b..d2b5de69fc9 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactory.java @@ -17,6 +17,7 @@ import tech.jhipster.lite.module.domain.Indentation; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.packagejson.PackageName; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @@ -26,6 +27,11 @@ public class AngularModuleFactory { private static final String ENGINES_NEEDLE = " \"engines\":"; private static final PackageName ANGULAR_CORE_PACKAGE = packageName("@angular/core"); + private final NpmLazyInstaller npmLazyInstaller; + + public AngularModuleFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } public JHipsterModule buildModule(JHipsterModuleProperties properties) { //@formatter:off @@ -82,6 +88,9 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) { .addScript(scriptKey("watch:test"), scriptCommand("ng test --watch")) .addScript(scriptKey("lint"), scriptCommand("eslint .")) .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .files() .add(SOURCE.template("angular.json"), to("angular.json")) .add(SOURCE.file("tsconfig.json"), to("tsconfig.json")) diff --git a/src/main/java/tech/jhipster/lite/generator/client/react/core/application/ReactCoreApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/react/core/application/ReactCoreApplicationService.java index c9438980ebe..1a789fc1f52 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/react/core/application/ReactCoreApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/react/core/application/ReactCoreApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.client.react.core.domain.ReactCoreModulesFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class ReactCoreApplicationService { private final ReactCoreModulesFactory factory; - public ReactCoreApplicationService() { - factory = new ReactCoreModulesFactory(); + public ReactCoreApplicationService(NpmLazyInstaller npmLazyInstaller) { + factory = new ReactCoreModulesFactory(npmLazyInstaller); } public JHipsterModule buildModule(JHipsterModuleProperties properties) { diff --git a/src/main/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactory.java b/src/main/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactory.java index af68e0c4381..86467a94cb2 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactory.java @@ -23,6 +23,7 @@ import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterDestination; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; import tech.jhipster.lite.module.domain.replacement.MandatoryReplacer; @@ -47,6 +48,12 @@ public class ReactCoreModulesFactory { private static final String TEST_PRIMARY = "src/test/webapp/unit/home/infrastructure/primary"; private static final String DEFAULT_TSCONFIG_PATH = "\"@/*\": [\"src/main/webapp/app/*\"]"; + private final NpmLazyInstaller npmLazyInstaller; + + public ReactCoreModulesFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } + public JHipsterModule buildModule(JHipsterModuleProperties properties) { //@formatter:off return moduleBuilder(properties) @@ -74,7 +81,10 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) { .addScript(scriptKey("build:vite"), scriptCommand("vite build --emptyOutDir")) .addScript(scriptKey("preview"), scriptCommand("vite preview")) .addScript(scriptKey("start"), scriptCommand("vite")) - .and() + .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .files() .batch(SOURCE, to(".")) .addTemplate("vite.config.ts") diff --git a/src/main/java/tech/jhipster/lite/generator/client/svelte/core/application/SvelteApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/svelte/core/application/SvelteApplicationService.java index ab788c60b8e..fe0b5894c46 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/svelte/core/application/SvelteApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/svelte/core/application/SvelteApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.client.svelte.core.domain.SvelteModuleFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class SvelteApplicationService { private final SvelteModuleFactory factory; - public SvelteApplicationService() { - this.factory = new SvelteModuleFactory(); + public SvelteApplicationService(NpmLazyInstaller npmLazyInstaller) { + this.factory = new SvelteModuleFactory(npmLazyInstaller); } public JHipsterModule buildModule(JHipsterModuleProperties project) { diff --git a/src/main/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactory.java index 84256079763..3c65946c8d1 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactory.java @@ -17,6 +17,7 @@ import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterDestination; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; import tech.jhipster.lite.shared.error.domain.Assert; @@ -33,6 +34,12 @@ public class SvelteModuleFactory { private static final JHipsterSource PRIMARY_TEST_SOURCE = SOURCE.append("src/test/unit/common/primary/app"); private static final JHipsterDestination PRIMARY_TEST_DESTINATION = to("src/test/webapp/unit/common/primary/app"); + private final NpmLazyInstaller npmLazyInstaller; + + public SvelteModuleFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } + public JHipsterModule buildSvelteModule(JHipsterModuleProperties properties) { Assert.notNull("properties", properties); @@ -83,6 +90,9 @@ public JHipsterModule buildSvelteModule(JHipsterModuleProperties properties) { .addScript(scriptKey("test:coverage"), scriptCommand("vitest run --coverage")) .addScript(scriptKey("test:watch"), scriptCommand("vitest --")) .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .optionalReplacements() .in(path("package.json")) .add(lineBeforeText(ENGINES_NEEDLE), type(properties.indentation())) diff --git a/src/main/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationService.java index 26bd495b4b9..141bc930e03 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.client.vue.core.domain.VueModulesFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class VueApplicationService { private final VueModulesFactory factory; - public VueApplicationService() { - factory = new VueModulesFactory(); + public VueApplicationService(NpmLazyInstaller npmLazyInstaller) { + factory = new VueModulesFactory(npmLazyInstaller); } public JHipsterModule buildVueModule(JHipsterModuleProperties properties) { diff --git a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactory.java b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactory.java index 4e4e03664bf..6119d26c382 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactory.java @@ -22,6 +22,7 @@ import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterDestination; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; import tech.jhipster.lite.module.domain.replacement.MandatoryReplacer; import tech.jhipster.lite.shared.error.domain.Assert; @@ -51,6 +52,12 @@ public class VueModulesFactory { app.use(pinia); """; + private final NpmLazyInstaller npmLazyInstaller; + + public VueModulesFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } + public JHipsterModule buildVueModule(JHipsterModuleProperties properties) { //@formatter:off return moduleBuilder(properties) @@ -80,6 +87,9 @@ public JHipsterModule buildVueModule(JHipsterModuleProperties properties) { .addScript(scriptKey("start"), scriptCommand("vite")) .addScript(scriptKey("watch:tsc"), scriptCommand("npm run build:tsc -- --watch")) .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .files() .add(SOURCE.file("tsconfig.build.json"), to("tsconfig.build.json")) .batch(SOURCE, to(".")) diff --git a/src/main/java/tech/jhipster/lite/generator/prettier/application/PrettierApplicationService.java b/src/main/java/tech/jhipster/lite/generator/prettier/application/PrettierApplicationService.java index cbf47520902..029047a9c32 100644 --- a/src/main/java/tech/jhipster/lite/generator/prettier/application/PrettierApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/prettier/application/PrettierApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.prettier.domain.PrettierModuleFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class PrettierApplicationService { private final PrettierModuleFactory factory; - public PrettierApplicationService() { - factory = new PrettierModuleFactory(); + public PrettierApplicationService(NpmLazyInstaller npmLazyInstaller) { + this.factory = new PrettierModuleFactory(npmLazyInstaller); } public JHipsterModule buildModule(JHipsterModuleProperties properties) { diff --git a/src/main/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactory.java index 187b5d7b61e..55b330b2bc7 100644 --- a/src/main/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactory.java @@ -13,6 +13,7 @@ import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterDestination; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; public class PrettierModuleFactory { @@ -20,6 +21,12 @@ public class PrettierModuleFactory { private static final JHipsterSource SOURCE = from("prettier"); private static final JHipsterDestination DESTINATION = to("."); + private final NpmLazyInstaller npmLazyInstaller; + + public PrettierModuleFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } + public JHipsterModule buildModule(JHipsterModuleProperties properties) { //@formatter:off return moduleBuilder(properties) @@ -43,6 +50,9 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) { .addScript(scriptKey("prettier:check"), scriptCommand("prettier --check .")) .addScript(scriptKey("prettier:format"), scriptCommand("prettier --write .")) .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .preCommitActions(stagedFilesFilter("*.{md,json,yml,html,css,scss,java,xml,feature}"), preCommitCommands("['prettier --write']")) .build(); //@formatter:on diff --git a/src/main/java/tech/jhipster/lite/generator/typescript/core/application/TypescriptApplicationService.java b/src/main/java/tech/jhipster/lite/generator/typescript/core/application/TypescriptApplicationService.java index 4bad4a15d6e..c8cdc9bfea7 100644 --- a/src/main/java/tech/jhipster/lite/generator/typescript/core/application/TypescriptApplicationService.java +++ b/src/main/java/tech/jhipster/lite/generator/typescript/core/application/TypescriptApplicationService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import tech.jhipster.lite.generator.typescript.core.domain.TypescriptModuleFactory; import tech.jhipster.lite.module.domain.JHipsterModule; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @Service @@ -10,8 +11,8 @@ public class TypescriptApplicationService { private final TypescriptModuleFactory factory; - public TypescriptApplicationService() { - this.factory = new TypescriptModuleFactory(); + public TypescriptApplicationService(NpmLazyInstaller npmLazyInstaller) { + this.factory = new TypescriptModuleFactory(npmLazyInstaller); } public JHipsterModule buildModule(JHipsterModuleProperties project) { diff --git a/src/main/java/tech/jhipster/lite/generator/typescript/core/domain/TypescriptModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/typescript/core/domain/TypescriptModuleFactory.java index 3191d8b2c0b..626e5e8d8aa 100644 --- a/src/main/java/tech/jhipster/lite/generator/typescript/core/domain/TypescriptModuleFactory.java +++ b/src/main/java/tech/jhipster/lite/generator/typescript/core/domain/TypescriptModuleFactory.java @@ -11,12 +11,18 @@ import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.file.JHipsterSource; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; import tech.jhipster.lite.shared.error.domain.Assert; public class TypescriptModuleFactory { private static final JHipsterSource SOURCE = from("typescript"); + private final NpmLazyInstaller npmLazyInstaller; + + public TypescriptModuleFactory(NpmLazyInstaller npmLazyInstaller) { + this.npmLazyInstaller = npmLazyInstaller; + } public JHipsterModule buildModule(JHipsterModuleProperties properties) { Assert.notNull("properties", properties); @@ -45,6 +51,9 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) { .addScript(scriptKey("watch:tsc"), scriptCommand("tsc --noEmit --watch")) .addScript(scriptKey("watch:test"), scriptCommand("vitest --")) .and() + .postActions() + .add(context -> npmLazyInstaller.runInstallIn(context.projectFolder())) + .and() .files() .batch(SOURCE, to(".")) .addFile("tsconfig.json") diff --git a/src/main/java/tech/jhipster/lite/module/domain/npm/NpmLazyInstaller.java b/src/main/java/tech/jhipster/lite/module/domain/npm/NpmLazyInstaller.java new file mode 100644 index 00000000000..2f7052e39de --- /dev/null +++ b/src/main/java/tech/jhipster/lite/module/domain/npm/NpmLazyInstaller.java @@ -0,0 +1,10 @@ +package tech.jhipster.lite.module.domain.npm; + +import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder; + +/** + * Run npm install if a previous npm install has already been done. + */ +public interface NpmLazyInstaller { + void runInstallIn(JHipsterProjectFolder folder); +} diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/FileSystemNpmLazyInstaller.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/FileSystemNpmLazyInstaller.java new file mode 100644 index 00000000000..6965926e59c --- /dev/null +++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/FileSystemNpmLazyInstaller.java @@ -0,0 +1,92 @@ +package tech.jhipster.lite.module.infrastructure.secondary.npm; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; +import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder; +import tech.jhipster.lite.shared.error.domain.Assert; +import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage; +import tech.jhipster.lite.shared.npmdetector.infrastructure.secondary.NpmInstallationReader; +import tech.jhipster.lite.shared.npmdetector.infrastructure.secondary.NpmInstallationType; + +/** + * Launches npm install if the npm command is detected and if an existing package-lock.json is present. + */ +@Service +@ExcludeFromGeneratedCodeCoverage(reason = "Hard to test, requires npm installed") +class FileSystemNpmLazyInstaller implements NpmLazyInstaller { + + private static final Logger log = LoggerFactory.getLogger(FileSystemNpmLazyInstaller.class); + private final NpmInstallationReader npmInstallationReader = new NpmInstallationReader(); + + public void runInstallIn(JHipsterProjectFolder folder) { + Assert.notNull("folder", folder); + + if (!folder.fileExists("package-lock.json")) { + log.info("No package-lock.json found, npm install skipped"); + return; + } + + NpmInstallationType npmInstallationType = npmInstallationReader.get(); + switch (npmInstallationType) { + case UNIX: + execute(folder, "npm", "install"); + break; + case WINDOWS: + execute(folder, "npm.cmd", "install"); + break; + case NONE: + log.info("No npm installed, can't install npm dependencies"); + break; + } + } + + private void execute(JHipsterProjectFolder path, String... commands) { + try { + Process process = new ProcessBuilder(commands).directory(folderFile(path)).start(); + + if (failedExecution(process)) { + throw new NpmInstallException("Error during installation of npm dependencies, process failed"); + } + + traceProcess(String.join(" ", commands), process); + } catch (IOException e) { + throw new NpmInstallException("Error during installation of npm dependencies: " + e.getMessage(), e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + throw new NpmInstallException("Error during installation of npm dependencies: " + e.getMessage(), e); + } + } + + private File folderFile(JHipsterProjectFolder path) { + return new File(path.get()); + } + + private boolean failedExecution(Process process) throws InterruptedException { + return !process.waitFor(1, TimeUnit.MINUTES); + } + + private void traceProcess(String command, Process process) throws IOException { + if (log.isTraceEnabled()) { + log.trace("{}: {}", command, read(process.getInputStream())); + } + + String errors = read(process.getErrorStream()); + + if (StringUtils.isNotBlank(errors)) { + log.error("Error during {}: {}", command, errors); + } + } + + private String read(InputStream stream) throws IOException { + return new String(stream.readAllBytes(), StandardCharsets.UTF_8).intern(); + } +} diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmErrorKey.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmErrorKey.java new file mode 100644 index 00000000000..66a0b6e6e0a --- /dev/null +++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmErrorKey.java @@ -0,0 +1,18 @@ +package tech.jhipster.lite.module.infrastructure.secondary.npm; + +import tech.jhipster.lite.shared.error.domain.ErrorKey; + +enum NpmErrorKey implements ErrorKey { + INSTALL_ERROR("install-error"); + + private final String key; + + NpmErrorKey(String key) { + this.key = key; + } + + @Override + public String get() { + return key; + } +} diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmInstallException.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmInstallException.java new file mode 100644 index 00000000000..dadcf67ceaf --- /dev/null +++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NpmInstallException.java @@ -0,0 +1,16 @@ +package tech.jhipster.lite.module.infrastructure.secondary.npm; + +import tech.jhipster.lite.shared.error.domain.GeneratorException; +import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage; + +@ExcludeFromGeneratedCodeCoverage +class NpmInstallException extends GeneratorException { + + public NpmInstallException(String message) { + super(internalServerError(NpmErrorKey.INSTALL_ERROR).message(message)); + } + + public NpmInstallException(String message, Throwable cause) { + super(internalServerError(NpmErrorKey.INSTALL_ERROR).message(message).cause(cause)); + } +} diff --git a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationType.java b/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationType.java deleted file mode 100644 index f8189d12365..00000000000 --- a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationType.java +++ /dev/null @@ -1,7 +0,0 @@ -package tech.jhipster.lite.project.infrastructure.secondary; - -enum NpmInstallationType { - NONE, - UNIX, - WINDOWS, -} diff --git a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfiguration.java b/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfiguration.java index 83434baa01a..1ea2bd43d91 100644 --- a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfiguration.java +++ b/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfiguration.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportRuntimeHints; +import tech.jhipster.lite.shared.npmdetector.infrastructure.secondary.NpmInstallationReader; @Configuration @ImportRuntimeHints(NativeHints.class) diff --git a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationReader.java b/src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationReader.java similarity index 91% rename from src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationReader.java rename to src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationReader.java index cfca65dd9fc..e76e434c6a0 100644 --- a/src/main/java/tech/jhipster/lite/project/infrastructure/secondary/NpmInstallationReader.java +++ b/src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationReader.java @@ -1,4 +1,4 @@ -package tech.jhipster.lite.project.infrastructure.secondary; +package tech.jhipster.lite.shared.npmdetector.infrastructure.secondary; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -9,7 +9,7 @@ @Service @ExcludeFromGeneratedCodeCoverage(reason = "Cases can only be tested by using different computers") -class NpmInstallationReader { +public class NpmInstallationReader { private static final Logger log = LoggerFactory.getLogger(NpmInstallationReader.class); diff --git a/src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationType.java b/src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationType.java new file mode 100644 index 00000000000..e29ba3fe69e --- /dev/null +++ b/src/main/java/tech/jhipster/lite/shared/npmdetector/infrastructure/secondary/NpmInstallationType.java @@ -0,0 +1,7 @@ +package tech.jhipster.lite.shared.npmdetector.infrastructure.secondary; + +public enum NpmInstallationType { + NONE, + UNIX, + WINDOWS, +} diff --git a/src/main/java/tech/jhipster/lite/shared/npmdetector/package-info.java b/src/main/java/tech/jhipster/lite/shared/npmdetector/package-info.java new file mode 100644 index 00000000000..b89e1072408 --- /dev/null +++ b/src/main/java/tech/jhipster/lite/shared/npmdetector/package-info.java @@ -0,0 +1,2 @@ +@tech.jhipster.lite.SharedKernel +package tech.jhipster.lite.shared.npmdetector; diff --git a/src/main/resources/messages/errors/generator-errors-messages.properties b/src/main/resources/messages/errors/generator-errors-messages.properties index ecc31ebf203..485b1c41df5 100644 --- a/src/main/resources/messages/errors/generator-errors-messages.properties +++ b/src/main/resources/messages/errors/generator-errors-messages.properties @@ -103,3 +103,6 @@ error.unknown-slug.title=Unknown slug error.unmappable-enum.detail=Can't map enum value error.unmappable-enum.title=Unmappable enum + +error.install-error.detail=An error happened while installing npm dependencies +error.install-error.title=Error while installing npm dependencies diff --git a/src/main/resources/messages/errors/generator-errors-messages_fr.properties b/src/main/resources/messages/errors/generator-errors-messages_fr.properties index 88a78bbd15c..fbebb5e1d38 100644 --- a/src/main/resources/messages/errors/generator-errors-messages_fr.properties +++ b/src/main/resources/messages/errors/generator-errors-messages_fr.properties @@ -103,3 +103,6 @@ error.unknown-slug.title=Slug inconnu error.unmappable-enum.detail=Valeur d'enum non mappable error.unmappable-enum.title=Enum in-mappable + +error.install-error.detail=Une erreur s'est produite lors de l'installation des dépendances npm +error.install-error.title=Erreur lors de l'installation des dépendances npm diff --git a/src/test/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactoryTest.java index a9cfb8217bd..dd2da846d66 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/angular/core/domain/AngularModuleFactoryTest.java @@ -1,18 +1,33 @@ package tech.jhipster.lite.generator.client.angular.core.domain; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.lintStagedConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeScript; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest +@ExtendWith(MockitoExtension.class) class AngularModuleFactoryTest { - private static final AngularModuleFactory factory = new AngularModuleFactory(); + @InjectMocks + private AngularModuleFactory factory; + + @Mock + private NpmLazyInstaller npmLazyInstaller; @Test void shouldCreateAngularModule() { @@ -104,6 +119,7 @@ void shouldCreateAngularModule() { "environment.spec.ts" ) .hasPrefixedFiles("src/main/webapp", "index.html", "main.ts", "styles.css"); + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } @Test diff --git a/src/test/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactoryTest.java index b9a97624e26..010d41bbaf2 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/react/core/domain/ReactCoreModulesFactoryTest.java @@ -1,23 +1,42 @@ package tech.jhipster.lite.generator.client.react.core.domain; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.eslintConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.lintStagedConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.tsConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.vitestConfigFile; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; +import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest +@ExtendWith(MockitoExtension.class) class ReactCoreModulesFactoryTest { - private static final ReactCoreModulesFactory factory = new ReactCoreModulesFactory(); + @InjectMocks + private ReactCoreModulesFactory factory; + + @Mock + private NpmLazyInstaller npmLazyInstaller; @Test void shouldBuildModuleWithStyle() { - JHipsterModule module = factory.buildModule( - JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()).projectBaseName("jhipster").build() - ); + JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()) + .projectBaseName("jhipster") + .build(); + JHipsterModule module = factory.buildModule(properties); assertThatModuleWithFiles(module, packageJsonFile(), lintStagedConfigFile(), eslintConfigFile(), tsConfigFile(), vitestConfigFile()) .hasFile("package.json") @@ -72,6 +91,8 @@ void shouldBuildModuleWithStyle() { .and() .hasFiles("src/main/webapp/app/home/infrastructure/primary/HomePage.css") .hasPrefixedFiles("src/main/webapp/assets", "ReactLogo.png", "JHipster-Lite-neon-blue.png"); + + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } @Test diff --git a/src/test/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactoryTest.java index 1be370445a9..caa19e85d87 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/svelte/core/domain/SvelteModuleFactoryTest.java @@ -1,18 +1,33 @@ package tech.jhipster.lite.generator.client.svelte.core.domain; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.lintStagedConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeScript; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest +@ExtendWith(MockitoExtension.class) class SvelteModuleFactoryTest { - private static final SvelteModuleFactory factory = new SvelteModuleFactory(); + @InjectMocks + private SvelteModuleFactory factory; + + @Mock + private NpmLazyInstaller npmLazyInstaller; @Test void shouldCreateSvelteModule() { @@ -74,5 +89,6 @@ void shouldCreateSvelteModule() { .hasPrefixedFiles("src/main/webapp/assets", "JHipster-Lite-neon-orange.png") .hasPrefixedFiles("src/main/webapp/assets", "svelte-logo.png"); // @formatter:on + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } } diff --git a/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactoryTest.java index a0ba30e1cb3..e246e6ae9cf 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueModulesFactoryTest.java @@ -1,18 +1,37 @@ package tech.jhipster.lite.generator.client.vue.core.domain; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.ModuleFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.eslintConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.lintStagedConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeScript; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.tsConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.vitestConfigFile; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest +@ExtendWith(MockitoExtension.class) class VueModulesFactoryTest { - private static final VueModulesFactory factory = new VueModulesFactory(); + @InjectMocks + private VueModulesFactory factory; + + @Mock + private NpmLazyInstaller npmLazyInstaller; @Test void shouldCreateVueModule() { @@ -80,6 +99,7 @@ void shouldCreateVueModule() { .hasFiles("src/test/webapp/unit/shared/http/infrastructure/secondary/AxiosStub.ts") .hasFiles("src/test/webapp/unit/router/infrastructure/primary/HomeRouter.spec.ts"); //@formatter:on + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } @Test diff --git a/src/test/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactoryTest.java index 9d2bbb6aab5..ad59ca3668b 100644 --- a/src/test/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/prettier/domain/PrettierModuleFactoryTest.java @@ -1,15 +1,23 @@ package tech.jhipster.lite.generator.prettier.domain; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.emptyLintStagedConfigFile; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.lintStagedConfigFileWithoutPrettier; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeScript; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest @@ -19,6 +27,9 @@ class PrettierModuleFactoryTest { @InjectMocks private PrettierModuleFactory factory; + @Mock + private NpmLazyInstaller npmLazyInstaller; + @Test void shouldBuildModuleWithoutPrettierLintStaged() { String folder = TestFileUtils.tmpDirForTest(); @@ -56,6 +67,8 @@ void shouldBuildModuleWithoutPrettierLintStaged() { .containing(nodeDependency("prettier-plugin-packagejson")) .containing(nodeScript("prettier:check", "prettier --check .")) .containing(nodeScript("prettier:format", "prettier --write .")); + + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } @Test diff --git a/src/test/java/tech/jhipster/lite/generator/typescript/domain/core/TypescriptModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/typescript/domain/core/TypescriptModuleFactoryTest.java index 3046dbe0da5..02239faeb8f 100644 --- a/src/test/java/tech/jhipster/lite/generator/typescript/domain/core/TypescriptModuleFactoryTest.java +++ b/src/test/java/tech/jhipster/lite/generator/typescript/domain/core/TypescriptModuleFactoryTest.java @@ -1,19 +1,33 @@ package tech.jhipster.lite.generator.typescript.domain.core; -import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*; +import static org.mockito.Mockito.verify; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.assertThatModuleWithFiles; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeDependency; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.nodeScript; +import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.packageJsonFile; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import tech.jhipster.lite.TestFileUtils; import tech.jhipster.lite.UnitTest; import tech.jhipster.lite.generator.typescript.core.domain.TypescriptModuleFactory; import tech.jhipster.lite.module.domain.JHipsterModule; import tech.jhipster.lite.module.domain.JHipsterModulesFixture; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties; @UnitTest +@ExtendWith(MockitoExtension.class) class TypescriptModuleFactoryTest { - private static final TypescriptModuleFactory factory = new TypescriptModuleFactory(); + @InjectMocks + private TypescriptModuleFactory factory; + + @Mock + private NpmLazyInstaller npmLazyInstaller; @Test void shouldCreateTypescriptModule() { @@ -44,5 +58,6 @@ void shouldCreateTypescriptModule() { .containing(nodeScript("lint", "eslint .")) .and() .hasPrefixedFiles("", "eslint.config.js", "tsconfig.json"); + verify(npmLazyInstaller).runInstallIn(properties.projectFolder()); } } diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NoopNpmLazyInstaller.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NoopNpmLazyInstaller.java new file mode 100644 index 00000000000..f17838c0b6c --- /dev/null +++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/npm/NoopNpmLazyInstaller.java @@ -0,0 +1,16 @@ +package tech.jhipster.lite.module.infrastructure.secondary.npm; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.jhipster.lite.module.domain.npm.NpmLazyInstaller; +import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder; + +public class NoopNpmLazyInstaller implements NpmLazyInstaller { + + private static final Logger logs = LoggerFactory.getLogger(NoopNpmLazyInstaller.class); + + @Override + public void runInstallIn(JHipsterProjectFolder folder) { + logs.info("Simulating installation of npm dependencies"); + } +} diff --git a/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfigurationTest.java b/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfigurationTest.java index c37c9b9fd16..f6b7a39cb7f 100644 --- a/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfigurationTest.java +++ b/src/test/java/tech/jhipster/lite/project/infrastructure/secondary/ProjectFormatterConfigurationTest.java @@ -1,7 +1,7 @@ package tech.jhipster.lite.project.infrastructure.secondary; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import ch.qos.logback.classic.Level; import org.junit.jupiter.api.Test; @@ -12,6 +12,8 @@ import tech.jhipster.lite.LogsSpy; import tech.jhipster.lite.LogsSpyExtension; import tech.jhipster.lite.UnitTest; +import tech.jhipster.lite.shared.npmdetector.infrastructure.secondary.NpmInstallationReader; +import tech.jhipster.lite.shared.npmdetector.infrastructure.secondary.NpmInstallationType; @UnitTest @ExtendWith({ MockitoExtension.class, LogsSpyExtension.class })