@visa/cli 4.1.0-rc.191 → 4.1.0-rc.192
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.
|
@@ -121,6 +121,14 @@ export type SubmitApprovedCheckoutOptions = {
|
|
|
121
121
|
onChallengeHold?: (signal: string | null) => void;
|
|
122
122
|
resolveEmailOtp?: OtpResolver;
|
|
123
123
|
};
|
|
124
|
+
export type SubmitCandidateMeta = {
|
|
125
|
+
label: string;
|
|
126
|
+
ariaHidden: boolean;
|
|
127
|
+
tabIndex: number | null;
|
|
128
|
+
visible: boolean;
|
|
129
|
+
area: number;
|
|
130
|
+
};
|
|
131
|
+
export declare function preferredSubmitIndex(cands: readonly SubmitCandidateMeta[]): number;
|
|
124
132
|
export declare function snapshotOrigin(rawUrl: string): string;
|
|
125
133
|
export type PreparedCheckoutSession = {
|
|
126
134
|
checkout: PreparedCheckout;
|
|
@@ -124,17 +124,52 @@ async function fingerprintSubmitTarget(locator, kind, fallbackLabel) {
|
|
|
124
124
|
};
|
|
125
125
|
}, { targetKind: kind, targetFallbackLabel: fallbackLabel });
|
|
126
126
|
}
|
|
127
|
+
// Shopify checkouts keep INERT duplicates of the pay control in the DOM —
|
|
128
|
+
// aria-hidden="true", tabindex="-1", and/or zero-size. Playwright still reports
|
|
129
|
+
// those as "visible, enabled and stable", so a bare `.first()` resolves to one
|
|
130
|
+
// and then every click is swallowed by whatever paints on top of it
|
|
131
|
+
// (observed live: `<h3 id="billingAddress"> intercepts pointer events`, retried
|
|
132
|
+
// until the 6s timeout, deterministically, on casper.com). Rank the real
|
|
133
|
+
// controls ahead of the inert ones and prefer a pay-labelled control.
|
|
134
|
+
export function preferredSubmitIndex(cands) {
|
|
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
|
+
return -1;
|
|
140
|
+
const payLike = usable.find(({ c }) => PAY_LABEL.test(c.label));
|
|
141
|
+
return (payLike ?? usable[0]).i;
|
|
142
|
+
}
|
|
143
|
+
const PAY_LABEL = /pay|place order|complete order|submit order|buy now/i;
|
|
127
144
|
async function findSubmit(page) {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
'
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
const controls = page.locator('button[type="submit"], input[type="submit"]');
|
|
146
|
+
const handles = await controls.all().catch(() => []);
|
|
147
|
+
if (handles.length > 0) {
|
|
148
|
+
const metas = await Promise.all(handles.map(async (h) => {
|
|
149
|
+
const text = ((await h.textContent().catch(() => '')) || '').trim();
|
|
150
|
+
const value = text || (await h.getAttribute('value').catch(() => '')) || '';
|
|
151
|
+
const ariaHidden = await h.getAttribute('aria-hidden').catch(() => null);
|
|
152
|
+
const tabIndexRaw = await h.getAttribute('tabindex').catch(() => null);
|
|
153
|
+
const visible = await h.isVisible().catch(() => false);
|
|
154
|
+
const box = await h.boundingBox().catch(() => null);
|
|
155
|
+
return {
|
|
156
|
+
label: value,
|
|
157
|
+
ariaHidden: ariaHidden === 'true',
|
|
158
|
+
tabIndex: tabIndexRaw === null ? null : Number(tabIndexRaw),
|
|
159
|
+
visible,
|
|
160
|
+
area: box ? box.width * box.height : 0,
|
|
161
|
+
};
|
|
162
|
+
}));
|
|
163
|
+
const idx = preferredSubmitIndex(metas);
|
|
164
|
+
if (idx >= 0) {
|
|
165
|
+
const chosen = handles[idx];
|
|
166
|
+
const label = metas[idx].label || 'submit';
|
|
167
|
+
return {
|
|
168
|
+
desc: `submit button ("${label}")`,
|
|
169
|
+
fingerprint: await fingerprintSubmitTarget(chosen, 'submit-control', 'submit'),
|
|
170
|
+
click: () => chosen.click(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
138
173
|
}
|
|
139
174
|
const byText = page.getByRole('button', { name: SUBMIT_TEXT }).first();
|
|
140
175
|
if ((await byText.count().catch(() => 0)) > 0) {
|