@granite-js/screen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/GraniteScreen.podspec +25 -0
  3. package/LICENSE +202 -0
  4. package/android/CMakeLists.txt +62 -0
  5. package/android/build.gradle +63 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
  8. package/android/src/main/cpp/BundleEvaluator.h +17 -0
  9. package/android/src/main/cpp/onLoad.cpp +6 -0
  10. package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
  11. package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
  12. package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
  13. package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
  14. package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
  15. package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
  16. package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
  17. package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
  18. package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
  19. package/gradle-plugin/LICENSE +201 -0
  20. package/gradle-plugin/README.md +578 -0
  21. package/gradle-plugin/build.gradle.kts +97 -0
  22. package/gradle-plugin/gradle/libs.versions.toml +17 -0
  23. package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
  24. package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
  25. package/gradle-plugin/gradle.properties +12 -0
  26. package/gradle-plugin/gradlew +248 -0
  27. package/gradle-plugin/gradlew.bat +93 -0
  28. package/gradle-plugin/settings.gradle.kts +1 -0
  29. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
  30. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
  31. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
  32. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
  33. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
  34. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
  35. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
  36. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
  37. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
  38. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
  39. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
  40. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
  41. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
  42. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
  43. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
  44. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
  45. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
  46. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
  47. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
  48. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
  49. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
  50. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
  51. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
  52. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
  53. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
  54. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
  55. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
  56. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
  57. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
  58. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
  59. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
  60. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
  61. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
  62. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
  63. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
  64. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
  65. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
  66. package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
  67. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
  68. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
  69. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
  70. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
  71. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
  72. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
  73. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
  74. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
  75. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
  76. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
  77. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
  78. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
  79. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
  80. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
  81. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
  82. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
  83. package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
  84. package/ios/BundleLoader/BundleEvaluator.h +16 -0
  85. package/ios/BundleLoader/BundleEvaluator.mm +76 -0
  86. package/ios/BundleLoader/BundleLoadable.swift +91 -0
  87. package/ios/GraniteBundleLoaderTypes.swift +7 -0
  88. package/ios/GraniteScreen.h +12 -0
  89. package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
  90. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
  91. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
  92. package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
  93. package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
  94. package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
  95. package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
  96. package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
  97. package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
  98. package/package.json +59 -0
  99. package/react-native.config.js +8 -0
