@visa/cli 4.1.0-rc.193 → 4.1.0-rc.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -128,7 +128,9 @@ export type SubmitCandidateMeta = {
128
128
  visible: boolean;
129
129
  area: number;
130
130
  };
131
- export declare function preferredSubmitIndex(cands: readonly SubmitCandidateMeta[]): number;
131
+ export declare function preferredSubmitIndex(cands: readonly SubmitCandidateMeta[], options?: {
132
+ allowInertFallback?: boolean;
133
+ }): number;
132
134
  export declare function snapshotOrigin(rawUrl: string): string;
133
135
  export type PreparedCheckoutSession = {
134
136
  checkout: PreparedCheckout;
@@ -131,17 +131,21 @@ async function fingerprintSubmitTarget(locator, kind, fallbackLabel) {
131
131
  // (observed live: `<h3 id="billingAddress"> intercepts pointer events`, retried
132
132
  // until the 6s timeout, deterministically, on casper.com). Rank the real
133
133
  // controls ahead of the inert ones and prefer a pay-labelled control.
134
- export function preferredSubmitIndex(cands) {
134
+ export function preferredSubmitIndex(cands, options = {}) {
135
135
  const usable = cands
136
136
  .map((c, i) => ({ c, i }))
137
137
  .filter(({ c }) => c.visible && !c.ariaHidden && c.tabIndex !== -1 && c.area > 0);
138
- if (usable.length === 0)
138
+ if (usable.length > 0) {
139
+ const payLike = usable.find(({ c }) => PAY_LABEL.test(c.label));
140
+ return (payLike ?? usable[0]).i;
141
+ }
142
+ if (!options.allowInertFallback || cands.length === 0)
139
143
  return -1;
140
- const payLike = usable.find(({ c }) => PAY_LABEL.test(c.label));
141
- return (payLike ?? usable[0]).i;
144
+ const payLike = cands.findIndex((c) => PAY_LABEL.test(c.label));
145
+ return payLike >= 0 ? payLike : 0;
142
146
  }
143
147
  const PAY_LABEL = /pay|place order|complete order|submit order|buy now/i;
144
- async function findSubmit(page) {
148
+ async function findSubmit(page, options = {}) {
145
149
  const controls = page.locator('button[type="submit"], input[type="submit"]');
146
150
  const handles = await controls.all().catch(() => []);
147
151
  if (handles.length > 0) {
@@ -160,7 +164,7 @@ async function findSubmit(page) {
160
164
  area: box ? box.width * box.height : 0,
161
165
  };
162
166
  }));
163
- const idx = preferredSubmitIndex(metas);
167
+ const idx = preferredSubmitIndex(metas, options);
164
168
  if (idx >= 0) {
165
169
  const chosen = handles[idx];
166
170
  const label = metas[idx].label || 'submit';
@@ -913,7 +917,10 @@ export async function prepareCheckout(opts, store = defaultPreparedCheckoutStore
913
917
  result: makeResult('blocked-by-mandate', fields, evidence, requiresAdapter, verdict.reason),
914
918
  };
915
919
  }
916
- const submit = await findSubmit(page);
920
+ // A later checkout step may own the submit control while it is still
921
+ // hidden. Fingerprint that future target for human review, but do not use
922
+ // the inert fallback at the final click boundary below.
923
+ const submit = await findSubmit(page, { allowInertFallback: true });
917
924
  const review = Object.freeze({
918
925
  id: randomUUID(),
919
926
  url: page.url(),
@@ -1034,7 +1041,10 @@ export async function submitApprovedCheckout(reviewId, opts, store = defaultPrep
1034
1041
  evidence.setSnapshotSummary(await snapshotSummary(page));
1035
1042
  return makeResult('blocked-by-mandate', state.fields, evidence, requiresAdapter, changedAtApproval);
1036
1043
  }
1037
- const approvalSubmit = await findSubmit(page);
1044
+ // Revalidate the same future target before credential minting. This may be
1045
+ // hidden on a multi-step checkout; the final lookup after reveal requires
1046
+ // a usable visible control before any click can happen.
1047
+ const approvalSubmit = await findSubmit(page, { allowInertFallback: true });
1038
1048
  const submitChangedAtApproval = submitTargetChangeReason(checkout.review, approvalSubmit);
1039
1049
  if (submitChangedAtApproval) {
1040
1050
  evidence.step('approval', {