/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.tasks import com.facebook.react.tests.OS import com.facebook.react.tests.OsRule import com.facebook.react.tests.WithOs import com.facebook.react.tests.createTestTask import java.io.File import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder class BundleHermesCTaskTest { @get:Rule val tempFolder = TemporaryFolder() @get:Rule val osRule = OsRule() @Test fun bundleTask_groupIsSetCorrectly() { val task = createTestTask {} assertThat(task.group).isEqualTo("react") } @Test fun bundleTask_inputFiles_areSetCorrectly() { val rootDir = tempFolder.newFolder("js").apply { File(this, "file.js").createNewFile() File(this, "file.jsx").createNewFile() File(this, "file.ts").createNewFile() File(this, "file.tsx").createNewFile() } val task = createTestTask { it.root.set(rootDir) } assertThat(task.sources.files.size).isEqualTo(4) assertThat(task.sources.files) .containsExactlyInAnyOrder( File(rootDir, "file.js"), File(rootDir, "file.jsx"), File(rootDir, "file.ts"), File(rootDir, "file.tsx")) } @Test fun bundleTask_inputFilesInExcludedPath_areExcluded() { fun File.createFileAndPath() { parentFile.mkdirs() createNewFile() } val rootDir = tempFolder.newFolder("js").apply { File(this, "afolder/includedfile.js").createFileAndPath() // Those files should be excluded due to their filepath File(this, "android/excludedfile.js").createFileAndPath() File(this, "ios/excludedfile.js").createFileAndPath() File(this, "build/excludedfile.js").createFileAndPath() File(this, "node_modules/react-native/excludedfile.js").createFileAndPath() } val task = createTestTask { it.root.set(rootDir) } assertThat(task.sources.excludes) .containsExactlyInAnyOrder( "**/android/**/*", "**/ios/**/*", "**/build/**/*", "**/node_modules/**/*") assertThat(task.sources.files.size).isEqualTo(1) assertThat(task.sources.files).containsExactly(File(rootDir, "afolder/includedfile.js")) } @Test fun bundleTask_staticInputs_areSetCorrectly() { val task = createTestTask { it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) it.bundleCommand.set("bundle") it.bundleAssetName.set("myassetname") it.minifyEnabled.set(true) it.hermesEnabled.set(true) it.devEnabled.set(true) it.extraPackagerArgs.set(listOf("extra", "arg")) it.hermesCommand.set("./my-hermesc") it.hermesFlags.set(listOf("flag1", "flag2")) } assertThat(task.nodeExecutableAndArgs.get()).isEqualTo(listOf("node", "arg1", "arg2")) assertThat(task.bundleCommand.get()).isEqualTo("bundle") assertThat(task.bundleAssetName.get()).isEqualTo("myassetname") assertThat(task.minifyEnabled.get()).isTrue() assertThat(task.hermesEnabled.get()).isTrue() assertThat(task.devEnabled.get()).isTrue() assertThat(task.extraPackagerArgs.get()).isEqualTo(listOf("extra", "arg")) assertThat(task.hermesCommand.get()).isEqualTo("./my-hermesc") assertThat(task.hermesFlags.get()).isEqualTo(listOf("flag1", "flag2")) } @Test fun bundleTask_filesInput_areSetCorrectly() { val entryFile = tempFolder.newFile("entry.js") val cliFile = tempFolder.newFile("cli.js") val jsBundleDir = tempFolder.newFolder("jsbundle") val resourcesDir = tempFolder.newFolder("resources") val jsIntermediateSourceMapsDir = tempFolder.newFolder("jsIntermediateSourceMaps") val jsSourceMapsDir = tempFolder.newFolder("jsSourceMaps") val bundleConfig = tempFolder.newFile("bundle.config") val reactNativeDir = tempFolder.newFolder("node_modules/react-native") val task = createTestTask { it.entryFile.set(entryFile) it.cliFile.set(cliFile) it.jsBundleDir.set(jsBundleDir) it.resourcesDir.set(resourcesDir) it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir) it.jsSourceMapsDir.set(jsSourceMapsDir) it.bundleConfig.set(bundleConfig) it.reactNativeDir.set(reactNativeDir) } assertThat(task.entryFile.get().asFile).isEqualTo(entryFile) assertThat(task.cliFile.get().asFile).isEqualTo(cliFile) assertThat(task.jsBundleDir.get().asFile).isEqualTo(jsBundleDir) assertThat(task.resourcesDir.get().asFile).isEqualTo(resourcesDir) assertThat(task.jsIntermediateSourceMapsDir.get().asFile).isEqualTo(jsIntermediateSourceMapsDir) assertThat(task.jsSourceMapsDir.get().asFile).isEqualTo(jsSourceMapsDir) assertThat(task.bundleConfig.get().asFile).isEqualTo(bundleConfig) assertThat(task.reactNativeDir.get().asFile).isEqualTo(reactNativeDir) } @Test fun resolvePackagerSourceMapFile_withHermesEnabled_returnsCorrectFile() { val jsIntermediateSourceMapsDir = tempFolder.newFolder("jsIntermediateSourceMaps") val bundleAssetName = "myassetname" val task = createTestTask { it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir) it.hermesEnabled.set(true) it.bundleAssetName.set(bundleAssetName) } assertThat(task.resolvePackagerSourceMapFile(bundleAssetName)) .isEqualTo(File(jsIntermediateSourceMapsDir, "myassetname.packager.map")) } @Test fun resolvePackagerSourceMapFile_withHermesDisabled_returnsCorrectFile() { val jsSourceMapsDir = tempFolder.newFolder("jsSourceMaps") val bundleAssetName = "myassetname" val task = createTestTask { it.jsSourceMapsDir.set(jsSourceMapsDir) it.hermesEnabled.set(false) } assertThat(task.resolvePackagerSourceMapFile(bundleAssetName)) .isEqualTo(File(jsSourceMapsDir, "myassetname.map")) } @Test fun resolveOutputSourceMap_returnsCorrectFile() { val jsSourceMapsDir = tempFolder.newFolder("jsSourceMaps") val bundleAssetName = "myassetname" val task = createTestTask { it.jsSourceMapsDir.set(jsSourceMapsDir) } assertThat(task.resolveOutputSourceMap(bundleAssetName)) .isEqualTo(File(jsSourceMapsDir, "myassetname.map")) } @Test fun resolveCompilerSourceMap_returnsCorrectFile() { val jsIntermediateSourceMapsDir = tempFolder.newFolder("jsIntermediateSourceMaps") val bundleAssetName = "myassetname" val task = createTestTask { it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir) } assertThat(task.resolveCompilerSourceMap(bundleAssetName)) .isEqualTo(File(jsIntermediateSourceMapsDir, "myassetname.compiler.map")) } @Test fun getBundleCommand_returnsCorrectCommand() { val entryFile = tempFolder.newFile("index.js") val cliFile = tempFolder.newFile("cli.js") val bundleFile = tempFolder.newFile("bundle.js") val sourceMapFile = tempFolder.newFile("bundle.js.map") val resourcesDir = tempFolder.newFolder("res") val bundleConfig = tempFolder.newFile("bundle.config") val task = createTestTask { it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) it.root.set(tempFolder.root) it.cliFile.set(cliFile) it.bundleCommand.set("bundle") it.devEnabled.set(true) it.entryFile.set(entryFile) it.resourcesDir.set(resourcesDir) it.bundleConfig.set(bundleConfig) it.minifyEnabled.set(true) it.extraPackagerArgs.set(listOf("--read-global-cache")) } val bundleCommand = task.getBundleCommand(bundleFile, sourceMapFile) assertThat(bundleCommand) .containsExactly( "node", "arg1", "arg2", cliFile.absolutePath, "bundle", "--platform", "android", "--dev", "true", "--reset-cache", "--entry-file", entryFile.absolutePath, "--bundle-output", bundleFile.absolutePath, "--assets-dest", resourcesDir.absolutePath, "--sourcemap-output", sourceMapFile.absolutePath, "--config", bundleConfig.absolutePath, "--minify", "true", "--read-global-cache", "--verbose") } @Test @WithOs(OS.WIN) fun getBundleCommand_onWindows_returnsWinValidCommandsPaths() { val entryFile = tempFolder.newFile("index.js") val cliFile = tempFolder.newFile("cli.js") val bundleFile = tempFolder.newFile("bundle.js") val sourceMapFile = tempFolder.newFile("bundle.js.map") val resourcesDir = tempFolder.newFolder("res") val bundleConfig = tempFolder.newFile("bundle.config") val task = createTestTask { it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) it.root.set(tempFolder.root) it.cliFile.set(cliFile) it.bundleCommand.set("bundle") it.devEnabled.set(true) it.entryFile.set(entryFile) it.resourcesDir.set(resourcesDir) it.bundleConfig.set(bundleConfig) it.minifyEnabled.set(true) it.extraPackagerArgs.set(listOf("--read-global-cache")) } val bundleCommand = task.getBundleCommand(bundleFile, sourceMapFile) assertThat(bundleCommand) .containsExactly( "cmd", "/c", "node", "arg1", "arg2", cliFile.relativeTo(tempFolder.root).path, "bundle", "--platform", "android", "--dev", "true", "--reset-cache", "--entry-file", entryFile.relativeTo(tempFolder.root).path, "--bundle-output", bundleFile.relativeTo(tempFolder.root).path, "--assets-dest", resourcesDir.relativeTo(tempFolder.root).path, "--sourcemap-output", sourceMapFile.relativeTo(tempFolder.root).path, "--config", bundleConfig.relativeTo(tempFolder.root).path, "--minify", "true", "--read-global-cache", "--verbose") } @Test fun getBundleCommand_withoutConfig_returnsCommandWithoutConfig() { val entryFile = tempFolder.newFile("index.js") val cliFile = tempFolder.newFile("cli.js") val bundleFile = tempFolder.newFile("bundle.js") val sourceMapFile = tempFolder.newFile("bundle.js.map") val resourcesDir = tempFolder.newFolder("res") val task = createTestTask { it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) it.root.set(tempFolder.root) it.cliFile.set(cliFile) it.bundleCommand.set("bundle") it.devEnabled.set(true) it.entryFile.set(entryFile) it.resourcesDir.set(resourcesDir) it.minifyEnabled.set(true) it.extraPackagerArgs.set(listOf("--read-global-cache")) } val bundleCommand = task.getBundleCommand(bundleFile, sourceMapFile) assertThat(bundleCommand).doesNotContain("--config") } @Test fun getHermescCommand_returnsCorrectCommand() { val customHermesc = "hermesc" val bytecodeFile = tempFolder.newFile("bundle.js.hbc") val bundleFile = tempFolder.newFile("bundle.js") val task = createTestTask { it.root.set(tempFolder.root) it.hermesFlags.set(listOf("my-custom-hermes-flag")) } val hermesCommand = task.getHermescCommand(customHermesc, bytecodeFile, bundleFile) assertThat(hermesCommand) .containsExactly( customHermesc, "-w", "-emit-binary", "-max-diagnostic-width=80", "-out", bytecodeFile.absolutePath, bundleFile.absolutePath, "my-custom-hermes-flag") } @Test @WithOs(OS.WIN) fun getHermescCommand_onWindows_returnsRelativePaths() { val customHermesc = "hermesc" val bytecodeFile = tempFolder.newFile("bundle.js.hbc") val bundleFile = tempFolder.newFile("bundle.js") val task = createTestTask { it.root.set(tempFolder.root) it.hermesFlags.set(listOf("my-custom-hermes-flag")) } val hermesCommand = task.getHermescCommand(customHermesc, bytecodeFile, bundleFile) assertThat(hermesCommand) .containsExactly( "cmd", "/c", customHermesc, "-w", "-emit-binary", "-max-diagnostic-width=80", "-out", bytecodeFile.relativeTo(tempFolder.root).path, bundleFile.relativeTo(tempFolder.root).path, "my-custom-hermes-flag") } @Test fun getComposeSourceMapsCommand_returnsCorrectCommand() { val packagerMap = tempFolder.newFile("bundle.js.packager.map") val compilerMap = tempFolder.newFile("bundle.js.compiler.map") val outputMap = tempFolder.newFile("bundle.js.map") val reactNativeDir = tempFolder.newFolder("node_modules/react-native") val composeSourceMapsFile = File(reactNativeDir, "scripts/compose-source-maps.js") val task = createTestTask { it.root.set(tempFolder.root) it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) } val composeSourcemapCommand = task.getComposeSourceMapsCommand(composeSourceMapsFile, packagerMap, compilerMap, outputMap) assertThat(composeSourcemapCommand) .containsExactly( "node", "arg1", "arg2", composeSourceMapsFile.absolutePath, packagerMap.absolutePath, compilerMap.absolutePath, "-o", outputMap.absolutePath) } @Test @WithOs(OS.WIN) fun getComposeSourceMapsCommand_onWindows_returnsRelativePaths() { val packagerMap = tempFolder.newFile("bundle.js.packager.map") val compilerMap = tempFolder.newFile("bundle.js.compiler.map") val outputMap = tempFolder.newFile("bundle.js.map") val reactNativeDir = tempFolder.newFolder("node_modules/react-native") val composeSourceMapsFile = File(reactNativeDir, "scripts/compose-source-maps.js") val task = createTestTask { it.root.set(tempFolder.root) it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2")) } val composeSourcemapCommand = task.getComposeSourceMapsCommand(composeSourceMapsFile, packagerMap, compilerMap, outputMap) assertThat(composeSourcemapCommand) .containsExactly( "cmd", "/c", "node", "arg1", "arg2", composeSourceMapsFile.relativeTo(tempFolder.root).path, packagerMap.relativeTo(tempFolder.root).path, compilerMap.relativeTo(tempFolder.root).path, "-o", outputMap.relativeTo(tempFolder.root).path) } }