WIP: rename sample app to Folio (#20)

* refactor: rename examples/sample-app to examples/folio

Directory-level rename and path references in Go tests, bundle-check,
top-level README, and getting-started docs. Package declarations,
Gradle config, iOS bundle IDs, and class names follow in later commits.

* refactor(folio): rename Kotlin package dev.uatu.sample to app.folio

Moves source dirs and sqldelight schema from dev/uatu/sample to
app/folio, updates package declarations and imports, and switches
Android namespace/applicationId, iOS binaryOption bundleId, and
sqldelight database packageName to the new identifier.

* refactor(folio): rename SampleApplication to FolioApplication

Android manifest now points at .FolioApplication with label 'Folio'
instead of 'Uatu Sample'.

* refactor(folio): set iOS bundle id and display name to Folio

bundleIdPrefix + PRODUCT_BUNDLE_IDENTIFIER -> app.folio.
CFBundleName + CFBundleDisplayName -> 'Folio'.

* refactor(folio): update demo email to [email protected]

* refactor(folio): point justfile at app.folio bundle id

Updates xcrun simctl launch target, uatu test --bundle-id, and the
build/uninstall comments to reference folio instead of sample.

* test: update fixture package ids to app.folio

Sidecar activity-resolver test and verifier spec-integration XML
fixtures referenced the old dev.uatu.sample Android package. Updates
them to match the folio app's real package id so the tests stay
representative of what the CLI sees on-device.

* test(verifier): rename SampleApp identifiers to Folio

Renames TestSampleAppSpec* functions, bundleSampleAppSpec helper, and
sampleAppHierarchyXML const (now loginHierarchyXML for consistency with
the other per-screen fixtures). Updates trailing sample-app mentions in
comments and assertion messages.

* refactor(folio): rename Gradle/npm/wasm project identifiers to folio

settings.gradle.kts rootProject.name, package.json + package-lock.json
name, and the WasmJS index.html <title> all still read 'uatu-sample' /
'Uatu Sample'. Realigns them with the Folio brand.

* docs(folio): rewrite README title + getting-started bundle id

examples/folio/README.md is now titled 'Folio' with the Kotlin source
paths corrected to app/folio. Getting-started example uses --bundle-id
app.folio. Harness launch message is now generic ('app under test')
since uatu-sample-harness is not specific to folio.

* chore(folio): drop trailing 'sample' reference in gradle.properties

* refactor(folio): rename LoginPage composable to LoginScreen

Align with KMP/Android industry convention (NowInAndroid, Cash App,
JetBrains samples use Screen, not Page).

* refactor(folio): rename HomePage composable to HomeScreen

* refactor(folio): rename AddAccountPage composable to AddAccountScreen

* refactor(folio): rename LedgerPage composable to LedgerScreen

* refactor(folio): rename AddTransactionPage composable to AddTransactionScreen

* refactor(folio): split Models.kt into app.folio.data package

Account, Transaction (with TxnType), and Session move into their own
files under app.folio.data, matching NowInAndroid-style per-type
organization.

* refactor(folio): move data layer into app.folio.data package

Repository, LedgerStore (expect + interface), SqlLedgerStore,
WebLedgerStore, DriverFactory (expect + actuals), AndroidLedgerContext,
and Snapshot move into app.folio.data. Update all consumer imports.

* refactor(folio): move Navigation into app.folio.navigation package

Split the former Navigation.kt into Route.kt (sealed interface) and
Navigator.kt (singleton). Update consumer imports across screens,
App.kt, and FolioApplication.

* refactor(folio): move Platform and Format into app.folio.platform

Both files carry expect declarations (Platform object, formatDate);
grouping them into a dedicated platform package makes the KMP seam
obvious and mirrors the structure used by JetBrains samples.

* refactor(folio): move login into feature/auth package

Create app.folio.feature.auth with LoginScreen + LoginUiState. Inline
the former Auth.kt (DEMO_EMAIL, DEMO_PASSWORD, checkCredentials) into
LoginScreen since it is the sole caller.

* refactor(folio): move HomeScreen into feature/home package

* refactor(folio): move account creation into feature/account package

AddAccountScreen gets its own AddAccountUiState colocated with the
screen, replacing the shared UiState.addAccountError.

* refactor(folio): move ledger screens into feature/ledger package

LedgerScreen and AddTransactionScreen move into app.folio.feature.ledger
with AddTransactionUiState (txnError, txnFormType) colocated. The
former catch-all UiState.kt is removed now that each screen owns its
state alongside its UI.

* refactor(folio): split Theme.kt; move theme and icons to subpackages

Theme split into Theme.kt (tokens, layout dims, LedgerTheme) and
Type.kt (typography) under app.folio.ui.theme. Icons moves to
app.folio.ui.icon. Update every consumer's imports to match.

* refactor(folio): split ui components into per-file under ui/component

Former Widgets.kt and Components.kt become 10 focused files: AppButton,
Card, EmptyState, ErrorText, FieldLabel, Header, IconButton (w/
BackButton), Screen, Segmented, TextInput. Matches NowInAndroid style
of one composable per file in a designsystem/component package.

* chore(folio): consolidate uatu testing files under uatu/ folder

Move spec.ts, package.json, package-lock.json into examples/folio/uatu
so all uatu-specific testing artifacts live in one place. runs/ and
node_modules/ follow the same convention (both remain gitignored).
Update justfile, README, and the two Go consumers (bundle-check tool +
verifier/trace tests) that referenced the old path.

* refactor(trace): drop folio path in writer test

Round-trip only needs a non-empty string; neutralize to keep the
library free of folio references.

* refactor(sidecar): neutralize ResolveActivity test fixtures

Swap app.folio for com.example.app in the fixture strings so the
sidecar tests don't reference the example app by name.

* refactor(bundle-check): take spec path as argument

Previously the tool hardcoded examples/folio/uatu/spec.ts. Accept a
positional spec path instead so the tool works for any example and
leaves no folio reference in the library surface.

* test(verifier): add neutral integration spec and hierarchy fixtures

Adds testdata/integration_spec.ts with two routes ("list", "form"),
an InputText on text_field, a Tap on primary/secondary_action, a
safety property (itemCountNonNegative), and a liveness property
(submitEventually). Adds hierarchies_test.go with matching XML
fixtures. Constants intentionally go in a _test.go at package root
rather than testdata/hierarchies.go because go skips .go files
under testdata/.

* refactor(verifier): replace folio integration tests with neutral ones

Renames bundleFolioSpec -> bundleIntegrationSpec and the three Test*
entry points to TestIntegrationSpec*. Uses the synthetic spec and
hierarchies added in the previous commit so the library's test suite
no longer references examples/folio at all.

Folio-specific coverage remains covered by examples/folio/justfile's
'just test' which exercises the real spec on device/emulator.

* chore: remove cmd/uatu-sample-harness

Not referenced by Makefile, docs, CI, or any script. Duplicates the
adb reverse helpers already in cmd/uatu/test_run.go, and its name
implies ownership by the sample app which violates the library/
example decoupling. If a bare-protocol debugging tool is later
needed it belongs inside cmd/uatu/.

* docs(folio): drop Layout section and KMP layout paragraph; fix AVD override syntax

The directory-tree Layout section rots faster than the code and
duplicates what ls shows for free. The expect/actual paragraph in
Stack was the same kind of filler. The README also claimed 'just
AVD=Pixel_7 test' but the justfile reads AVD as an env var via
env_var_or_default, so the correct invocation is 'AVD=Pixel_7
just test'.
This commit is contained in:
pj authored and GitHub committed 2026-04-20 16:04:20 +07:00
1 parent c1de4bf57a
commit a2e96af1af
88 files changed
+949 -900

No files matched your search

+15 -2
View File
@@ -9,17 +9,30 @@ import (
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: bundle-check <spec.ts>")
os.Exit(1)
}
entryFile, err := filepath.Abs(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "resolve spec path: %v\n", err)
os.Exit(1)
}
repoRoot, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "getwd: %v\n", err)
os.Exit(1)
}
specApiPath := filepath.Join(repoRoot, "pkg/spec-api/src/index.ts")
defaultPropertiesPath := filepath.Join(repoRoot, "pkg/spec-api/src/defaults/properties.ts")
result, err := bundler.Bundle(bundler.Options{
EntryFile: "examples/sample-app/spec.ts",
EntryFile: entryFile,
Aliases: map[string]string{
"@uatu/spec": specApiPath,
"@uatu/spec": specApiPath,
"@uatu/spec/defaults/properties": defaultPropertiesPath,
},
})
if err != nil {
-98
View File
@@ -1,98 +0,0 @@
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"github.com/priyanshujain/uatu/internal/agent"
)
func main() {
port := flag.Int("port", 0, "TCP port to listen on (0 = ephemeral)")
socketName := flag.String("socket-name", "uatu-agent", "abstract socket name on the device")
cycles := flag.Int("cycles", 3, "number of snapshot cycles to run")
serial := flag.String("serial", "", "adb device serial (empty = default)")
cycleInterval := flag.Duration("interval", 500*time.Millisecond, "delay between snapshot cycles")
flag.Parse()
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
if err != nil {
die("listen: %v", err)
}
defer listener.Close()
listenerAddr := listener.Addr().(*net.TCPAddr)
fmt.Printf("listening on 127.0.0.1:%d\n", listenerAddr.Port)
if err := adbReverse(*serial, *socketName, listenerAddr.Port); err != nil {
die("adb reverse: %v", err)
}
defer func() {
if err := adbReverseRemove(*serial, *socketName); err != nil {
fmt.Fprintf(os.Stderr, "adb reverse cleanup: %v\n", err)
}
}()
fmt.Printf("forwarded localabstract:%s -> tcp:%d on device\n", *socketName, listenerAddr.Port)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
server := agent.NewServer(listener)
fmt.Println("waiting for SDK to connect (launch the sample app on the device)...")
conn, err := server.Accept(ctx)
if err != nil {
die("accept: %v", err)
}
defer conn.Close()
hello := conn.Hello()
fmt.Printf("HELLO from platform=%s app=%s sdk=%s\n", hello.Platform, hello.AppPackage, hello.Version)
for index := 1; index <= *cycles; index++ {
snapshotCtx, snapshotCancel := context.WithTimeout(ctx, 5*time.Second)
state, err := conn.Snapshot(snapshotCtx)
snapshotCancel()
if err != nil {
die("snapshot #%d: %v", index, err)
}
marshalled, _ := json.Marshal(state.Snapshots)
fmt.Printf("STATE %d: %s\n", index, marshalled)
if err := conn.Release(ctx); err != nil {
die("release #%d: %v", index, err)
}
if index < *cycles {
time.Sleep(*cycleInterval)
}
}
fmt.Println("round-trip complete")
}
func adbReverse(serial, socketName string, port int) error {
commandArguments := []string{"reverse", fmt.Sprintf("localabstract:%s", socketName), fmt.Sprintf("tcp:%d", port)}
if serial != "" {
commandArguments = append([]string{"-s", serial}, commandArguments...)
}
command := exec.Command("adb", commandArguments...)
command.Stdout = os.Stdout
command.Stderr = os.Stderr
return command.Run()
}
func adbReverseRemove(serial, socketName string) error {
commandArguments := []string{"reverse", "--remove", fmt.Sprintf("localabstract:%s", socketName)}
if serial != "" {
commandArguments = append([]string{"-s", serial}, commandArguments...)
}
return exec.Command("adb", commandArguments...).Run()
}
func die(format string, args ...any) {
fmt.Fprintf(os.Stderr, "error: "+format+"\n", args...)
os.Exit(1)
}