Skip to content

Commit 698bda4

Browse files
committed
fix(preview): classify click lookup failures on the error type
A free function picked hidden/disabled/ambiguous and defaulted ambiguous matches to 0. The policy now lives on PreviewAutomationTargetNotFoundError.fromLookupFailure, and ambiguous requires an explicit match count.
1 parent 4de44be commit 698bda4

2 files changed

Lines changed: 54 additions & 34 deletions

File tree

apps/desktop/src/preview/Manager.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,5 +2867,17 @@ describe("Preview automation diagnostics", () => {
28672867
expect(hidden.message).not.toContain("secret");
28682868
expect(disabled.message).not.toContain("secret");
28692869
expect(ambiguous.message).not.toContain("secret");
2870+
2871+
const fromAmbiguous = PreviewManager.PreviewAutomationTargetNotFoundError.fromLookupFailure({
2872+
operation: "click",
2873+
tabId: "tab_1",
2874+
selectorKind: "locator",
2875+
selectorLength: selector.length,
2876+
failureKind: "ambiguous",
2877+
matchCount: 3,
2878+
});
2879+
expect(fromAmbiguous).toBeInstanceOf(PreviewManager.PreviewAutomationTargetAmbiguousError);
2880+
expect(fromAmbiguous.message).toContain("matched 3 elements");
2881+
expect(fromAmbiguous.message).not.toContain("secret");
28702882
});
28712883
});

apps/desktop/src/preview/Manager.ts

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,8 +2994,12 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
29942994
| { invalidSelector: true; message: string }
29952995
| {
29962996
notFound: true;
2997-
failureKind: "missing" | "hidden" | "disabled" | "ambiguous";
2998-
matchCount?: number;
2997+
failureKind: "missing" | "hidden" | "disabled";
2998+
}
2999+
| {
3000+
notFound: true;
3001+
failureKind: "ambiguous";
3002+
matchCount: number;
29993003
}
30003004
>(
30013005
tabId,
@@ -3041,12 +3045,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
30413045
});
30423046
}
30433047
if ("notFound" in point) {
3044-
return yield* raiseAutomationTargetLookupError({
3048+
return yield* PreviewAutomationTargetNotFoundError.fromLookupFailure({
30453049
operation: "click",
30463050
tabId,
30473051
...automationSelectorDiagnostics(input),
3048-
failureKind: point.failureKind,
3049-
...(point.matchCount === undefined ? {} : { matchCount: point.matchCount }),
3052+
...(point.failureKind === "ambiguous"
3053+
? { failureKind: "ambiguous", matchCount: point.matchCount }
3054+
: { failureKind: point.failureKind }),
30503055
});
30513056
}
30523057
return point;
@@ -3700,6 +3705,38 @@ export class PreviewAutomationTargetNotFoundError extends Schema.TaggedErrorClas
37003705
"PreviewAutomationTargetNotFoundError",
37013706
PreviewAutomationTargetLookupFields,
37023707
) {
3708+
static fromLookupFailure(
3709+
input: {
3710+
readonly operation: string;
3711+
readonly tabId: string;
3712+
readonly selectorKind: PreviewAutomationSelectorKind;
3713+
readonly selectorLength?: number;
3714+
} & (
3715+
| { readonly failureKind: "ambiguous"; readonly matchCount: number }
3716+
| { readonly failureKind?: "missing" | "hidden" | "disabled" }
3717+
),
3718+
) {
3719+
const shared = {
3720+
operation: input.operation,
3721+
tabId: input.tabId,
3722+
selectorKind: input.selectorKind,
3723+
...(input.selectorLength === undefined ? {} : { selectorLength: input.selectorLength }),
3724+
};
3725+
if (input.failureKind === "hidden") {
3726+
return new PreviewAutomationTargetHiddenError(shared);
3727+
}
3728+
if (input.failureKind === "disabled") {
3729+
return new PreviewAutomationTargetDisabledError(shared);
3730+
}
3731+
if (input.failureKind === "ambiguous") {
3732+
return new PreviewAutomationTargetAmbiguousError({
3733+
...shared,
3734+
matchCount: input.matchCount,
3735+
});
3736+
}
3737+
return new PreviewAutomationTargetNotFoundError(shared);
3738+
}
3739+
37033740
override get message(): string {
37043741
const target = previewAutomationTargetLabel(this.selectorKind, this.selectorLength);
37053742
return `Preview automation ${this.operation} could not find ${target} in tab ${this.tabId}`;
@@ -3739,35 +3776,6 @@ export class PreviewAutomationTargetAmbiguousError extends Schema.TaggedErrorCla
37393776
}
37403777
}
37413778

3742-
const raiseAutomationTargetLookupError = (input: {
3743-
readonly operation: string;
3744-
readonly tabId: string;
3745-
readonly selectorKind: PreviewAutomationSelectorKind;
3746-
readonly selectorLength?: number;
3747-
readonly failureKind?: "missing" | "hidden" | "disabled" | "ambiguous";
3748-
readonly matchCount?: number;
3749-
}) => {
3750-
const shared = {
3751-
operation: input.operation,
3752-
tabId: input.tabId,
3753-
selectorKind: input.selectorKind,
3754-
...(input.selectorLength === undefined ? {} : { selectorLength: input.selectorLength }),
3755-
};
3756-
if (input.failureKind === "hidden") {
3757-
return new PreviewAutomationTargetHiddenError(shared);
3758-
}
3759-
if (input.failureKind === "disabled") {
3760-
return new PreviewAutomationTargetDisabledError(shared);
3761-
}
3762-
if (input.failureKind === "ambiguous") {
3763-
return new PreviewAutomationTargetAmbiguousError({
3764-
...shared,
3765-
matchCount: input.matchCount ?? 0,
3766-
});
3767-
}
3768-
return new PreviewAutomationTargetNotFoundError(shared);
3769-
};
3770-
37713779
export class PreviewAutomationTargetNotEditableError extends Schema.TaggedErrorClass<PreviewAutomationTargetNotEditableError>()(
37723780
"PreviewAutomationTargetNotEditableError",
37733781
{

0 commit comments

Comments
 (0)