@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,191 @@
|
|
|
1
|
+
package run.granite.gradle
|
|
2
|
+
|
|
3
|
+
import org.gradle.testfixtures.ProjectBuilder
|
|
4
|
+
import org.junit.jupiter.api.BeforeEach
|
|
5
|
+
import org.junit.jupiter.api.Test
|
|
6
|
+
import org.junit.jupiter.api.assertThrows
|
|
7
|
+
import org.junit.jupiter.api.io.TempDir
|
|
8
|
+
import java.io.File
|
|
9
|
+
import kotlin.test.assertEquals
|
|
10
|
+
import kotlin.test.assertTrue
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Unit tests for GraniteExtension.
|
|
14
|
+
*/
|
|
15
|
+
class GraniteExtensionTest {
|
|
16
|
+
|
|
17
|
+
@TempDir
|
|
18
|
+
lateinit var testProjectDir: File
|
|
19
|
+
|
|
20
|
+
private lateinit var project: org.gradle.api.Project
|
|
21
|
+
private lateinit var extension: GraniteExtension
|
|
22
|
+
|
|
23
|
+
@BeforeEach
|
|
24
|
+
fun setup() {
|
|
25
|
+
project = ProjectBuilder.builder()
|
|
26
|
+
.withProjectDir(testProjectDir)
|
|
27
|
+
.build()
|
|
28
|
+
|
|
29
|
+
// Apply plugins to create extension
|
|
30
|
+
project.pluginManager.apply("com.android.library")
|
|
31
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
32
|
+
|
|
33
|
+
extension = project.extensions.getByType(GraniteExtension::class.java)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
fun `extension has correct default values`() {
|
|
38
|
+
// Then
|
|
39
|
+
assertEquals("src/main/js/index.js", extension.entryFile.get())
|
|
40
|
+
assertEquals("index.android.bundle", extension.bundleAssetName.get())
|
|
41
|
+
assertTrue(extension.bundleCompressionEnabled.get())
|
|
42
|
+
assertEquals(4, extension.nativeArchitectures.get().size)
|
|
43
|
+
assertTrue(extension.nativeArchitectures.get().contains("arm64-v8a"))
|
|
44
|
+
assertTrue(extension.nativeArchitectures.get().contains("armeabi-v7a"))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Test
|
|
48
|
+
fun `extension allows customization of properties`() {
|
|
49
|
+
// When
|
|
50
|
+
extension.entryFile.set("custom/entry.js")
|
|
51
|
+
extension.bundleAssetName.set("custom.bundle")
|
|
52
|
+
extension.bundleCompressionEnabled.set(false)
|
|
53
|
+
extension.nativeArchitectures.set(listOf("arm64-v8a"))
|
|
54
|
+
|
|
55
|
+
// Then
|
|
56
|
+
assertEquals("custom/entry.js", extension.entryFile.get())
|
|
57
|
+
assertEquals("custom.bundle", extension.bundleAssetName.get())
|
|
58
|
+
assertEquals(false, extension.bundleCompressionEnabled.get())
|
|
59
|
+
assertEquals(1, extension.nativeArchitectures.get().size)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Test removed: entryFile validation moved to BundleTask execution time
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
fun `validate succeeds when all required files exist`() {
|
|
66
|
+
// Given
|
|
67
|
+
setupValidProject()
|
|
68
|
+
|
|
69
|
+
// When/Then - Should not throw
|
|
70
|
+
extension.validate()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun `validate succeeds even without entry file`() {
|
|
75
|
+
// Given - Only React Native directory setup, no entry file
|
|
76
|
+
val nodeModules = File(testProjectDir, "node_modules")
|
|
77
|
+
val reactNativeDir = File(nodeModules, "react-native")
|
|
78
|
+
reactNativeDir.mkdirs()
|
|
79
|
+
|
|
80
|
+
val packageJson = File(reactNativeDir, "package.json")
|
|
81
|
+
packageJson.writeText("""{"version":"0.81.6"}""")
|
|
82
|
+
|
|
83
|
+
extension.reactNativeDir.set(reactNativeDir)
|
|
84
|
+
extension.nodeModulesDir.set(nodeModules)
|
|
85
|
+
|
|
86
|
+
// When/Then - Should not throw (entryFile validation deferred to BundleTask)
|
|
87
|
+
extension.validate()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
fun `validate fails when React Native directory does not exist`() {
|
|
92
|
+
// Given - React Native dir not created
|
|
93
|
+
|
|
94
|
+
// When/Then
|
|
95
|
+
assertThrows<IllegalStateException> {
|
|
96
|
+
extension.validate()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Test removed: Hermes is now always enabled, cannot be disabled
|
|
101
|
+
|
|
102
|
+
@Test
|
|
103
|
+
fun `validate fails with invalid ABI`() {
|
|
104
|
+
// Given
|
|
105
|
+
setupValidProject()
|
|
106
|
+
extension.nativeArchitectures.set(listOf("invalid-abi"))
|
|
107
|
+
|
|
108
|
+
// When/Then
|
|
109
|
+
val exception = assertThrows<IllegalStateException> {
|
|
110
|
+
extension.validate()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
assertTrue(exception.message?.contains("Invalid") == true)
|
|
114
|
+
assertTrue(exception.message?.contains("ABI") == true)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Test
|
|
118
|
+
fun `validate accepts all valid ABIs`() {
|
|
119
|
+
// Given
|
|
120
|
+
setupValidProject()
|
|
121
|
+
val validAbis = listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
|
122
|
+
extension.nativeArchitectures.set(validAbis)
|
|
123
|
+
|
|
124
|
+
// When/Then - Should not throw
|
|
125
|
+
extension.validate()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@Test
|
|
129
|
+
fun `getEntryFileResolved returns correct file`() {
|
|
130
|
+
// Given
|
|
131
|
+
extension.entryFile.set("custom/entry.js")
|
|
132
|
+
|
|
133
|
+
// When
|
|
134
|
+
val resolved = extension.getEntryFileResolved()
|
|
135
|
+
|
|
136
|
+
// Then - Use canonical paths to handle symlinks (e.g., /var vs /private/var on macOS)
|
|
137
|
+
assertEquals(
|
|
138
|
+
File(testProjectDir, "custom/entry.js").canonicalPath,
|
|
139
|
+
resolved.canonicalPath,
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@Test
|
|
144
|
+
fun `getReactNativeDirResolved returns correct directory`() {
|
|
145
|
+
// Given
|
|
146
|
+
val customRnDir = File(testProjectDir, "custom/react-native")
|
|
147
|
+
extension.reactNativeDir.set(customRnDir)
|
|
148
|
+
|
|
149
|
+
// When
|
|
150
|
+
val resolved = extension.getReactNativeDirResolved()
|
|
151
|
+
|
|
152
|
+
// Then - Use canonical paths to handle symlinks
|
|
153
|
+
assertEquals(customRnDir.canonicalPath, resolved.canonicalPath)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@Test
|
|
157
|
+
fun `getNodeModulesDirResolved returns correct directory`() {
|
|
158
|
+
// Given
|
|
159
|
+
val customNodeModules = File(testProjectDir, "custom/node_modules")
|
|
160
|
+
extension.nodeModulesDir.set(customNodeModules)
|
|
161
|
+
|
|
162
|
+
// When
|
|
163
|
+
val resolved = extension.getNodeModulesDirResolved()
|
|
164
|
+
|
|
165
|
+
// Then - Use canonical paths to handle symlinks
|
|
166
|
+
assertEquals(customNodeModules.canonicalPath, resolved.canonicalPath)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Sets up a valid project structure for testing.
|
|
171
|
+
*/
|
|
172
|
+
private fun setupValidProject() {
|
|
173
|
+
// Create entry file
|
|
174
|
+
val entryFile = File(testProjectDir, "src/main/js/index.js")
|
|
175
|
+
entryFile.parentFile.mkdirs()
|
|
176
|
+
entryFile.writeText("// Entry file")
|
|
177
|
+
|
|
178
|
+
// Create React Native directory structure
|
|
179
|
+
val nodeModules = File(testProjectDir, "node_modules")
|
|
180
|
+
val reactNativeDir = File(nodeModules, "react-native")
|
|
181
|
+
reactNativeDir.mkdirs()
|
|
182
|
+
|
|
183
|
+
// Create package.json
|
|
184
|
+
val packageJson = File(reactNativeDir, "package.json")
|
|
185
|
+
packageJson.writeText("""{"version":"0.81.6"}""")
|
|
186
|
+
|
|
187
|
+
// Update extension to point to test directories
|
|
188
|
+
extension.reactNativeDir.set(reactNativeDir)
|
|
189
|
+
extension.nodeModulesDir.set(nodeModules)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
package run.granite.gradle
|
|
2
|
+
|
|
3
|
+
import org.gradle.testfixtures.ProjectBuilder
|
|
4
|
+
import org.junit.jupiter.api.BeforeEach
|
|
5
|
+
import org.junit.jupiter.api.Test
|
|
6
|
+
import org.junit.jupiter.api.assertThrows
|
|
7
|
+
import org.junit.jupiter.api.io.TempDir
|
|
8
|
+
import java.io.File
|
|
9
|
+
import kotlin.test.assertEquals
|
|
10
|
+
import kotlin.test.assertNotNull
|
|
11
|
+
import kotlin.test.assertTrue
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Unit tests for GranitePlugin.
|
|
15
|
+
*/
|
|
16
|
+
class GranitePluginTest {
|
|
17
|
+
|
|
18
|
+
@TempDir
|
|
19
|
+
lateinit var testProjectDir: File
|
|
20
|
+
|
|
21
|
+
private lateinit var buildFile: File
|
|
22
|
+
private lateinit var settingsFile: File
|
|
23
|
+
|
|
24
|
+
@BeforeEach
|
|
25
|
+
fun setup() {
|
|
26
|
+
buildFile = File(testProjectDir, "build.gradle.kts")
|
|
27
|
+
settingsFile = File(testProjectDir, "settings.gradle.kts")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Test
|
|
31
|
+
fun `plugin applies successfully to library project`() {
|
|
32
|
+
// Given
|
|
33
|
+
val project = ProjectBuilder.builder()
|
|
34
|
+
.withProjectDir(testProjectDir)
|
|
35
|
+
.build()
|
|
36
|
+
|
|
37
|
+
// When
|
|
38
|
+
project.pluginManager.apply("com.android.library")
|
|
39
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
40
|
+
|
|
41
|
+
// Then
|
|
42
|
+
assertTrue(project.plugins.hasPlugin(GranitePlugin::class.java))
|
|
43
|
+
assertNotNull(project.extensions.findByName("granite"))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
fun `plugin creates granite extension with correct type`() {
|
|
48
|
+
// Given
|
|
49
|
+
val project = ProjectBuilder.builder()
|
|
50
|
+
.withProjectDir(testProjectDir)
|
|
51
|
+
.build()
|
|
52
|
+
|
|
53
|
+
// When
|
|
54
|
+
project.pluginManager.apply("com.android.library")
|
|
55
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
56
|
+
|
|
57
|
+
// Then
|
|
58
|
+
val extension = project.extensions.findByName("granite")
|
|
59
|
+
assertNotNull(extension)
|
|
60
|
+
assertTrue(extension is GraniteExtension)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Test
|
|
64
|
+
fun `plugin registers autolinking task`() {
|
|
65
|
+
// Given
|
|
66
|
+
val project = ProjectBuilder.builder()
|
|
67
|
+
.withProjectDir(testProjectDir)
|
|
68
|
+
.build()
|
|
69
|
+
|
|
70
|
+
setupMinimalProject(project)
|
|
71
|
+
|
|
72
|
+
// When
|
|
73
|
+
project.pluginManager.apply("com.android.library")
|
|
74
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
75
|
+
|
|
76
|
+
// Force evaluation of afterEvaluate blocks by accessing the task container
|
|
77
|
+
// Note: This is a simplified test. Full task registration should be tested with GradleRunner.
|
|
78
|
+
// For now, we just verify the plugin applies without errors
|
|
79
|
+
assertTrue(project.plugins.hasPlugin(GranitePlugin::class.java))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Test
|
|
83
|
+
fun `plugin registers codegen tasks`() {
|
|
84
|
+
// Given
|
|
85
|
+
val project = ProjectBuilder.builder()
|
|
86
|
+
.withProjectDir(testProjectDir)
|
|
87
|
+
.build()
|
|
88
|
+
|
|
89
|
+
setupMinimalProject(project)
|
|
90
|
+
|
|
91
|
+
// When
|
|
92
|
+
project.pluginManager.apply("com.android.library")
|
|
93
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
94
|
+
|
|
95
|
+
// Note: Task registration happens in afterEvaluate, which doesn't execute with ProjectBuilder.
|
|
96
|
+
// Full task registration should be tested with GradleRunner integration tests.
|
|
97
|
+
// For now, we verify the plugin and extension are configured correctly
|
|
98
|
+
val extension = project.extensions.findByName("granite")
|
|
99
|
+
assertNotNull(extension)
|
|
100
|
+
assertTrue(extension is GraniteExtension)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Test
|
|
104
|
+
fun `plugin fails when applied to application project`() {
|
|
105
|
+
// Given
|
|
106
|
+
val project = ProjectBuilder.builder()
|
|
107
|
+
.withProjectDir(testProjectDir)
|
|
108
|
+
.build()
|
|
109
|
+
|
|
110
|
+
// When/Then
|
|
111
|
+
project.pluginManager.apply("com.android.application")
|
|
112
|
+
|
|
113
|
+
// Gradle wraps exceptions in PluginApplicationException
|
|
114
|
+
val exception = assertThrows<org.gradle.api.internal.plugins.PluginApplicationException> {
|
|
115
|
+
project.pluginManager.apply(GranitePlugin::class.java)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Verify the cause is our expected IllegalStateException
|
|
119
|
+
assertTrue(exception.cause is IllegalStateException)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `plugin constants are correct`() {
|
|
124
|
+
assertEquals("run.granite.library", GranitePlugin.PLUGIN_ID)
|
|
125
|
+
assertEquals("granite", GranitePlugin.EXTENSION_NAME)
|
|
126
|
+
assertEquals("granite", GranitePlugin.PLUGIN_GROUP)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Sets up minimal project structure for testing.
|
|
131
|
+
*/
|
|
132
|
+
private fun setupMinimalProject(project: org.gradle.api.Project) {
|
|
133
|
+
// Create minimal required directories
|
|
134
|
+
File(testProjectDir, "src/main/js").mkdirs()
|
|
135
|
+
File(testProjectDir, "src/main/java").mkdirs()
|
|
136
|
+
|
|
137
|
+
// Create minimal entry file
|
|
138
|
+
val entryFile = File(testProjectDir, "src/main/js/index.js")
|
|
139
|
+
entryFile.parentFile.mkdirs()
|
|
140
|
+
entryFile.writeText("// Empty entry file")
|
|
141
|
+
|
|
142
|
+
// Create fake node_modules structure
|
|
143
|
+
val nodeModules = File(testProjectDir, "node_modules")
|
|
144
|
+
val reactNativeDir = File(nodeModules, "react-native")
|
|
145
|
+
val androidDir = File(reactNativeDir, "android")
|
|
146
|
+
androidDir.mkdirs()
|
|
147
|
+
|
|
148
|
+
// Create package.json
|
|
149
|
+
val packageJson = File(reactNativeDir, "package.json")
|
|
150
|
+
packageJson.writeText("""{"version":"0.81.6"}""")
|
|
151
|
+
|
|
152
|
+
// Create cli.js
|
|
153
|
+
val cliJs = File(reactNativeDir, "cli.js")
|
|
154
|
+
cliJs.writeText("// Mock CLI")
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
package run.granite.gradle
|
|
2
|
+
|
|
3
|
+
import org.gradle.testfixtures.ProjectBuilder
|
|
4
|
+
import org.junit.jupiter.api.Test
|
|
5
|
+
import org.junit.jupiter.api.assertThrows
|
|
6
|
+
import kotlin.test.assertEquals
|
|
7
|
+
import kotlin.test.assertNotNull
|
|
8
|
+
import kotlin.test.assertTrue
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Unit tests for GraniteRootProjectPlugin.
|
|
12
|
+
*/
|
|
13
|
+
class GraniteRootProjectPluginTest {
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun `plugin constants are correct`() {
|
|
17
|
+
assertEquals("run.granite.rootproject", GraniteRootProjectPlugin.PLUGIN_ID)
|
|
18
|
+
assertEquals("graniteRoot", GraniteRootProjectPlugin.EXTENSION_NAME)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Test
|
|
22
|
+
fun `plugin applies successfully to root project`() {
|
|
23
|
+
// Given
|
|
24
|
+
val project = ProjectBuilder.builder().build()
|
|
25
|
+
|
|
26
|
+
// When
|
|
27
|
+
project.pluginManager.apply(GraniteRootProjectPlugin::class.java)
|
|
28
|
+
|
|
29
|
+
// Then
|
|
30
|
+
assertTrue(project.plugins.hasPlugin(GraniteRootProjectPlugin::class.java))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Test
|
|
34
|
+
fun `plugin creates graniteRoot extension`() {
|
|
35
|
+
// Given
|
|
36
|
+
val project = ProjectBuilder.builder().build()
|
|
37
|
+
|
|
38
|
+
// When
|
|
39
|
+
project.pluginManager.apply(GraniteRootProjectPlugin::class.java)
|
|
40
|
+
|
|
41
|
+
// Then
|
|
42
|
+
val extension = project.extensions.findByName("graniteRoot")
|
|
43
|
+
assertNotNull(extension)
|
|
44
|
+
assertTrue(extension is GraniteRootExtension)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Test
|
|
48
|
+
fun `extension has correct default values`() {
|
|
49
|
+
// Given
|
|
50
|
+
val project = ProjectBuilder.builder().build()
|
|
51
|
+
project.pluginManager.apply(GraniteRootProjectPlugin::class.java)
|
|
52
|
+
|
|
53
|
+
// When
|
|
54
|
+
val extension = project.extensions.getByType(GraniteRootExtension::class.java)
|
|
55
|
+
|
|
56
|
+
// Then
|
|
57
|
+
assertEquals("com.facebook.react", extension.reactGroup.get())
|
|
58
|
+
assertEquals("com.facebook.hermes", extension.hermesGroup.get())
|
|
59
|
+
assertEquals(project.file("node_modules"), extension.nodeModulesDir.get())
|
|
60
|
+
// reactNativeVersion and hermesVersion should not have conventions (are not present)
|
|
61
|
+
assertTrue(!extension.reactNativeVersion.isPresent)
|
|
62
|
+
assertTrue(!extension.hermesVersion.isPresent)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Test
|
|
66
|
+
fun `plugin fails when applied to non-root project`() {
|
|
67
|
+
// Given
|
|
68
|
+
val rootProject = ProjectBuilder.builder()
|
|
69
|
+
.withName("root")
|
|
70
|
+
.build()
|
|
71
|
+
val subProject = ProjectBuilder.builder()
|
|
72
|
+
.withName("sub")
|
|
73
|
+
.withParent(rootProject)
|
|
74
|
+
.build()
|
|
75
|
+
|
|
76
|
+
// When/Then
|
|
77
|
+
// Gradle wraps exceptions in PluginApplicationException
|
|
78
|
+
val exception = assertThrows<org.gradle.api.internal.plugins.PluginApplicationException> {
|
|
79
|
+
subProject.pluginManager.apply(GraniteRootProjectPlugin::class.java)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verify the cause is our expected IllegalStateException
|
|
83
|
+
assertTrue(exception.cause is IllegalStateException)
|
|
84
|
+
assertTrue(exception.cause!!.message!!.contains("can only be applied to the root project"))
|
|
85
|
+
assertTrue(exception.cause!!.message!!.contains("Current project: :sub"))
|
|
86
|
+
}
|
|
87
|
+
}
|
package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
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 BuildConfigConfigurator.
|
|
14
|
+
*
|
|
15
|
+
* Tests the configuration of Android BuildConfig field generation for
|
|
16
|
+
* feature flags and React Native configuration.
|
|
17
|
+
*/
|
|
18
|
+
class BuildConfigConfiguratorTest {
|
|
19
|
+
|
|
20
|
+
private lateinit var project: Project
|
|
21
|
+
private lateinit var extension: GraniteExtension
|
|
22
|
+
private lateinit var androidExtension: LibraryExtension
|
|
23
|
+
private lateinit var configurator: BuildConfigConfigurator
|
|
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 = BuildConfigConfigurator(project, extension)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
fun `configurator is created successfully`() {
|
|
38
|
+
assertNotNull(configurator, "BuildConfigConfigurator should be created")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `configure method executes without errors`() {
|
|
43
|
+
// Hermes is always enabled
|
|
44
|
+
extension.reactNativeVersion.set("0.81.0")
|
|
45
|
+
extension.bundleAssetName.set("index.android")
|
|
46
|
+
|
|
47
|
+
// Should not throw
|
|
48
|
+
configurator.configure(androidExtension)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `buildConfig feature is enabled`() {
|
|
53
|
+
// Hermes is always enabled
|
|
54
|
+
extension.reactNativeVersion.set("0.81.0")
|
|
55
|
+
extension.bundleAssetName.set("index.android")
|
|
56
|
+
|
|
57
|
+
configurator.configure(androidExtension)
|
|
58
|
+
|
|
59
|
+
// Verify BuildConfig is enabled
|
|
60
|
+
assertTrue(
|
|
61
|
+
androidExtension.buildFeatures.buildConfig == true,
|
|
62
|
+
"BuildConfig feature should be enabled",
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
fun `configure works with default extension values`() {
|
|
68
|
+
// Should not throw with default values
|
|
69
|
+
configurator.configure(androidExtension)
|
|
70
|
+
|
|
71
|
+
assertTrue(
|
|
72
|
+
androidExtension.buildFeatures.buildConfig == true,
|
|
73
|
+
"BuildConfig feature should be enabled even with defaults",
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Test removed: Hermes is now always enabled, cannot be disabled
|
|
78
|
+
|
|
79
|
+
@Test
|
|
80
|
+
fun `configure works with new architecture enabled`() {
|
|
81
|
+
// Both Hermes and New Architecture are always enabled
|
|
82
|
+
extension.reactNativeVersion.set("0.81.0")
|
|
83
|
+
extension.bundleAssetName.set("index.android")
|
|
84
|
+
|
|
85
|
+
// Should not throw
|
|
86
|
+
configurator.configure(androidExtension)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Test
|
|
90
|
+
fun `configure can be called multiple times`() {
|
|
91
|
+
// Hermes is always enabled
|
|
92
|
+
extension.reactNativeVersion.set("0.81.0")
|
|
93
|
+
extension.bundleAssetName.set("index.android")
|
|
94
|
+
|
|
95
|
+
// Should not throw when called multiple times
|
|
96
|
+
configurator.configure(androidExtension)
|
|
97
|
+
configurator.configure(androidExtension)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Test
|
|
101
|
+
fun `configure with different react native versions`() {
|
|
102
|
+
extension.reactNativeVersion.set("0.82.0")
|
|
103
|
+
|
|
104
|
+
// Should not throw
|
|
105
|
+
configurator.configure(androidExtension)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun `configure with custom bundle asset name`() {
|
|
110
|
+
extension.bundleAssetName.set("custom.bundle")
|
|
111
|
+
|
|
112
|
+
// Should not throw
|
|
113
|
+
configurator.configure(androidExtension)
|
|
114
|
+
}
|
|
115
|
+
}
|