@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.
- package/CHANGELOG.md +7 -0
- package/GraniteScreen.podspec +25 -0
- package/LICENSE +202 -0
- package/android/CMakeLists.txt +62 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
- package/android/src/main/cpp/BundleEvaluator.h +17 -0
- package/android/src/main/cpp/onLoad.cpp +6 -0
- package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
- package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
- package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
- package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
- package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
- package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
- package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
- package/gradle-plugin/LICENSE +201 -0
- package/gradle-plugin/README.md +578 -0
- package/gradle-plugin/build.gradle.kts +97 -0
- package/gradle-plugin/gradle/libs.versions.toml +17 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/gradle-plugin/gradle.properties +12 -0
- package/gradle-plugin/gradlew +248 -0
- package/gradle-plugin/gradlew.bat +93 -0
- package/gradle-plugin/settings.gradle.kts +1 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
- package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
- package/ios/BundleLoader/BundleEvaluator.h +16 -0
- package/ios/BundleLoader/BundleEvaluator.mm +76 -0
- package/ios/BundleLoader/BundleLoadable.swift +91 -0
- package/ios/GraniteBundleLoaderTypes.swift +7 -0
- package/ios/GraniteScreen.h +12 -0
- package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
- package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
- package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
- package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
- package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
- package/package.json +59 -0
- package/react-native.config.js +8 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import org.junit.jupiter.api.Test
|
|
4
|
+
import org.junit.jupiter.api.io.TempDir
|
|
5
|
+
import java.io.File
|
|
6
|
+
import kotlin.test.assertEquals
|
|
7
|
+
import kotlin.test.assertFalse
|
|
8
|
+
import kotlin.test.assertNotNull
|
|
9
|
+
import kotlin.test.assertTrue
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Unit tests for DependencyConfigurator.
|
|
13
|
+
*
|
|
14
|
+
* Tests the dependency substitution logic that replaces deprecated React Native artifacts
|
|
15
|
+
* with new ones and enforces version consistency across the project.
|
|
16
|
+
*
|
|
17
|
+
* Note: Custom Maven group tests are excluded (no real use case, official RN always uses com.facebook.react)
|
|
18
|
+
*/
|
|
19
|
+
class DependencyConfiguratorTest {
|
|
20
|
+
|
|
21
|
+
@TempDir
|
|
22
|
+
lateinit var tempDir: File
|
|
23
|
+
|
|
24
|
+
@Test
|
|
25
|
+
fun `getDependencySubstitutions returns base substitutions for default group`() {
|
|
26
|
+
// Given
|
|
27
|
+
val version = "0.81.1"
|
|
28
|
+
val group = "com.facebook.react"
|
|
29
|
+
|
|
30
|
+
// When
|
|
31
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
32
|
+
|
|
33
|
+
// Then
|
|
34
|
+
// When using default group, only react-native and hermes-engine substitutions are needed
|
|
35
|
+
assertEquals(2, substitutions.size)
|
|
36
|
+
|
|
37
|
+
// react-native -> react-android substitution
|
|
38
|
+
val reactSubstitution = substitutions.find { it.first == "com.facebook.react:react-native" }
|
|
39
|
+
assertEquals("com.facebook.react:react-android:0.81.1", reactSubstitution?.second)
|
|
40
|
+
assertTrue(reactSubstitution?.third?.contains("deprecated") == true)
|
|
41
|
+
|
|
42
|
+
// hermes-engine -> hermes-android substitution
|
|
43
|
+
val hermesSubstitution = substitutions.find { it.first == "com.facebook.react:hermes-engine" }
|
|
44
|
+
assertEquals("com.facebook.react:hermes-android:0.81.1", hermesSubstitution?.second)
|
|
45
|
+
assertTrue(hermesSubstitution?.third?.contains("deprecated") == true)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Test
|
|
49
|
+
fun `getDependencySubstitutions handles SNAPSHOT versions`() {
|
|
50
|
+
// Given
|
|
51
|
+
val version = "0.0.0-20250123-1234-abc123-SNAPSHOT"
|
|
52
|
+
val group = "com.facebook.react"
|
|
53
|
+
|
|
54
|
+
// When
|
|
55
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
56
|
+
|
|
57
|
+
// Then
|
|
58
|
+
assertEquals(2, substitutions.size)
|
|
59
|
+
|
|
60
|
+
val reactSubstitution = substitutions.find { it.first == "com.facebook.react:react-native" }
|
|
61
|
+
assertEquals("com.facebook.react:react-android:0.0.0-20250123-1234-abc123-SNAPSHOT", reactSubstitution?.second)
|
|
62
|
+
|
|
63
|
+
val hermesSubstitution = substitutions.find { it.first == "com.facebook.react:hermes-engine" }
|
|
64
|
+
assertEquals("com.facebook.react:hermes-android:0.0.0-20250123-1234-abc123-SNAPSHOT", hermesSubstitution?.second)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun `getDependencySubstitutions includes reason for each substitution`() {
|
|
69
|
+
// Given
|
|
70
|
+
val version = "0.81.1"
|
|
71
|
+
val group = "com.facebook.react"
|
|
72
|
+
|
|
73
|
+
// When
|
|
74
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
75
|
+
|
|
76
|
+
// Then
|
|
77
|
+
// All substitution rules should include a reason (third = reason)
|
|
78
|
+
assertTrue(substitutions.all { it.third.isNotBlank() })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Test
|
|
82
|
+
fun `getDependencySubstitutions handles stable release versions`() {
|
|
83
|
+
// Given
|
|
84
|
+
val version = "0.81.6"
|
|
85
|
+
val group = "com.facebook.react"
|
|
86
|
+
|
|
87
|
+
// When
|
|
88
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
89
|
+
|
|
90
|
+
// Then
|
|
91
|
+
assertEquals(2, substitutions.size)
|
|
92
|
+
|
|
93
|
+
val reactSubstitution = substitutions.find { it.first == "com.facebook.react:react-native" }
|
|
94
|
+
assertEquals("com.facebook.react:react-android:0.81.6", reactSubstitution?.second)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
fun `getDependencySubstitutions handles rc versions`() {
|
|
99
|
+
// Given
|
|
100
|
+
val version = "0.82.0-rc.0"
|
|
101
|
+
val group = "com.facebook.react"
|
|
102
|
+
|
|
103
|
+
// When
|
|
104
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
105
|
+
|
|
106
|
+
// Then
|
|
107
|
+
assertEquals(2, substitutions.size)
|
|
108
|
+
|
|
109
|
+
val reactSubstitution = substitutions.find { it.first == "com.facebook.react:react-native" }
|
|
110
|
+
assertEquals("com.facebook.react:react-android:0.82.0-rc.0", reactSubstitution?.second)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// === DependencyCoordinates-based tests ===
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
fun `getDependencySubstitutions with coordinates includes hermes-android to new group substitution`() {
|
|
117
|
+
// Given - RN 0.84 scenario
|
|
118
|
+
val coordinates = DependencyCoordinates(
|
|
119
|
+
reactVersion = "0.84.0",
|
|
120
|
+
hermesVersion = "0.15.0",
|
|
121
|
+
hermesV1Version = "250829098.0.6",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// When
|
|
125
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
126
|
+
|
|
127
|
+
// Then
|
|
128
|
+
// Verify new substitution rules
|
|
129
|
+
val hermesAndroidSubstitution = substitutions.find {
|
|
130
|
+
it.first == "com.facebook.react:hermes-android"
|
|
131
|
+
}
|
|
132
|
+
assertNotNull(hermesAndroidSubstitution)
|
|
133
|
+
assertEquals("com.facebook.hermes:hermes-android:250829098.0.6", hermesAndroidSubstitution.second)
|
|
134
|
+
assertTrue(hermesAndroidSubstitution.third.contains("moved"))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `getDependencySubstitutions with coordinates uses V1 hermes version`() {
|
|
139
|
+
// Given
|
|
140
|
+
val coordinates = DependencyCoordinates(
|
|
141
|
+
reactVersion = "0.84.0",
|
|
142
|
+
hermesVersion = "0.15.0",
|
|
143
|
+
hermesV1Version = "250829098.0.6",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// When
|
|
147
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
148
|
+
|
|
149
|
+
// Then
|
|
150
|
+
val hermesEngineSubstitution = substitutions.find {
|
|
151
|
+
it.first == "com.facebook.react:hermes-engine"
|
|
152
|
+
}
|
|
153
|
+
// V1 version should be used
|
|
154
|
+
assertEquals("com.facebook.hermes:hermes-android:250829098.0.6", hermesEngineSubstitution?.second)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@Test
|
|
158
|
+
fun `getDependencySubstitutions with coordinates falls back to classic hermes when V1 empty`() {
|
|
159
|
+
// Given
|
|
160
|
+
val coordinates = DependencyCoordinates(
|
|
161
|
+
reactVersion = "0.84.0",
|
|
162
|
+
hermesVersion = "0.15.0",
|
|
163
|
+
hermesV1Version = "", // No V1
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// When
|
|
167
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
168
|
+
|
|
169
|
+
// Then
|
|
170
|
+
val hermesEngineSubstitution = substitutions.find {
|
|
171
|
+
it.first == "com.facebook.react:hermes-engine"
|
|
172
|
+
}
|
|
173
|
+
assertEquals("com.facebook.hermes:hermes-android:0.15.0", hermesEngineSubstitution?.second)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@Test
|
|
177
|
+
fun `getDependencySubstitutions with coordinates returns 3 base substitutions for default groups`() {
|
|
178
|
+
// Given
|
|
179
|
+
val coordinates = DependencyCoordinates(
|
|
180
|
+
reactVersion = "0.84.0",
|
|
181
|
+
hermesVersion = "0.15.0",
|
|
182
|
+
hermesV1Version = "250829098.0.6",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// When
|
|
186
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
187
|
+
|
|
188
|
+
// Then
|
|
189
|
+
assertEquals(3, substitutions.size) // react-native, hermes-engine, hermes-android
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Test
|
|
193
|
+
fun `getDependencySubstitutions with custom react group adds react-android substitution`() {
|
|
194
|
+
// Given
|
|
195
|
+
val coordinates = DependencyCoordinates(
|
|
196
|
+
reactVersion = "0.84.0",
|
|
197
|
+
hermesVersion = "0.15.0",
|
|
198
|
+
hermesV1Version = "250829098.0.6",
|
|
199
|
+
reactGroup = "io.github.custom",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// When
|
|
203
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
204
|
+
|
|
205
|
+
// Then
|
|
206
|
+
assertEquals(4, substitutions.size)
|
|
207
|
+
val reactAndroidSubstitution = substitutions.find {
|
|
208
|
+
it.first == "com.facebook.react:react-android"
|
|
209
|
+
}
|
|
210
|
+
assertEquals("io.github.custom:react-android:0.84.0", reactAndroidSubstitution?.second)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@Test
|
|
214
|
+
fun `getDependencySubstitutions with custom hermes group adds hermes-android substitution`() {
|
|
215
|
+
// Given
|
|
216
|
+
val coordinates = DependencyCoordinates(
|
|
217
|
+
reactVersion = "0.84.0",
|
|
218
|
+
hermesVersion = "0.15.0",
|
|
219
|
+
hermesV1Version = "250829098.0.6",
|
|
220
|
+
hermesGroup = "io.github.custom.hermes",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
// When
|
|
224
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
225
|
+
|
|
226
|
+
// Then
|
|
227
|
+
assertEquals(4, substitutions.size) // base 3 + custom hermes group
|
|
228
|
+
val hermesSubstitution = substitutions.find {
|
|
229
|
+
it.first == "com.facebook.hermes:hermes-android"
|
|
230
|
+
}
|
|
231
|
+
assertEquals("io.github.custom.hermes:hermes-android:250829098.0.6", hermesSubstitution?.second)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@Test
|
|
235
|
+
fun `getDependencySubstitutions with both custom groups adds all substitutions`() {
|
|
236
|
+
// Given
|
|
237
|
+
val coordinates = DependencyCoordinates(
|
|
238
|
+
reactVersion = "0.84.0",
|
|
239
|
+
hermesVersion = "0.15.0",
|
|
240
|
+
hermesV1Version = "250829098.0.6",
|
|
241
|
+
reactGroup = "io.github.custom",
|
|
242
|
+
hermesGroup = "io.github.custom.hermes",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
// When
|
|
246
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
247
|
+
|
|
248
|
+
// Then
|
|
249
|
+
assertEquals(5, substitutions.size) // base 3 + custom react + custom hermes
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@Test
|
|
253
|
+
fun `getDependencySubstitutions handles Hermes V1 version format correctly`() {
|
|
254
|
+
// Given - Hermes V1 build number format
|
|
255
|
+
val coordinates = DependencyCoordinates(
|
|
256
|
+
reactVersion = "0.84.0",
|
|
257
|
+
hermesVersion = "0.15.0",
|
|
258
|
+
hermesV1Version = "250829098.0.6", // Build number-based
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
// When
|
|
262
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
263
|
+
|
|
264
|
+
// Then
|
|
265
|
+
val allHermesSubstitutions = substitutions.filter {
|
|
266
|
+
it.second.contains("hermes-android")
|
|
267
|
+
}
|
|
268
|
+
assertTrue(allHermesSubstitutions.all { it.second.contains("250829098.0.6") })
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// === Tests for existing deprecated API (additional) ===
|
|
272
|
+
|
|
273
|
+
@Test
|
|
274
|
+
fun `getDependencySubstitutions with custom group returns 4 substitutions`() {
|
|
275
|
+
// Given
|
|
276
|
+
val version = "0.81.1"
|
|
277
|
+
val group = "io.github.custom"
|
|
278
|
+
|
|
279
|
+
// When
|
|
280
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(version, group)
|
|
281
|
+
|
|
282
|
+
// Then
|
|
283
|
+
assertEquals(4, substitutions.size) // base 2 + custom group 2
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// === Dynamic version substitution tests ===
|
|
287
|
+
|
|
288
|
+
@Test
|
|
289
|
+
fun `substitution rules use version-less module selectors for dynamic version matching`() {
|
|
290
|
+
// Given
|
|
291
|
+
val coordinates = DependencyCoordinates(
|
|
292
|
+
reactVersion = "0.84.0",
|
|
293
|
+
hermesVersion = "0.15.0",
|
|
294
|
+
hermesV1Version = "250829098.0.6",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
// When
|
|
298
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
299
|
+
|
|
300
|
+
// Then - All source coordinates should be version-less for dynamic version matching
|
|
301
|
+
val reactNativeSubstitution = substitutions.find {
|
|
302
|
+
it.first == "com.facebook.react:react-native"
|
|
303
|
+
}
|
|
304
|
+
assertNotNull(reactNativeSubstitution)
|
|
305
|
+
// Source has no version (group:artifact format without version)
|
|
306
|
+
assertFalse(reactNativeSubstitution.first.contains(":0."))
|
|
307
|
+
assertFalse(reactNativeSubstitution.first.contains(":+"))
|
|
308
|
+
// Target has version
|
|
309
|
+
assertTrue(reactNativeSubstitution.second.contains(":0.84.0"))
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@Test
|
|
313
|
+
fun `substitution rules match all versions including dynamic versions`() {
|
|
314
|
+
// Given - Scenario where dynamic versions are used
|
|
315
|
+
// When com.facebook.react:react-native:+ is declared
|
|
316
|
+
val coordinates = DependencyCoordinates(
|
|
317
|
+
reactVersion = "0.84.0",
|
|
318
|
+
hermesVersion = "0.15.0",
|
|
319
|
+
hermesV1Version = "250829098.0.6",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
// When
|
|
323
|
+
val substitutions = DependencyConfigurator.getDependencySubstitutions(coordinates)
|
|
324
|
+
|
|
325
|
+
// Then - Substitution source should be declared without version
|
|
326
|
+
// Gradle's substitute(module("group:artifact")) matches all versions
|
|
327
|
+
// (+, latest.release, 1.+, 0.84.0, etc.)
|
|
328
|
+
for ((source, target, _) in substitutions) {
|
|
329
|
+
// Source is "group:artifact" format (no version)
|
|
330
|
+
val parts = source.split(":")
|
|
331
|
+
assertEquals(2, parts.size, "Source '$source' should be version-less (group:artifact)")
|
|
332
|
+
|
|
333
|
+
// Target is "group:artifact:version" format (with version)
|
|
334
|
+
val targetParts = target.split(":")
|
|
335
|
+
assertEquals(3, targetParts.size, "Target '$target' should have version (group:artifact:version)")
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import org.gradle.api.Project
|
|
4
|
+
import org.gradle.testfixtures.ProjectBuilder
|
|
5
|
+
import org.junit.jupiter.api.AfterEach
|
|
6
|
+
import org.junit.jupiter.api.BeforeEach
|
|
7
|
+
import org.junit.jupiter.api.Test
|
|
8
|
+
import run.granite.gradle.GraniteExtension
|
|
9
|
+
import java.io.File
|
|
10
|
+
import kotlin.test.assertFalse
|
|
11
|
+
import kotlin.test.assertNotNull
|
|
12
|
+
import kotlin.test.assertTrue
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Unit tests for DevServerResourceConfigurator.
|
|
16
|
+
*
|
|
17
|
+
* Tests the generation of Android resources for React Native development server configuration.
|
|
18
|
+
*/
|
|
19
|
+
class DevServerResourceConfiguratorTest {
|
|
20
|
+
|
|
21
|
+
private lateinit var project: Project
|
|
22
|
+
private lateinit var extension: GraniteExtension
|
|
23
|
+
private lateinit var configurator: DevServerResourceConfigurator
|
|
24
|
+
private lateinit var testResDir: File
|
|
25
|
+
|
|
26
|
+
@BeforeEach
|
|
27
|
+
fun setup() {
|
|
28
|
+
project = ProjectBuilder.builder().build()
|
|
29
|
+
project.pluginManager.apply("com.android.library")
|
|
30
|
+
|
|
31
|
+
extension = project.extensions.create("granite", GraniteExtension::class.java, project)
|
|
32
|
+
configurator = DevServerResourceConfigurator(project, extension)
|
|
33
|
+
|
|
34
|
+
testResDir = File(project.projectDir, "src/debug/res/values")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@AfterEach
|
|
38
|
+
fun cleanup() {
|
|
39
|
+
// Clean up generated test files
|
|
40
|
+
val debugSrcDir = File(project.projectDir, "src/debug")
|
|
41
|
+
if (debugSrcDir.exists()) {
|
|
42
|
+
debugSrcDir.deleteRecursively()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
fun `configurator is created successfully`() {
|
|
48
|
+
assertNotNull(configurator, "DevServerResourceConfigurator should be created")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `configure does not generate files when dev server not configured`() {
|
|
53
|
+
// Don't set devServerHost or devServerPort
|
|
54
|
+
configurator.configure()
|
|
55
|
+
|
|
56
|
+
// Verify no resources were generated
|
|
57
|
+
assertFalse(
|
|
58
|
+
testResDir.exists(),
|
|
59
|
+
"Resource directory should not be created when dev server is not configured",
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Test
|
|
64
|
+
fun `configure generates strings xml when host is set`() {
|
|
65
|
+
extension.devServerHost.set("localhost")
|
|
66
|
+
|
|
67
|
+
configurator.configure()
|
|
68
|
+
|
|
69
|
+
val stringsFile = File(testResDir, "strings.xml")
|
|
70
|
+
assertTrue(stringsFile.exists(), "strings.xml should be generated")
|
|
71
|
+
assertTrue(
|
|
72
|
+
stringsFile.readText().contains("react_native_dev_server_host"),
|
|
73
|
+
"strings.xml should contain dev server host",
|
|
74
|
+
)
|
|
75
|
+
assertTrue(
|
|
76
|
+
stringsFile.readText().contains("localhost"),
|
|
77
|
+
"strings.xml should contain the configured host",
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Test
|
|
82
|
+
fun `configure generates integers xml when port is set`() {
|
|
83
|
+
extension.devServerPort.set(8081)
|
|
84
|
+
|
|
85
|
+
configurator.configure()
|
|
86
|
+
|
|
87
|
+
val integersFile = File(testResDir, "integers.xml")
|
|
88
|
+
assertTrue(integersFile.exists(), "integers.xml should be generated")
|
|
89
|
+
assertTrue(
|
|
90
|
+
integersFile.readText().contains("react_native_dev_server_port"),
|
|
91
|
+
"integers.xml should contain dev server port",
|
|
92
|
+
)
|
|
93
|
+
assertTrue(
|
|
94
|
+
integersFile.readText().contains("8081"),
|
|
95
|
+
"integers.xml should contain the configured port",
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@Test
|
|
100
|
+
fun `configure generates both strings and integers when both are set`() {
|
|
101
|
+
extension.devServerHost.set("10.0.2.2")
|
|
102
|
+
extension.devServerPort.set(8082)
|
|
103
|
+
|
|
104
|
+
configurator.configure()
|
|
105
|
+
|
|
106
|
+
val stringsFile = File(testResDir, "strings.xml")
|
|
107
|
+
val integersFile = File(testResDir, "integers.xml")
|
|
108
|
+
|
|
109
|
+
assertTrue(stringsFile.exists(), "strings.xml should be generated")
|
|
110
|
+
assertTrue(integersFile.exists(), "integers.xml should be generated")
|
|
111
|
+
|
|
112
|
+
assertTrue(
|
|
113
|
+
stringsFile.readText().contains("10.0.2.2"),
|
|
114
|
+
"strings.xml should contain the configured host",
|
|
115
|
+
)
|
|
116
|
+
assertTrue(
|
|
117
|
+
integersFile.readText().contains("8082"),
|
|
118
|
+
"integers.xml should contain the configured port",
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `configure generates valid xml format for strings`() {
|
|
124
|
+
extension.devServerHost.set("192.168.1.100")
|
|
125
|
+
|
|
126
|
+
configurator.configure()
|
|
127
|
+
|
|
128
|
+
val stringsFile = File(testResDir, "strings.xml")
|
|
129
|
+
val content = stringsFile.readText()
|
|
130
|
+
|
|
131
|
+
assertTrue(content.contains("<?xml version=\"1.0\" encoding=\"utf-8\"?>"), "Should have XML declaration")
|
|
132
|
+
assertTrue(content.contains("<resources>"), "Should have resources tag")
|
|
133
|
+
assertTrue(content.contains("</resources>"), "Should close resources tag")
|
|
134
|
+
assertTrue(content.contains("translatable=\"false\""), "Should mark as non-translatable")
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `configure generates valid xml format for integers`() {
|
|
139
|
+
extension.devServerPort.set(9999)
|
|
140
|
+
|
|
141
|
+
configurator.configure()
|
|
142
|
+
|
|
143
|
+
val integersFile = File(testResDir, "integers.xml")
|
|
144
|
+
val content = integersFile.readText()
|
|
145
|
+
|
|
146
|
+
assertTrue(content.contains("<?xml version=\"1.0\" encoding=\"utf-8\"?>"), "Should have XML declaration")
|
|
147
|
+
assertTrue(content.contains("<resources>"), "Should have resources tag")
|
|
148
|
+
assertTrue(content.contains("</resources>"), "Should close resources tag")
|
|
149
|
+
assertTrue(content.contains("<integer name=\"react_native_dev_server_port\">"), "Should have integer tag")
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Test
|
|
153
|
+
fun `configure can be called multiple times`() {
|
|
154
|
+
extension.devServerHost.set("localhost")
|
|
155
|
+
extension.devServerPort.set(8081)
|
|
156
|
+
|
|
157
|
+
// Should not throw when called multiple times
|
|
158
|
+
configurator.configure()
|
|
159
|
+
configurator.configure()
|
|
160
|
+
|
|
161
|
+
// Files should still exist
|
|
162
|
+
assertTrue(File(testResDir, "strings.xml").exists())
|
|
163
|
+
assertTrue(File(testResDir, "integers.xml").exists())
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@Test
|
|
167
|
+
fun `configure handles emulator android host`() {
|
|
168
|
+
extension.devServerHost.set("10.0.2.2") // Standard Android emulator host
|
|
169
|
+
|
|
170
|
+
configurator.configure()
|
|
171
|
+
|
|
172
|
+
val stringsFile = File(testResDir, "strings.xml")
|
|
173
|
+
assertTrue(
|
|
174
|
+
stringsFile.readText().contains("10.0.2.2"),
|
|
175
|
+
"Should handle Android emulator host address",
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@Test
|
|
180
|
+
fun `configure handles custom ports`() {
|
|
181
|
+
extension.devServerPort.set(19000) // Custom Expo port
|
|
182
|
+
|
|
183
|
+
configurator.configure()
|
|
184
|
+
|
|
185
|
+
val integersFile = File(testResDir, "integers.xml")
|
|
186
|
+
assertTrue(
|
|
187
|
+
integersFile.readText().contains("19000"),
|
|
188
|
+
"Should handle custom port numbers",
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Test
|
|
193
|
+
fun `configure creates directory structure if not exists`() {
|
|
194
|
+
// Ensure directory doesn't exist
|
|
195
|
+
val debugSrcDir = File(project.projectDir, "src/debug")
|
|
196
|
+
if (debugSrcDir.exists()) {
|
|
197
|
+
debugSrcDir.deleteRecursively()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
extension.devServerHost.set("localhost")
|
|
201
|
+
configurator.configure()
|
|
202
|
+
|
|
203
|
+
assertTrue(testResDir.exists(), "Should create directory structure")
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import com.android.build.gradle.LibraryExtension
|
|
4
|
+
import org.gradle.api.Project
|
|
5
|
+
import org.gradle.testfixtures.ProjectBuilder
|
|
6
|
+
import org.junit.jupiter.api.BeforeEach
|
|
7
|
+
import org.junit.jupiter.api.Test
|
|
8
|
+
import run.granite.gradle.GraniteExtension
|
|
9
|
+
import kotlin.test.assertNotNull
|
|
10
|
+
import kotlin.test.assertTrue
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Unit tests for ResourceConfigurator.
|
|
14
|
+
*
|
|
15
|
+
* Tests the configuration of Android resource packaging options,
|
|
16
|
+
* particularly bundle compression settings.
|
|
17
|
+
*/
|
|
18
|
+
class ResourceConfiguratorTest {
|
|
19
|
+
|
|
20
|
+
private lateinit var project: Project
|
|
21
|
+
private lateinit var extension: GraniteExtension
|
|
22
|
+
private lateinit var androidExtension: LibraryExtension
|
|
23
|
+
private lateinit var configurator: ResourceConfigurator
|
|
24
|
+
|
|
25
|
+
@BeforeEach
|
|
26
|
+
fun setup() {
|
|
27
|
+
project = ProjectBuilder.builder().build()
|
|
28
|
+
project.pluginManager.apply("com.android.library")
|
|
29
|
+
|
|
30
|
+
extension = project.extensions.create("granite", GraniteExtension::class.java, project)
|
|
31
|
+
androidExtension = project.extensions.getByType(LibraryExtension::class.java)
|
|
32
|
+
|
|
33
|
+
configurator = ResourceConfigurator(project, extension)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
fun `configurator is created successfully`() {
|
|
38
|
+
assertNotNull(configurator, "ResourceConfigurator should be created")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `configure method executes without errors when compression is enabled`() {
|
|
43
|
+
extension.bundleCompressionEnabled.set(true)
|
|
44
|
+
|
|
45
|
+
// Should not throw
|
|
46
|
+
configurator.configure(androidExtension)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun `configure method executes without errors when compression is disabled`() {
|
|
51
|
+
extension.bundleCompressionEnabled.set(false)
|
|
52
|
+
|
|
53
|
+
// Should not throw
|
|
54
|
+
configurator.configure(androidExtension)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Test
|
|
58
|
+
fun `packaging configuration is applied to android extension`() {
|
|
59
|
+
extension.bundleCompressionEnabled.set(false)
|
|
60
|
+
|
|
61
|
+
configurator.configure(androidExtension)
|
|
62
|
+
|
|
63
|
+
// Verify packaging configuration exists
|
|
64
|
+
assertNotNull(androidExtension.packaging, "Packaging should be configured")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun `map files are always excluded from compression`() {
|
|
69
|
+
extension.bundleCompressionEnabled.set(true)
|
|
70
|
+
|
|
71
|
+
configurator.configure(androidExtension)
|
|
72
|
+
|
|
73
|
+
// Verify packaging resources exist
|
|
74
|
+
assertNotNull(androidExtension.packaging.resources, "Packaging resources should be configured")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Test
|
|
78
|
+
fun `bundle compression disabled excludes bundle and hbc files`() {
|
|
79
|
+
extension.bundleCompressionEnabled.set(false)
|
|
80
|
+
|
|
81
|
+
configurator.configure(androidExtension)
|
|
82
|
+
|
|
83
|
+
val excludes = androidExtension.packaging.resources.excludes
|
|
84
|
+
|
|
85
|
+
// When compression is disabled, bundle files should be excluded from APK compression
|
|
86
|
+
assertTrue(
|
|
87
|
+
excludes.any { it.contains(".bundle") },
|
|
88
|
+
"Bundle files should be excluded when compression is disabled",
|
|
89
|
+
)
|
|
90
|
+
assertTrue(
|
|
91
|
+
excludes.any { it.contains(".hbc") },
|
|
92
|
+
"Hermes bytecode files should be excluded when compression is disabled",
|
|
93
|
+
)
|
|
94
|
+
assertTrue(
|
|
95
|
+
excludes.any { it.contains(".map") },
|
|
96
|
+
"Map files should always be excluded",
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Test
|
|
101
|
+
fun `bundle compression enabled only excludes map files`() {
|
|
102
|
+
extension.bundleCompressionEnabled.set(true)
|
|
103
|
+
|
|
104
|
+
configurator.configure(androidExtension)
|
|
105
|
+
|
|
106
|
+
val excludes = androidExtension.packaging.resources.excludes
|
|
107
|
+
|
|
108
|
+
// When compression is enabled, only map files should be excluded
|
|
109
|
+
assertTrue(
|
|
110
|
+
excludes.any { it.contains(".map") },
|
|
111
|
+
"Map files should always be excluded",
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
fun `configurator can be called multiple times`() {
|
|
117
|
+
extension.bundleCompressionEnabled.set(false)
|
|
118
|
+
|
|
119
|
+
// Should not throw when called multiple times
|
|
120
|
+
configurator.configure(androidExtension)
|
|
121
|
+
configurator.configure(androidExtension)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Test
|
|
125
|
+
fun `configurator works with default extension values`() {
|
|
126
|
+
// Don't set any values explicitly, use defaults
|
|
127
|
+
|
|
128
|
+
// Should not throw with default values
|
|
129
|
+
configurator.configure(androidExtension)
|
|
130
|
+
}
|
|
131
|
+
}
|