mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
feat(verifier): name extractors for diff surfacing
bindExtract accepts an optional name argument; falls back to extractor_N when omitted. The name is stored on extractorState alongside prev/curr value caches that the next change will use to emit per-step diffs.
This commit is contained in:
1 parent
cbef329cdd
commit
1b35e5a6a6
2 files changed
+39
-3
No files matched your search
@@ -10,6 +10,12 @@ import (
|
||||
type extractorState struct {
|
||||
getter goja.Callable
|
||||
handle *goja.Object
|
||||
name string
|
||||
// prev/curr cache the JSON-encoded extractor values from the prior and
|
||||
// current PushSnapshot, used by ChangedExtractors to surface per-step
|
||||
// diffs in the trace.
|
||||
prev []byte
|
||||
curr []byte
|
||||
}
|
||||
|
||||
type formulaState struct {
|
||||
@@ -139,19 +145,29 @@ func (v *Verifier) installRuntimeBindings() error {
|
||||
}
|
||||
|
||||
func (v *Verifier) bindExtract(call goja.FunctionCall) goja.Value {
|
||||
if len(call.Arguments) != 1 {
|
||||
panic(v.runtime.NewTypeError("extract requires exactly one argument"))
|
||||
if len(call.Arguments) < 1 || len(call.Arguments) > 2 {
|
||||
panic(v.runtime.NewTypeError("extract requires (getter) or (getter, name)"))
|
||||
}
|
||||
getter, ok := goja.AssertFunction(call.Arguments[0])
|
||||
if !ok {
|
||||
panic(v.runtime.NewTypeError("extract argument must be a function"))
|
||||
}
|
||||
name := ""
|
||||
if len(call.Arguments) == 2 {
|
||||
arg := call.Arguments[1]
|
||||
if !goja.IsUndefined(arg) && !goja.IsNull(arg) {
|
||||
name = arg.String()
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("extractor_%d", len(v.extractors))
|
||||
}
|
||||
|
||||
handle := v.runtime.NewObject()
|
||||
_ = handle.Set("current", goja.Undefined())
|
||||
_ = handle.Set("previous", goja.Undefined())
|
||||
|
||||
v.extractors = append(v.extractors, &extractorState{getter: getter, handle: handle})
|
||||
v.extractors = append(v.extractors, &extractorState{getter: getter, handle: handle, name: name})
|
||||
return handle
|
||||
}
|
||||
|
||||
|
||||
@@ -894,6 +894,26 @@ func TestSelectorStringFromJS_CanonicalGrammar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestExtract_DefaultsAndNamedNames verifies bindExtract assigns a fallback
|
||||
// `extractor_N` name when no name is supplied and respects an explicit one.
|
||||
func TestExtract_DefaultsAndNamedNames(t *testing.T) {
|
||||
verifier := newVerifier(t)
|
||||
mustLoad(t, verifier, `
|
||||
__sanderling__.extract(state => 1);
|
||||
__sanderling__.extract(state => 2, "ledgerRows");
|
||||
__sanderling__.extract(state => 3);
|
||||
`)
|
||||
if len(verifier.extractors) != 3 {
|
||||
t.Fatalf("extractors registered: got %d, want 3", len(verifier.extractors))
|
||||
}
|
||||
want := []string{"extractor_0", "ledgerRows", "extractor_2"}
|
||||
for i, name := range want {
|
||||
if got := verifier.extractors[i].name; got != name {
|
||||
t.Errorf("extractor %d name: got %q, want %q", i, got, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSelectorStringFromJS_NullEmpty verifies that nil/undefined args produce
|
||||
// an empty string instead of "null"/"undefined" garbage.
|
||||
func TestSelectorStringFromJS_NullEmpty(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user