@@ -0,0 +1,318 @@
1
+ package run.granite.gradle.tasks
2
+
3
+ import org.assertj.core.api.Assertions.assertThat
4
+ import org.junit.jupiter.api.Test
5
+ import org.junit.jupiter.api.io.TempDir
6
+ import run.granite.gradle.utils.createTestFile
7
+ import run.granite.gradle.utils.createTestTask
8
+ import run.granite.gradle.utils.readFileContent
9
+ import java.io.File
10
+ import java.util.zip.GZIPInputStream
11
+
12
+ /**
13
+ * Unit tests for AssetPackagingTask.
14
+ *
15
+ * Tests asset packaging and compression logic by executing the actual task.
16
+ */
17
+ class AssetPackagingTaskTest {
18
+
19
+ @TempDir
20
+ lateinit var tempDir: File
21
+
22
+ @Test
23
+ fun `task is cacheable`() {
24
+ val annotations = AssetPackagingTask::class.annotations
25
+ assertThat(annotations)
26
+ .anyMatch { it.annotationClass.simpleName == "CacheableTask" }
27
+ }
28
+
29
+ @Test
30
+ fun `task has correct group and description`() {
31
+ val task = createTestTask<AssetPackagingTask>()
32
+
33
+ assertThat(task.group).isEqualTo("granite")
34
+ assertThat(task.description).isEqualTo("Packages React Native assets and bundles")
35
+ }
36
+
37
+ @Test
38
+ fun `packageBundle copies uncompressed bundle when compression disabled`() {
39
+ // Create test bundle file
40
+ val bundleContent = "// React Native bundle\nvar __BUNDLE_START_TIME__=Date.now();"
41
+ val bundleFile = createTestFile(tempDir, "index.android.bundle", bundleContent)
42
+
43
+ val outputDir = File(tempDir, "output/assets")
44
+ val outputFile = File(outputDir, "index.android.bundle")
45
+
46
+ val task = createTestTask<AssetPackagingTask> {
47
+ it.bundleFile.set(bundleFile)
48
+ it.outputAssetsDir.set(outputDir)
49
+ it.bundleAssetName.set("index.android.bundle")
50
+ it.compressionEnabled.set(false)
51
+ it.variantName.set("release")
52
+ }
53
+
54
+ task.execute()
55
+
56
+ // Verify uncompressed bundle was copied
57
+ assertThat(outputFile).exists()
58
+ assertThat(readFileContent(outputFile)).isEqualTo(bundleContent)
59
+
60
+ // Verify no gzipped file was created
61
+ assertThat(File(outputDir, "index.android.bundle.gz")).doesNotExist()
62
+ }
63
+
64
+ @Test
65
+ fun `packageBundle creates gzipped bundle when compression enabled`() {
66
+ // Create test bundle file
67
+ val bundleContent = "// React Native bundle\nvar __BUNDLE_START_TIME__=Date.now();"
68
+ val bundleFile = createTestFile(tempDir, "index.android.hbc", bundleContent)
69
+
70
+ val outputDir = File(tempDir, "output/assets")
71
+ val compressedFile = File(outputDir, "index.android.hbc.gz")
72
+
73
+ val task = createTestTask<AssetPackagingTask> {
74
+ it.bundleFile.set(bundleFile)
75
+ it.outputAssetsDir.set(outputDir)
76
+ it.bundleAssetName.set("index.android.hbc")
77
+ it.compressionEnabled.set(true)
78
+ it.variantName.set("release")
79
+ }
80
+
81
+ task.execute()
82
+
83
+ // Verify gzipped bundle was created
84
+ assertThat(compressedFile).exists()
85
+
86
+ // Verify content can be decompressed
87
+ val decompressed = GZIPInputStream(compressedFile.inputStream()).use {
88
+ it.readBytes().toString(Charsets.UTF_8)
89
+ }
90
+ assertThat(decompressed).isEqualTo(bundleContent)
91
+ }
92
+
93
+ @Test
94
+ fun `packageBundle handles Hermes bytecode files`() {
95
+ // Create test Hermes bytecode file (simulated)
96
+ val hbcContent = ByteArray(100) { it.toByte() } // Fake bytecode
97
+ val bundleFile = File(tempDir, "index.android.hbc")
98
+ bundleFile.writeBytes(hbcContent)
99
+
100
+ val outputDir = File(tempDir, "output/assets")
101
+ val outputFile = File(outputDir, "index.android.hbc")
102
+
103
+ val task = createTestTask<AssetPackagingTask> {
104
+ it.bundleFile.set(bundleFile)
105
+ it.outputAssetsDir.set(outputDir)
106
+ it.bundleAssetName.set("index.android.hbc")
107
+ it.compressionEnabled.set(false)
108
+ it.variantName.set("release")
109
+ }
110
+
111
+ task.execute()
112
+
113
+ assertThat(outputFile).exists()
114
+ assertThat(outputFile.readBytes()).isEqualTo(hbcContent)
115
+ }
116
+
117
+ @Test
118
+ fun `packageDrawableAssets copies single drawable directory`() {
119
+ // Create drawable assets
120
+ val assetsDir = File(tempDir, "assets")
121
+ val drawableMdpi = File(assetsDir, "drawable-mdpi")
122
+ createTestFile(drawableMdpi, "icon.png", "fake png content")
123
+
124
+ val outputResDir = File(tempDir, "output/res")
125
+ val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
126
+
127
+ val task = createTestTask<AssetPackagingTask> {
128
+ it.bundleFile.set(bundleFile)
129
+ it.assetsDir.set(assetsDir)
130
+ it.outputAssetsDir.set(File(tempDir, "output/assets"))
131
+ it.outputResDir.set(outputResDir)
132
+ it.bundleAssetName.set("index.android.bundle")
133
+ it.compressionEnabled.set(false)
134
+ it.variantName.set("release")
135
+ }
136
+
137
+ task.execute()
138
+
139
+ // Verify drawable was copied
140
+ val copiedDrawable = File(outputResDir, "drawable-mdpi/icon.png")
141
+ assertThat(copiedDrawable).exists()
142
+ assertThat(readFileContent(copiedDrawable)).isEqualTo("fake png content")
143
+ }
144
+
145
+ @Test
146
+ fun `packageDrawableAssets copies multiple drawable densities`() {
147
+ // Create multiple drawable density directories
148
+ val assetsDir = File(tempDir, "assets")
149
+ val densities = listOf("mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi")
150
+
151
+ for (density in densities) {
152
+ val drawableDir = File(assetsDir, "drawable-$density")
153
+ createTestFile(drawableDir, "icon.png", "icon for $density")
154
+ createTestFile(drawableDir, "logo.png", "logo for $density")
155
+ }
156
+
157
+ val outputResDir = File(tempDir, "output/res")
158
+ val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
159
+
160
+ val task = createTestTask<AssetPackagingTask> {
161
+ it.bundleFile.set(bundleFile)
162
+ it.assetsDir.set(assetsDir)
163
+ it.outputAssetsDir.set(File(tempDir, "output/assets"))
164
+ it.outputResDir.set(outputResDir)
165
+ it.bundleAssetName.set("index.android.bundle")
166
+ it.compressionEnabled.set(false)
167
+ it.variantName.set("release")
168
+ }
169
+
170
+ task.execute()
171
+
172
+ // Verify all densities were copied
173
+ for (density in densities) {
174
+ val iconFile = File(outputResDir, "drawable-$density/icon.png")
175
+ val logoFile = File(outputResDir, "drawable-$density/logo.png")
176
+
177
+ assertThat(iconFile).exists()
178
+ assertThat(logoFile).exists()
179
+ assertThat(readFileContent(iconFile)).isEqualTo("icon for $density")
180
+ assertThat(readFileContent(logoFile)).isEqualTo("logo for $density")
181
+ }
182
+ }
183
+
184
+ @Test
185
+ fun `packageDrawableAssets handles nested subdirectories`() {
186
+ // Create drawable with nested structure
187
+ val assetsDir = File(tempDir, "assets")
188
+ val drawableXhdpi = File(assetsDir, "drawable-xhdpi")
189
+ createTestFile(File(drawableXhdpi, "icons"), "app_icon.png", "nested icon")
190
+
191
+ val outputResDir = File(tempDir, "output/res")
192
+ val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
193
+
194
+ val task = createTestTask<AssetPackagingTask> {
195
+ it.bundleFile.set(bundleFile)
196
+ it.assetsDir.set(assetsDir)
197
+ it.outputAssetsDir.set(File(tempDir, "output/assets"))
198
+ it.outputResDir.set(outputResDir)
199
+ it.bundleAssetName.set("index.android.bundle")
200
+ it.compressionEnabled.set(false)
201
+ it.variantName.set("release")
202
+ }
203
+
204
+ task.execute()
205
+
206
+ // Verify nested file was copied with same structure
207
+ val copiedNestedFile = File(outputResDir, "drawable-xhdpi/icons/app_icon.png")
208
+ assertThat(copiedNestedFile).exists()
209
+ assertThat(readFileContent(copiedNestedFile)).isEqualTo("nested icon")
210
+ }
211
+
212
+ @Test
213
+ fun `packageDrawableAssets handles no assets directory`() {
214
+ // Don't set assetsDir (optional)
215
+ val outputAssetsDir = File(tempDir, "output/assets")
216
+ val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
217
+
218
+ val task = createTestTask<AssetPackagingTask> {
219
+ it.bundleFile.set(bundleFile)
220
+ // assetsDir not set
221
+ it.outputAssetsDir.set(outputAssetsDir)
222
+ // outputResDir not set
223
+ it.bundleAssetName.set("index.android.bundle")
224
+ it.compressionEnabled.set(false)
225
+ it.variantName.set("release")
226
+ }
227
+
228
+ // Should not throw
229
+ task.execute()
230
+
231
+ // Only bundle should be packaged
232
+ assertThat(File(outputAssetsDir, "index.android.bundle")).exists()
233
+ }
234
+
235
+ @Test
236
+ fun `packageDrawableAssets handles empty assets directory`() {
237
+ // Create empty assets directory
238
+ val assetsDir = File(tempDir, "assets")
239
+ assetsDir.mkdirs()
240
+
241
+ val outputResDir = File(tempDir, "output/res")
242
+ val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
243
+
244
+ val task = createTestTask<AssetPackagingTask> {
245
+ it.bundleFile.set(bundleFile)
246
+ it.assetsDir.set(assetsDir)
247
+ it.outputAssetsDir.set(File(tempDir, "output/assets"))
248
+ it.outputResDir.set(outputResDir)
249
+ it.bundleAssetName.set("index.android.bundle")
250
+ it.compressionEnabled.set(false)
251
+ it.variantName.set("release")
252
+ }
253
+
254
+ // Should not throw
255
+ task.execute()
256
+
257
+ // res directory may be created but empty
258
+ if (outputResDir.exists()) {
259
+ assertThat(outputResDir.listFiles()).isEmpty()
260
+ }
261
+ }
262
+
263
+ @Test
264
+ fun `task packages both bundle and drawables together`() {
265
+ // Create bundle and drawables
266
+ val bundleContent = "// React Native bundle"
267
+ val bundleFile = createTestFile(tempDir, "index.android.bundle", bundleContent)
268
+
269
+ val assetsDir = File(tempDir, "assets")
270
+ createTestFile(File(assetsDir, "drawable-mdpi"), "icon.png", "mdpi icon")
271
+ createTestFile(File(assetsDir, "drawable-hdpi"), "icon.png", "hdpi icon")
272
+
273
+ val outputAssetsDir = File(tempDir, "output/assets")
274
+ val outputResDir = File(tempDir, "output/res")
275
+
276
+ val task = createTestTask<AssetPackagingTask> {
277
+ it.bundleFile.set(bundleFile)
278
+ it.assetsDir.set(assetsDir)
279
+ it.outputAssetsDir.set(outputAssetsDir)
280
+ it.outputResDir.set(outputResDir)
281
+ it.bundleAssetName.set("index.android.bundle")
282
+ it.compressionEnabled.set(false)
283
+ it.variantName.set("release")
284
+ }
285
+
286
+ task.execute()
287
+
288
+ // Verify both bundle and drawables were packaged
289
+ assertThat(File(outputAssetsDir, "index.android.bundle")).exists()
290
+ assertThat(File(outputResDir, "drawable-mdpi/icon.png")).exists()
291
+ assertThat(File(outputResDir, "drawable-hdpi/icon.png")).exists()
292
+ }
293
+
294
+ @Test
295
+ fun `gzip compression actually reduces file size for text bundles`() {
296
+ // Create large repetitive text bundle (compresses well)
297
+ val bundleContent = "var x = 'test';\n".repeat(1000)
298
+ val bundleFile = createTestFile(tempDir, "large.bundle", bundleContent)
299
+
300
+ val outputDir = File(tempDir, "output/assets")
301
+
302
+ val task = createTestTask<AssetPackagingTask> {
303
+ it.bundleFile.set(bundleFile)
304
+ it.outputAssetsDir.set(outputDir)
305
+ it.bundleAssetName.set("large.bundle")
306
+ it.compressionEnabled.set(true)
307
+ it.variantName.set("release")
308
+ }
309
+
310
+ task.execute()
311
+
312
+ val compressedFile = File(outputDir, "large.bundle.gz")
313
+ assertThat(compressedFile).exists()
314
+
315
+ // Compressed file should be smaller than original
316
+ assertThat(compressedFile.length()).isLessThan(bundleFile.length())
317
+ }
318
+ }
@@ -0,0 +1,89 @@
1
+ package run.granite.gradle.tasks
2
+
3
+ import org.assertj.core.api.Assertions.assertThat
4
+ import org.junit.jupiter.api.Test
5
+ import run.granite.gradle.fixtures.NativeModuleFixtures
6
+ import run.granite.gradle.generators.CppAutolinkingGenerator
7
+
8
+ /**
9
+ * Unit tests for AutolinkingTask.
10
+ *
11
+ * Tests PackageList.kt generation logic and configuration.
12
+ *
13
+ * Note: Full integration testing (actual CLI execution) requires Node.js and
14
+ * React Native CLI, so is done separately. These tests focus on task structure validation.
15
+ */
16
+ class AutolinkingTaskTest {
17
+
18
+ @Test
19
+ fun `task is cacheable`() {
20
+ val annotations = AutolinkingTask::class.annotations
21
+ assertThat(annotations)
22
+ .anyMatch { it.annotationClass.simpleName == "CacheableTask" }
23
+ }
24
+
25
+ @Test
26
+ fun `task has all required properties`() {
27
+ val propertyNames = AutolinkingTask::class.members
28
+ .filter {
29
+ it.name.endsWith("File") || it.name.endsWith("Dir") ||
30
+ it.name == "packageName"
31
+ }
32
+ .map { it.name }
33
+ .toSet()
34
+
35
+ assertThat(propertyNames).contains("reactNativeDir")
36
+ assertThat(propertyNames).contains("nodeModulesDir")
37
+ assertThat(propertyNames).contains("projectDir")
38
+ assertThat(propertyNames).contains("outputDir")
39
+ assertThat(propertyNames).contains("packageListFile")
40
+ assertThat(propertyNames).contains("packageName")
41
+ }
42
+
43
+ @Test
44
+ fun `task has execute method`() {
45
+ val executeMethods = AutolinkingTask::class.members
46
+ .filter { it.name == "execute" }
47
+
48
+ assertThat(executeMethods).isNotEmpty
49
+ }
50
+
51
+ @Test
52
+ fun `task class is abstract`() {
53
+ assertThat(AutolinkingTask::class.isAbstract).isTrue
54
+ }
55
+
56
+ @Test
57
+ fun `autolinking filters modules by includesGeneratedCode flag`() {
58
+ // Create mixed modules: one standard, one with custom codegen
59
+ val modules = NativeModuleFixtures.createMixedModules()
60
+
61
+ // Simulate the filtering logic from AutolinkingTask
62
+ val filteredModules = modules.filter { !it.includesGeneratedCode }
63
+
64
+ // Standard module should be included
65
+ assertThat(filteredModules).anyMatch { it.name == "react-native-example" }
66
+
67
+ // Custom codegen module should be excluded
68
+ assertThat(filteredModules).noneMatch { it.name == "custom-codegen-module" }
69
+
70
+ assertThat(filteredModules).hasSize(1)
71
+ }
72
+
73
+ @Test
74
+ fun `generated autolinking excludes modules with includesGeneratedCode flag`() {
75
+ // Filtering by includesGeneratedCode is the caller's responsibility (AutolinkingTask),
76
+ // not the generator's. Test that pre-filtered input produces correct output.
77
+ val allModules = NativeModuleFixtures.createMixedModules()
78
+ val filteredModules = allModules.filter { !it.includesGeneratedCode }
79
+
80
+ val generatedCpp = CppAutolinkingGenerator.generate(filteredModules)
81
+
82
+ // Custom codegen module should be excluded after filtering
83
+ assertThat(generatedCpp).doesNotContain("CustomCodegenSpec_ModuleProvider")
84
+ assertThat(generatedCpp).doesNotContain("custom_codegen_module")
85
+
86
+ // Standard module should still be included
87
+ assertThat(generatedCpp).contains("ExampleSpec_ModuleProvider")
88
+ }
89
+ }
@@ -0,0 +1,68 @@
1
+ package run.granite.gradle.tasks
2
+
3
+ import org.assertj.core.api.Assertions.assertThat
4
+ import org.junit.jupiter.api.Test
5
+
6
+ /**
7
+ * Unit tests for BundleTask.
8
+ *
9
+ * Tests JavaScript bundling and Hermes compilation logic structure.
10
+ *
11
+ * Note: Full integration testing (actual Metro/Hermes execution) requires
12
+ * Node.js, React Native CLI, and Hermes compiler, so is done separately.
13
+ * These tests focus on task structure validation.
14
+ */
15
+ class BundleTaskTest {
16
+
17
+ @Test
18
+ fun `task is cacheable`() {
19
+ val annotations = BundleTask::class.annotations
20
+ assertThat(annotations)
21
+ .anyMatch { it.annotationClass.simpleName == "CacheableTask" }
22
+ }
23
+
24
+ @Test
25
+ fun `task has all required properties`() {
26
+ val propertyNames = BundleTask::class.members
27
+ .filter {
28
+ it.name.endsWith("File") || it.name.endsWith("Dir") ||
29
+ it.name == "devMode" ||
30
+ it.name == "bundleAssetName" || it.name == "variantName"
31
+ }
32
+ .map { it.name }
33
+ .toSet()
34
+
35
+ assertThat(propertyNames).contains("entryFile")
36
+ assertThat(propertyNames).contains("reactNativeDir")
37
+ assertThat(propertyNames).contains("nodeModulesDir")
38
+ assertThat(propertyNames).contains("projectDir")
39
+ assertThat(propertyNames).contains("bundleFile")
40
+ assertThat(propertyNames).contains("sourceMapFile")
41
+ assertThat(propertyNames).contains("bundleAssetName")
42
+ assertThat(propertyNames).contains("devMode")
43
+ assertThat(propertyNames).contains("variantName")
44
+ }
45
+
46
+ @Test
47
+ fun `task has execute method`() {
48
+ val executeMethods = BundleTask::class.members
49
+ .filter { it.name == "execute" }
50
+
51
+ assertThat(executeMethods).isNotEmpty
52
+ }
53
+
54
+ @Test
55
+ fun `task class is abstract`() {
56
+ assertThat(BundleTask::class.isAbstract).isTrue
57
+ }
58
+
59
+ @Test
60
+ fun `task has private helper methods`() {
61
+ val privateMembers = BundleTask::class.members
62
+ .map { it.name }
63
+ .toSet()
64
+
65
+ // Execute method orchestrates the bundling process
66
+ assertThat(privateMembers).contains("execute")
67
+ }
68
+ }