@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,410 @@
1
+ package run.granite.gradle.tasks
2
+
3
+ import com.google.gson.Gson
4
+ import com.google.gson.JsonObject
5
+ import org.assertj.core.api.Assertions.assertThat
6
+ import org.junit.jupiter.api.Test
7
+ import org.junit.jupiter.api.io.TempDir
8
+ import run.granite.gradle.utils.createTestFile
9
+ import run.granite.gradle.utils.createTestTask
10
+ import run.granite.gradle.utils.readFileContent
11
+ import java.io.File
12
+
13
+ /**
14
+ * Unit tests for Codegen tasks (Schema and Artifacts).
15
+ *
16
+ * Tests schema generation and artifact generation for TurboModules and Fabric components.
17
+ */
18
+ class CodegenTasksTest {
19
+
20
+ @TempDir
21
+ lateinit var tempDir: File
22
+
23
+ // ========== CodegenSchemaTask Tests ==========
24
+
25
+ @Test
26
+ fun `CodegenSchemaTask is cacheable`() {
27
+ val annotations = CodegenSchemaTask::class.annotations
28
+ assertThat(annotations)
29
+ .anyMatch { it.annotationClass.simpleName == "CacheableTask" }
30
+ }
31
+
32
+ @Test
33
+ fun `CodegenSchemaTask has correct group and description`() {
34
+ val task = createTestTask<CodegenSchemaTask>()
35
+
36
+ assertThat(task.group).isEqualTo("granite")
37
+ assertThat(task.description).isEqualTo("Scans JavaScript sources and generates React Native Codegen schema")
38
+ }
39
+
40
+ @Test
41
+ fun `scanForSpecFiles finds TurboModule specs with Native prefix`() {
42
+ // Create TurboModule spec files
43
+ val jsSourceDir = File(tempDir, "src")
44
+ createTestFile(jsSourceDir, "NativeSampleModule.js", "// TurboModule spec")
45
+ createTestFile(jsSourceDir, "NativeTestModule.ts", "// TurboModule spec")
46
+ createTestFile(jsSourceDir, "NativeExample.tsx", "// TurboModule spec")
47
+
48
+ // Create non-spec files (should be ignored)
49
+ createTestFile(jsSourceDir, "Component.js", "// Regular component")
50
+ createTestFile(jsSourceDir, "utils.ts", "// Utility file")
51
+
52
+ val outputDir = File(tempDir, "output")
53
+ val schemaFile = File(outputDir, "schema.json")
54
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
55
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
56
+
57
+ val task = createTestTask<CodegenSchemaTask> {
58
+ it.jsSourceDirs.set(listOf(jsSourceDir))
59
+ it.reactNativeDir.set(reactNativeDir)
60
+ it.nodeModulesDir.set(nodeModulesDir)
61
+ it.outputDir.set(outputDir)
62
+ it.schemaFile.set(schemaFile)
63
+ }
64
+
65
+ task.execute()
66
+
67
+ // Verify schema was generated
68
+ assertThat(schemaFile).exists()
69
+
70
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
71
+ val modules = schema.getAsJsonObject("modules")
72
+
73
+ // Should find 3 TurboModule specs
74
+ assertThat(modules.has("NativeSampleModule")).isTrue
75
+ assertThat(modules.has("NativeTestModule")).isTrue
76
+ assertThat(modules.has("NativeExample")).isTrue
77
+
78
+ // Should NOT find non-spec files
79
+ assertThat(modules.has("Component")).isFalse
80
+ assertThat(modules.has("utils")).isFalse
81
+ }
82
+
83
+ @Test
84
+ fun `scanForSpecFiles finds Fabric component specs with NativeComponent suffix`() {
85
+ // Create Fabric component spec files
86
+ val jsSourceDir = File(tempDir, "src")
87
+ createTestFile(jsSourceDir, "MyViewNativeComponent.js", "// Fabric component")
88
+ createTestFile(jsSourceDir, "CustomButtonNativeComponent.ts", "// Fabric component")
89
+
90
+ val outputDir = File(tempDir, "output")
91
+ val schemaFile = File(outputDir, "schema.json")
92
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
93
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
94
+
95
+ val task = createTestTask<CodegenSchemaTask> {
96
+ it.jsSourceDirs.set(listOf(jsSourceDir))
97
+ it.reactNativeDir.set(reactNativeDir)
98
+ it.nodeModulesDir.set(nodeModulesDir)
99
+ it.outputDir.set(outputDir)
100
+ it.schemaFile.set(schemaFile)
101
+ }
102
+
103
+ task.execute()
104
+
105
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
106
+ val modules = schema.getAsJsonObject("modules")
107
+
108
+ assertThat(modules.has("MyViewNativeComponent")).isTrue
109
+ assertThat(modules.has("CustomButtonNativeComponent")).isTrue
110
+ }
111
+
112
+ @Test
113
+ fun `scanForSpecFiles handles nested directory structure`() {
114
+ // Create nested directory structure
115
+ val jsSourceDir = File(tempDir, "src")
116
+ createTestFile(File(jsSourceDir, "modules/auth"), "NativeAuthModule.ts", "// Auth module")
117
+ createTestFile(File(jsSourceDir, "components/views"), "ImageViewNativeComponent.js", "// Image view")
118
+ createTestFile(File(jsSourceDir, "deep/nested/path"), "NativeUtilsModule.tsx", "// Utils")
119
+
120
+ val outputDir = File(tempDir, "output")
121
+ val schemaFile = File(outputDir, "schema.json")
122
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
123
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
124
+
125
+ val task = createTestTask<CodegenSchemaTask> {
126
+ it.jsSourceDirs.set(listOf(jsSourceDir))
127
+ it.reactNativeDir.set(reactNativeDir)
128
+ it.nodeModulesDir.set(nodeModulesDir)
129
+ it.outputDir.set(outputDir)
130
+ it.schemaFile.set(schemaFile)
131
+ }
132
+
133
+ task.execute()
134
+
135
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
136
+ val modules = schema.getAsJsonObject("modules")
137
+
138
+ // Should find all nested spec files
139
+ assertThat(modules.has("NativeAuthModule")).isTrue
140
+ assertThat(modules.has("ImageViewNativeComponent")).isTrue
141
+ assertThat(modules.has("NativeUtilsModule")).isTrue
142
+ }
143
+
144
+ @Test
145
+ fun `scanForSpecFiles handles multiple source directories`() {
146
+ // Create multiple source directories
147
+ val srcDir1 = File(tempDir, "src1")
148
+ val srcDir2 = File(tempDir, "src2")
149
+ val srcDir3 = File(tempDir, "src3")
150
+
151
+ createTestFile(srcDir1, "NativeModule1.js", "// Module 1")
152
+ createTestFile(srcDir2, "NativeModule2.ts", "// Module 2")
153
+ createTestFile(srcDir3, "NativeModule3.tsx", "// Module 3")
154
+
155
+ val outputDir = File(tempDir, "output")
156
+ val schemaFile = File(outputDir, "schema.json")
157
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
158
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
159
+
160
+ val task = createTestTask<CodegenSchemaTask> {
161
+ it.jsSourceDirs.set(listOf(srcDir1, srcDir2, srcDir3))
162
+ it.reactNativeDir.set(reactNativeDir)
163
+ it.nodeModulesDir.set(nodeModulesDir)
164
+ it.outputDir.set(outputDir)
165
+ it.schemaFile.set(schemaFile)
166
+ }
167
+
168
+ task.execute()
169
+
170
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
171
+ val modules = schema.getAsJsonObject("modules")
172
+
173
+ // Should find modules from all source directories
174
+ assertThat(modules.size()).isEqualTo(3)
175
+ assertThat(modules.has("NativeModule1")).isTrue
176
+ assertThat(modules.has("NativeModule2")).isTrue
177
+ assertThat(modules.has("NativeModule3")).isTrue
178
+ }
179
+
180
+ @Test
181
+ fun `generateSchema creates empty schema when no spec files found`() {
182
+ // Create empty source directory
183
+ val jsSourceDir = File(tempDir, "src").apply { mkdirs() }
184
+ createTestFile(jsSourceDir, "Component.js", "// Regular component")
185
+ createTestFile(jsSourceDir, "utils.ts", "// Utils")
186
+
187
+ val outputDir = File(tempDir, "output")
188
+ val schemaFile = File(outputDir, "schema.json")
189
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
190
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
191
+
192
+ val task = createTestTask<CodegenSchemaTask> {
193
+ it.jsSourceDirs.set(listOf(jsSourceDir))
194
+ it.reactNativeDir.set(reactNativeDir)
195
+ it.nodeModulesDir.set(nodeModulesDir)
196
+ it.outputDir.set(outputDir)
197
+ it.schemaFile.set(schemaFile)
198
+ }
199
+
200
+ task.execute()
201
+
202
+ // Should create empty schema
203
+ assertThat(schemaFile).exists()
204
+
205
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
206
+ assertThat(schema.has("version")).isTrue
207
+ assertThat(schema.has("modules")).isTrue
208
+
209
+ val modules = schema.getAsJsonObject("modules")
210
+ assertThat(modules.size()).isEqualTo(0)
211
+ }
212
+
213
+ @Test
214
+ fun `generateSchema includes version and module metadata`() {
215
+ val jsSourceDir = File(tempDir, "src")
216
+ createTestFile(jsSourceDir, "NativeSampleModule.ts", "// TurboModule")
217
+
218
+ val outputDir = File(tempDir, "output")
219
+ val schemaFile = File(outputDir, "schema.json")
220
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
221
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
222
+
223
+ val task = createTestTask<CodegenSchemaTask> {
224
+ it.jsSourceDirs.set(listOf(jsSourceDir))
225
+ it.reactNativeDir.set(reactNativeDir)
226
+ it.nodeModulesDir.set(nodeModulesDir)
227
+ it.outputDir.set(outputDir)
228
+ it.schemaFile.set(schemaFile)
229
+ }
230
+
231
+ task.execute()
232
+
233
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
234
+
235
+ // Verify version
236
+ assertThat(schema.has("version")).isTrue
237
+ assertThat(schema.get("version").asString).isEqualTo("0.81.0")
238
+
239
+ // Verify module structure
240
+ val modules = schema.getAsJsonObject("modules")
241
+ val sampleModule = modules.getAsJsonObject("NativeSampleModule")
242
+
243
+ assertThat(sampleModule.get("type").asString).isEqualTo("NativeModule")
244
+ assertThat(sampleModule.has("spec")).isTrue
245
+ }
246
+
247
+ @Test
248
+ fun `generateSchema supports all JavaScript file extensions`() {
249
+ val jsSourceDir = File(tempDir, "src")
250
+ createTestFile(jsSourceDir, "NativeModule1.js", "// .js")
251
+ createTestFile(jsSourceDir, "NativeModule2.ts", "// .ts")
252
+ createTestFile(jsSourceDir, "NativeModule3.jsx", "// .jsx")
253
+ createTestFile(jsSourceDir, "NativeModule4.tsx", "// .tsx")
254
+
255
+ val outputDir = File(tempDir, "output")
256
+ val schemaFile = File(outputDir, "schema.json")
257
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
258
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
259
+
260
+ val task = createTestTask<CodegenSchemaTask> {
261
+ it.jsSourceDirs.set(listOf(jsSourceDir))
262
+ it.reactNativeDir.set(reactNativeDir)
263
+ it.nodeModulesDir.set(nodeModulesDir)
264
+ it.outputDir.set(outputDir)
265
+ it.schemaFile.set(schemaFile)
266
+ }
267
+
268
+ task.execute()
269
+
270
+ val schema = Gson().fromJson(readFileContent(schemaFile), JsonObject::class.java)
271
+ val modules = schema.getAsJsonObject("modules")
272
+
273
+ // All file extensions should be supported
274
+ assertThat(modules.size()).isEqualTo(4)
275
+ assertThat(modules.has("NativeModule1")).isTrue
276
+ assertThat(modules.has("NativeModule2")).isTrue
277
+ assertThat(modules.has("NativeModule3")).isTrue
278
+ assertThat(modules.has("NativeModule4")).isTrue
279
+ }
280
+
281
+ @Test
282
+ fun `schema file is valid JSON`() {
283
+ val jsSourceDir = File(tempDir, "src")
284
+ createTestFile(jsSourceDir, "NativeTestModule.ts", "// Test")
285
+
286
+ val outputDir = File(tempDir, "output")
287
+ val schemaFile = File(outputDir, "schema.json")
288
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
289
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
290
+
291
+ val task = createTestTask<CodegenSchemaTask> {
292
+ it.jsSourceDirs.set(listOf(jsSourceDir))
293
+ it.reactNativeDir.set(reactNativeDir)
294
+ it.nodeModulesDir.set(nodeModulesDir)
295
+ it.outputDir.set(outputDir)
296
+ it.schemaFile.set(schemaFile)
297
+ }
298
+
299
+ task.execute()
300
+
301
+ // Should be parseable as JSON
302
+ val content = readFileContent(schemaFile)
303
+ val schema = Gson().fromJson(content, JsonObject::class.java)
304
+
305
+ assertThat(schema).isNotNull
306
+ assertThat(schema.isJsonObject).isTrue
307
+ }
308
+
309
+ // ========== CodegenArtifactsTask Tests ==========
310
+
311
+ @Test
312
+ fun `CodegenArtifactsTask is cacheable`() {
313
+ val annotations = CodegenArtifactsTask::class.annotations
314
+ assertThat(annotations)
315
+ .anyMatch { it.annotationClass.simpleName == "CacheableTask" }
316
+ }
317
+
318
+ @Test
319
+ fun `CodegenArtifactsTask has correct group and description`() {
320
+ val task = createTestTask<CodegenArtifactsTask>()
321
+
322
+ assertThat(task.group).isEqualTo("granite")
323
+ assertThat(task.description).isEqualTo("Generates React Native Codegen artifacts (Java and C++)")
324
+ }
325
+
326
+ @Test
327
+ fun `CodegenArtifactsTask creates output directories when codegen binary not found`() {
328
+ // Create empty schema
329
+ val schemaContent = """{"version":"0.81.0","modules":{}}"""
330
+ val schemaFile = createTestFile(tempDir, "schema.json", schemaContent)
331
+
332
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
333
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
334
+ val javaOutputDir = File(tempDir, "output/java")
335
+ val jniOutputDir = File(tempDir, "output/jni")
336
+
337
+ val task = createTestTask<CodegenArtifactsTask> {
338
+ it.schemaFile.set(schemaFile)
339
+ it.reactNativeDir.set(reactNativeDir)
340
+ it.nodeModulesDir.set(nodeModulesDir)
341
+ it.packageName.set("com.example.test")
342
+ it.libraryName.set("TestLibrary")
343
+ it.javaOutputDir.set(javaOutputDir)
344
+ it.jniOutputDir.set(jniOutputDir)
345
+ }
346
+
347
+ // Execute (will not find codegen binary but should not fail)
348
+ task.execute()
349
+
350
+ // Output directories should be created even when codegen binary is not found
351
+ assertThat(javaOutputDir).exists()
352
+ assertThat(jniOutputDir).exists()
353
+ }
354
+
355
+ @Test
356
+ fun `CodegenArtifactsTask uses package name property`() {
357
+ val task = createTestTask<CodegenArtifactsTask> {
358
+ it.packageName.set("com.custom.package")
359
+ }
360
+
361
+ assertThat(task.packageName.get()).isEqualTo("com.custom.package")
362
+ }
363
+
364
+ @Test
365
+ fun `CodegenArtifactsTask uses library name property`() {
366
+ val task = createTestTask<CodegenArtifactsTask> {
367
+ it.libraryName.set("MyNativeLibrary")
368
+ }
369
+
370
+ assertThat(task.libraryName.get()).isEqualTo("MyNativeLibrary")
371
+ }
372
+
373
+ @Test
374
+ fun `CodegenArtifactsTask handles valid schema file`() {
375
+ // Create a schema with modules
376
+ val schemaContent = """
377
+ {
378
+ "version": "0.81.0",
379
+ "modules": {
380
+ "NativeTestModule": {
381
+ "type": "NativeModule",
382
+ "spec": "/path/to/spec"
383
+ }
384
+ }
385
+ }
386
+ """.trimIndent()
387
+
388
+ val schemaFile = createTestFile(tempDir, "schema.json", schemaContent)
389
+ val reactNativeDir = File(tempDir, "react-native").apply { mkdirs() }
390
+ val nodeModulesDir = File(tempDir, "node_modules").apply { mkdirs() }
391
+ val javaOutputDir = File(tempDir, "output/java")
392
+ val jniOutputDir = File(tempDir, "output/jni")
393
+
394
+ val task = createTestTask<CodegenArtifactsTask> {
395
+ it.schemaFile.set(schemaFile)
396
+ it.reactNativeDir.set(reactNativeDir)
397
+ it.nodeModulesDir.set(nodeModulesDir)
398
+ it.packageName.set("com.example.test")
399
+ it.libraryName.set("TestLibrary")
400
+ it.javaOutputDir.set(javaOutputDir)
401
+ it.jniOutputDir.set(jniOutputDir)
402
+ }
403
+
404
+ // Should not throw (will warn about missing codegen binary but continue)
405
+ task.execute()
406
+
407
+ assertThat(javaOutputDir).exists()
408
+ assertThat(jniOutputDir).exists()
409
+ }
410
+ }