package verifier import ( "encoding/json" "path/filepath" "testing" "github.com/priyanshujain/uatu/internal/bundler" "github.com/priyanshujain/uatu/internal/hierarchy" "github.com/priyanshujain/uatu/internal/ltl" ) const sampleAppHierarchyXML = ` ` // bundleSampleAppSpec bundles examples/sample-app/spec.ts via the real // @uatu/spec API so the integration test exercises the same path the CLI uses. func bundleSampleAppSpec(t *testing.T) string { t.Helper() specPath, err := filepath.Abs("../../examples/sample-app/spec.ts") if err != nil { t.Fatal(err) } apiPath, err := filepath.Abs("../../pkg/spec-api/src/index.ts") if err != nil { t.Fatal(err) } bundle, err := bundler.Bundle(bundler.Options{ EntryFile: specPath, Aliases: map[string]string{"@uatu/spec": apiPath}, }) if err != nil { t.Fatal(err) } return string(bundle.JavaScript) } // TestSampleAppSpecTapsClickMe verifies the bundled sample-app spec emits a // Tap on the "Click me" button when that button is present in the hierarchy. func TestSampleAppSpecTapsClickMe(t *testing.T) { v := newVerifier(t) if err := v.Load(bundleSampleAppSpec(t)); err != nil { t.Fatal(err) } tree, err := hierarchy.Parse(sampleAppHierarchyXML) if err != nil { t.Fatal(err) } snapshots := Snapshots{ "app_state": json.RawMessage(`"running"`), "click_count": json.RawMessage(`0`), } if err := v.PushSnapshot(snapshots, tree); err != nil { t.Fatal(err) } tapHits := 0 inputHits := 0 for range 400 { action, err := v.NextAction() if err != nil { continue } switch { case action.Kind == ActionKindTap && action.On == "text:Click me": tapHits++ case action.Kind == ActionKindInputText && action.On == "desc:username_field" && action.Text == "alice": inputHits++ } } if tapHits == 0 { t.Fatal("tapClickMe never fired on sample-app hierarchy") } if inputHits == 0 { t.Fatal("typeUsername never fired on sample-app hierarchy") } } // TestSampleAppSpecPropertiesHold checks the three properties declared in the // sample-app spec evaluate correctly across a realistic snapshot sequence. func TestSampleAppSpecPropertiesHold(t *testing.T) { v := newVerifier(t) if err := v.Load(bundleSampleAppSpec(t)); err != nil { t.Fatal(err) } steps := []struct { appState string clickCount int username string want map[string]ltl.Verdict }{ {"running", 0, "", map[string]ltl.Verdict{ "appIsRunning": ltl.VerdictHolds, "clickCountNonNegative": ltl.VerdictHolds, "clickCountNeverDecreases": ltl.VerdictHolds, "usernameNeverShrinks": ltl.VerdictHolds, }}, {"running", 5, "alice", map[string]ltl.Verdict{ "appIsRunning": ltl.VerdictHolds, "clickCountNonNegative": ltl.VerdictHolds, "clickCountNeverDecreases": ltl.VerdictHolds, "usernameNeverShrinks": ltl.VerdictHolds, }}, {"running", 3, "al", map[string]ltl.Verdict{ "appIsRunning": ltl.VerdictHolds, "clickCountNonNegative": ltl.VerdictHolds, "clickCountNeverDecreases": ltl.VerdictViolated, "usernameNeverShrinks": ltl.VerdictViolated, }}, } for index, step := range steps { stateRaw, _ := json.Marshal(step.appState) countRaw, _ := json.Marshal(step.clickCount) usernameRaw, _ := json.Marshal(step.username) if err := v.PushSnapshot(Snapshots{ "app_state": stateRaw, "click_count": countRaw, "username": usernameRaw, }, nil); err != nil { t.Fatalf("step %d: %v", index, err) } got := v.EvaluateProperties() for property, want := range step.want { if got[property] != want { t.Errorf("step %d %q: got %v, want %v", index, property, got[property], want) } } } }