From 0b9e33da78df2523bbac6380b96534a1014b327f Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sun, 10 May 2026 15:08:15 +0200 Subject: [PATCH] feat: scaffold counter_kt via init_kotlin_app --- .gitignore | 11 +++ README.md | 18 +++++ app.md | 68 +++++++++++++++++++ app/build.gradle.kts | 63 +++++++++++++++++ .../fnregistry/counterkt/MainActivityTest.kt | 23 +++++++ app/src/main/AndroidManifest.xml | 20 ++++++ .../com/fnregistry/counterkt/MainActivity.kt | 24 +++++++ app/src/main/res/values/strings.xml | 3 + .../counterkt/ExampleScreenshotTest.kt | 39 +++++++++++ build.gradle.kts | 5 ++ gradle.properties | 4 ++ gradle/wrapper/gradle-wrapper.properties | 7 ++ gradlew | 7 ++ settings.gradle.kts | 23 +++++++ 14 files changed, 315 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app.md create mode 100644 app/build.gradle.kts create mode 100644 app/src/androidTest/kotlin/com/fnregistry/counterkt/MainActivityTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/kotlin/com/fnregistry/counterkt/MainActivity.kt create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/test/kotlin/com/fnregistry/counterkt/ExampleScreenshotTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a943de5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.gradle/ +build/ +local.properties +*.iml +.idea/ +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +*.aab +app/src/test/snapshots/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3422b1c --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# counter_kt + +Counter demo: validates FnTheme + Material3 + state + tests + +Generado con `init_kotlin_app` del registry fn_registry. + +## Requisitos + +- Android SDK 34 +- JDK 17 +- Gradle 8.6 (via wrapper) +- `kotlin/functions/ui` modulo del registry (composite build) + +## Build rapido + +```bash +fn run gradle_assemble_debug_bash_infra apps/counter_kt +``` diff --git a/app.md b/app.md new file mode 100644 index 0000000..11c1d7b --- /dev/null +++ b/app.md @@ -0,0 +1,68 @@ +--- +name: counter_kt +description: "Counter demo: validates FnTheme + Material3 + state + tests" +tags: [demo, counter, kotlin, compose, android] +lang: kt +framework: compose +entry_point: "app/src/main/kotlin/com/fnregistry/counterkt/MainActivity.kt" +dir_path: "apps/counter_kt" +repo_url: "https://gitea.organic-machine.com/dataforge/counter_kt" +uses_functions: + - fn_theme_kt_ui + - fn_tokens_kt_ui +uses_types: [] +e2e_checks: + - id: unit + cmd: "fn run gradle_unit_test_bash_infra apps/counter_kt" + timeout_s: 240 + - id: screenshot + cmd: "fn run gradle_screenshot_test_bash_infra apps/counter_kt" + timeout_s: 240 + - id: build + cmd: "fn run gradle_assemble_debug_bash_infra apps/counter_kt" + timeout_s: 360 + - id: emu_start + cmd: "fn run android_emulator_start_bash_infra Medium_Phone_API_35" + timeout_s: 240 + - id: instrumented + cmd: "fn run gradle_instrumented_test_bash_infra apps/counter_kt" + timeout_s: 600 + - id: emu_stop + cmd: "fn run android_emulator_stop_bash_infra" + severity: warning + timeout_s: 30 +--- + +# counter_kt + +Counter demo: validates FnTheme + Material3 + state + tests + +## Build + +```bash +fn run gradle_assemble_debug_bash_infra apps/counter_kt +``` + +## Tests unitarios + Roborazzi screenshots + +```bash +fn run gradle_unit_test_bash_infra apps/counter_kt +fn run gradle_screenshot_test_bash_infra apps/counter_kt +``` + +## Tests instrumentados (requiere emulador) + +```bash +fn run android_emulator_start_bash_infra Medium_Phone_API_35 +fn run gradle_instrumented_test_bash_infra apps/counter_kt +fn run android_emulator_stop_bash_infra +``` + +## Package + +`com.fnregistry.counterkt` + +## FnTheme + +La app usa FnTheme y FnTokens via composite build en `kotlin/functions/ui`. +Para cambiar colores o tipografia, editar el modulo del registry. diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..9f286a8 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,63 @@ +plugins { + id("com.android.application") version "8.4.0" + id("org.jetbrains.kotlin.android") version "1.9.22" + id("io.github.takahirom.roborazzi") version "1.20.0" +} + +android { + namespace = "com.fnregistry.counterkt" + compileSdk = 34 + + defaultConfig { + applicationId = "com.fnregistry.counterkt" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "0.1.0" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.8" + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } + testOptions { + unitTests { + isIncludeAndroidResources = true + all { + it.systemProperty("robolectric.graphicsMode", "NATIVE") + } + } + } +} + +dependencies { + implementation("androidx.activity:activity-compose:1.8.2") + implementation(platform("androidx.compose:compose-bom:2024.02.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui-tooling-preview") + debugImplementation("androidx.compose.ui:ui-tooling") + // FnTheme + FnTokens via composite build + implementation("fn.compose:ui") + + testImplementation("junit:junit:4.13.2") + testImplementation("org.robolectric:robolectric:4.11.1") + testImplementation("androidx.compose.ui:ui-test-junit4") + testImplementation("io.github.takahirom.roborazzi:roborazzi:1.20.0") + testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.20.0") + testImplementation("io.github.takahirom.roborazzi:roborazzi-junit-rule:1.20.0") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-test-manifest") +} diff --git a/app/src/androidTest/kotlin/com/fnregistry/counterkt/MainActivityTest.kt b/app/src/androidTest/kotlin/com/fnregistry/counterkt/MainActivityTest.kt new file mode 100644 index 0000000..9cef2d1 --- /dev/null +++ b/app/src/androidTest/kotlin/com/fnregistry/counterkt/MainActivityTest.kt @@ -0,0 +1,23 @@ +package com.fnregistry.counterkt + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MainActivityTest { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + @Test + fun appLaunchesAndShowsReadyText() { + composeTestRule + .onNodeWithText("counter_kt ready") + .assertIsDisplayed() + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5d9e368 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/kotlin/com/fnregistry/counterkt/MainActivity.kt b/app/src/main/kotlin/com/fnregistry/counterkt/MainActivity.kt new file mode 100644 index 0000000..cd37af0 --- /dev/null +++ b/app/src/main/kotlin/com/fnregistry/counterkt/MainActivity.kt @@ -0,0 +1,24 @@ +package com.fnregistry.counterkt + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import fn.compose.theme.FnTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + FnTheme { + Surface(modifier = Modifier.fillMaxSize()) { + Text("counter_kt ready") + } + } + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..9b43baf --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + counter_kt + diff --git a/app/src/test/kotlin/com/fnregistry/counterkt/ExampleScreenshotTest.kt b/app/src/test/kotlin/com/fnregistry/counterkt/ExampleScreenshotTest.kt new file mode 100644 index 0000000..8a453b7 --- /dev/null +++ b/app/src/test/kotlin/com/fnregistry/counterkt/ExampleScreenshotTest.kt @@ -0,0 +1,39 @@ +package com.fnregistry.counterkt + +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.ui.test.junit4.createComposeRule +import com.github.takahirom.roborazzi.RoborazziRule +import fn.compose.theme.FnTheme +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class ExampleScreenshotTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @get:Rule + val roborazziRule = RoborazziRule( + options = RoborazziRule.Options( + outputDirectoryPath = "src/test/snapshots/images", + ), + ) + + @Test + fun screenshotFnThemeSurface() { + composeTestRule.setContent { + FnTheme { + Surface { + Text("counter_kt screenshot") + } + } + } + // Captura automaticamente al finalizar el test via RoborazziRule + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..0199fbb --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.4.0" apply false + id("org.jetbrains.kotlin.android") version "1.9.22" apply false +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f0a2e55 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +kotlin.code.style=official +android.nonTransitiveRClass=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a80b22c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..bb6e1a9 --- /dev/null +++ b/gradlew @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Gradle wrapper stub — descarga gradle-wrapper.jar si no existe. +# En CI / en WSL instalar el wrapper real con: gradle wrapper --gradle-version 8.6 +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$SCRIPT_DIR/gradle/wrapper/gradlew" "$@" 2>/dev/null || \ + exec gradle "$@" diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..07961f2 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "counter_kt" +include(":app") + +// Composite build: FnTheme + FnTokens desde el registry +includeBuild("../../kotlin/functions/ui") { + dependencySubstitution { + substitute(module("fn.compose:ui")).using(project(":")) + } +}