@visa/cli 4.1.0-rc.196 → 4.1.0-rc.197

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,8 +128,8 @@ export type SubmitCandidateMeta = {
128
128
  visible: boolean;
129
129
  area: number;
130
130
  };
131
- export declare function preferredSubmitIndex(cands: readonly SubmitCandidateMeta[], options?: {
132
- allowInertFallback?: boolean;
131
+ export declare function preferredSubmitIndex(cands: readonly SubmitCandidateMeta[], opts?: {
132
+ allowDeferred?: boolean;
133
133
  }): number;
134
134
  export declare function snapshotOrigin(rawUrl: string): string;
135
135
  export type PreparedCheckoutSession = {
@@ -131,21 +131,28 @@ 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, options = {}) {
135
- const usable = cands
136
- .map((c, i) => ({ c, i }))
137
- .filter(({ c }) => c.visible && !c.ariaHidden && c.tabIndex !== -1 && c.area > 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)
134
+ export function preferredSubmitIndex(cands, opts = {}) {
135
+ const indexed = cands.map((c, i) => ({ c, i }));
136
+ const usable = indexed.filter(({ c }) => c.visible && !c.ariaHidden && c.tabIndex !== -1 && c.area > 0);
137
+ const usablePayLike = usable.find(({ c }) => PAY_LABEL.test(c.label));
138
+ if (usablePayLike)
139
+ return usablePayLike.i;
140
+ if (!opts.allowDeferred)
141
+ return usable[0]?.i ?? -1;
142
+ // A genuine multi-step checkout can keep its final submit control inside a
143
+ // hidden payment section until a safe, non-submit Continue button advances
144
+ // the page. The human still needs that exact control bound into the review
145
+ // before approval. Accept it for fingerprinting only when it is not marked
146
+ // inert; the pre-click lookup remains strict and will refuse unless the same
147
+ // control becomes visible and has non-zero area after reveal.
148
+ const deferred = indexed.filter(({ c }) => !c.ariaHidden && c.tabIndex !== -1);
149
+ if (deferred.length === 0)
143
150
  return -1;
144
- const payLike = cands.findIndex((c) => PAY_LABEL.test(c.label));
145
- return payLike >= 0 ? payLike : 0;
151
+ const deferredPayLike = deferred.find(({ c }) => PAY_LABEL.test(c.label));
152
+ return (deferredPayLike ?? usable[0] ?? deferred[0]).i;
146
153
  }
147
154
  const PAY_LABEL = /pay|place order|complete order|submit order|buy now/i;
148
- async function findSubmit(page, options = {}) {
155
+ async function findSubmit(page, opts = {}) {
149
156
  const controls = page.locator('button[type="submit"], input[type="submit"]');
150
157
  const handles = await controls.all().catch(() => []);
151
158
  if (handles.length > 0) {
@@ -164,7 +171,7 @@ async function findSubmit(page, options = {}) {
164
171
  area: box ? box.width * box.height : 0,
165
172
  };
166
173
  }));
167
- const idx = preferredSubmitIndex(metas, options);
174
+ const idx = preferredSubmitIndex(metas, opts);
168
175
  if (idx >= 0) {
169
176
  const chosen = handles[idx];
170
177
  const label = metas[idx].label || 'submit';
@@ -917,10 +924,10 @@ export async function prepareCheckout(opts, store = defaultPreparedCheckoutStore
917
924
  result: makeResult('blocked-by-mandate', fields, evidence, requiresAdapter, verdict.reason),
918
925
  };
919
926
  }
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 });
927
+ // Multi-step pages may expose the final submit control in a hidden section
928
+ // before a safe Continue reveals it. Fingerprint that exact control for the
929
+ // human review; the eventual click path still requires it to be usable.
930
+ const submit = await findSubmit(page, { allowDeferred: true });
924
931
  const review = Object.freeze({
925
932
  id: randomUUID(),
926
933
  url: page.url(),
@@ -1043,8 +1050,8 @@ export async function submitApprovedCheckout(reviewId, opts, store = defaultPrep
1043
1050
  }
1044
1051
  // Revalidate the same future target before credential minting. This may be
1045
1052
  // 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 });
1053
+ // the reviewed control to be visible and usable before any click.
1054
+ const approvalSubmit = await findSubmit(page, { allowDeferred: true });
1048
1055
  const submitChangedAtApproval = submitTargetChangeReason(checkout.review, approvalSubmit);
1049
1056
  if (submitChangedAtApproval) {
1050
1057
  evidence.step('approval', {