GGua REFERENCE日本語GitHub ↗
GUIDE / RECORDING

Recording guide

Gua.Testing.Recording stores clicks, value changes, selections, and key input as semantic actions instead of screen coordinates, then replays them for regression testing and bug reproduction. It provides engine-independent, host-completion-aware journeys when multi-step flows would be unreliable as fixed-delay macros.

Concrete use cases

SituationValue
Login, configure, and save regression flowVersion a multi-step scenario as one document
Share a bug reproductionUse stable IDs and roles instead of screen coordinates
Run a flow against Godot and Unity fixturesReplay against the engine-independent IGuaContext
Enter a passwordStore a secretKey, never plaintext
Semantic targetID / role / focusEnqueue actionassign requestIdHost completionsame requestIdrecording.jsontarget, timing, revisionReplay + waitresolve every step
Figure 1: Recording waits for host completion instead of treating queue acceptance as success.

Install

Add the latest stable package to a scenario test projectpowershell
dotnet add package Gua.Testing.Recording

Record a flow

Record logincsharp
using Gua.Testing.Recording;

var recorder = new GuaRecorder(host.Context);
await recorder.ClickAsync(new GuaRecordingTarget(Id: "open-login"));

var emailTarget = new GuaRecordingTarget(
    Role: "textbox", Name: "Email", Scope: "login-dialog");
await recorder.SetValueAsync(
    emailTarget,
    "player@example.com",
    waitCondition: GuaWaitConditions.Visible("email"));

var passwordTarget = new GuaRecordingTarget(Id: "password");
await recorder.SetValueAsync(
    passwordTarget, password,
    sensitive: true, secretKey: "login-password");
await recorder.FocusAsync(passwordTarget);
await recorder.PressKeyAsync("Enter");

GuaRecordingFile.Save("recordings/login.json", recorder.Recording);

A step's waitCondition is evaluated before that step resolves its target and enqueues its action. Here the email step waits for the dialog's email node after the preceding click. SetValueAsync does not focus its target, so the password receives an explicit focus action before the untargeted PressKeyAsync sends Enter to the current focus. The recorder stores these actions, monotonic relative timing, revisions, and request IDs.

Choose stable targets

TargetUse whenRule
IdThe game exposes a stable IDPreferred; keep it stable through scene refactors
Role + NameAn accessible name is uniqueAdd Scope when names repeat
CurrentFocusSending a key to current focusValid only for press_key
Coordinate fallbackImporting legacy dataRejected by default

Prefer conditions to fixed delays

With the default PreferConditions mode, a step with a condition waits for semantic state instead of repeating its recorded delay. Conditions include visible, hidden, enabled, disabled, focused, unfocused, checked, unchecked, text, and value.

Conditionscsharp
GuaWaitConditions.Visible("login-dialog")
GuaWaitConditions.Enabled("submit")
GuaWaitConditions.Text("status", "Ready")
GuaWaitConditions.Value("volume", "0.8")

Replay with secret injection

Replay a saved flowcsharp
var recording = GuaRecordingFile.Load("recordings/login.json");

var result = await GuaReplayer.ReplayAsync(
    host.Context, recording, new GuaReplayOptions
    {
        TimingMode = GuaReplayTimingMode.PreferConditions,
        SecretResolver = key => key == "login-password" ? password : null,
        ActionTimeout = TimeSpan.FromSeconds(5),
        PollInterval = TimeSpan.FromMilliseconds(50),
    });

A sensitive set_value stores only its key. Replay fails if SecretResolver cannot supply the value. Keep the resolver from writing secrets to logs or artifacts.

Replay safety boundaries

  • Every semantic target is resolved against the current tree and must be unique.
  • Every action waits for completion with the same request ID without consuming unrelated events.
  • Coordinate fallback is rejected by default and requires an explicit caller-provided executor.
  • Schema version, timing, revisions, action arguments, and secret leakage are validated on load.

Import retained diagnostics

GuaRecordingFile.ImportDiagnostics pairs enqueued operations with observed completion events by request ID. Its metadata reports paired requests, unpaired steps, and whether a legacy payload required synthetic timing.

Convert diagnostics historycsharp
var imported = GuaRecordingFile.ImportDiagnostics(context.GetDiagnosticsJson());
if (imported.UnpairedStepCount != 0)
    throw new InvalidOperationException("Diagnostics contains unpaired operations.");
GuaRecordingFile.Save("recordings/imported.json", imported.Recording);